Authors often define media query "breakpoints" for small/medium/large layouts (preferrably named), and hate repeating those media queries all over the place, especially in many places in the HTML markup for <source>
.
Use of breakpoint-specific media query in <source>
is a maintenance work if author decides to change the breakpoints (HTML templating could help a bit, HTML and CSS use different preprocessors, so there's still mix of syntaxes and technologies).
I propose solving this by introducing Media Query Variables. This solves verbosity problem not only for picture, but for breakpoints everywhere in general.
CSS Variables with cascade/inheritance can't be used for MQs, because cascade and inheritance is calculated after media queries. However, a similar, familiar syntax can be used for media query variables.
You define a Media Query Variable (breakpoint name) at the top of a stylesheet with @var-media
:
@var-media breakpoint-name: (min-width:etc) and (whatever-you-like:etc);
(note there's no {}
, this only defines that a name is equal to a query).
To use the variable you use var(name)
in the place where you'd use the full media query:
@media var(breakpoint-name) { ...CSS goes here as usual... }
For example if you define this in CSS (I recommend in <style>
element in <head>
, more on that later):
@var-media small-touchscreen: screen and (max-device-width: 480px) and (pointer: coarse);
Then you can use images specifically for this breakpoint like this:
<picture>
<source media="var(small-touchscreen)" src="tap.png">
<source src="click.png">
</picture>
and it works just as if it was:
<picture>
<source media="screen and (max-device-width: 480px) and (pointer: coarse)" src="tap.png">
<source src="click.png">
</picture>
I think @srcset
could also be changed from weird w/h
syntax to use MQ variables.
Bigger example:
<!DOCTYPE html>
<head>
<style>
/* @var-media <name of the variable>: <media query>; */
@var-media smalltouchscreen: screen and (max-device-width: 480px) and (pointer: coarse);
</style>
<style>
/* var(<name of mq variable>) is equivalent to using full MQ in its place */
@media var(smalltouchscreen) {
}
/* thanks to the definition at the top is now same as: */
@media screen and (max-device-width: 480px) and (pointer: coarse) {
}
</style>
<link rel=stylesheet href="…"><!-- can use @media var(smalltouchscreen) too -->
</head>
<body>
<!--
<img srcset="<file> <1x/2x/etc optional> <media query optional>"
-->
<img srcset="tap-lowres.png var(smalltouchscreen), tap-hires.png 2x var(smalltouchscreen), click-hires.png 2x, click.png"/>
<img srcset="click.png screen, pencil-and-fax-back.eps print"/>
<picture>
<source media="var(smalltouchscreen)" src="tap.png">
<source src="click.png">
</picture>
</body>
CSS vs Preload Scanner
Browsers want to start downloading images before external CSS is loaded, and before layout is computed. Therefore solutions that depend on layout or cascaded/inherited CSS properties on elements are disqualified.
Luckily MQ variables don't use inheritance and don't depend on layout. If they're put in <style>
in <head>
it will be possible to interpret them without waiting for external CSS or layout (authors who can't edit <head>
can still use full MQs in <picture>
).
I'm sure there will be some grimacing about parsing CSS in the preload scanner, but just extracting @var-media
rules from top of the stylesheet is very easy (I'll write the few lines of parser code for browser vendors who complain it's too hard).
Having MQ variables in <head>
rather than external file isn't ideal for authors, but it's the best compromise that can be done for maintainability without discarding the preload scanner.
Variables that are not (yet) defined don't match anything. This gives nice feature at zero extra implementation cost: If @var-media
definitions are put in an external CSS file and all <source>
alternatives use a MQ variable, then <picture>
wouldn't load anything until external CSS file is loaded (this is good — it's a bit like lazyload/postpone and avoids double-loading by preload scanner).
This way authors have a choice of having decent maintainability and good performance by inlining <style>
with MQ definitions, or putting definitions in an external CSS file only at cost of losing preload scanner (which may be reasonable in a JS app without progressive enhancement, because it can't benefit from preload scanner already).