Media Queries in HTML Video
I decided to try serving different video using some of the features we use for serving content based on user preferences and accessibility features.
I made a video. Then I made alternate versions for assorted media queries. While this would be onerous for some organizations to do for many of their videos, I wanted to at least test both the support and the practicality for teams that can wrangle the budget to give this a shot.
Here is my starting video, which I may submit to Sundance or SouthBy or a local community theater’s Christmas pageant:
In each of the examples below I will:
- Embed that video with its alternate version,
- show the HTML code I used, and
- provide the alternate version alone.
This way you can compare and confirm any changes you made to your browser or system or within dev tools were successful. If you are unfamiliar how to tweak stuff in your browser dev tools, instructions for Chrome, for Firefox, and for Safari, and for Edge.
prefers-reduced-motion
It might seem weird to consider less movement in a video, but prefers-reduced-motion
can let you offer videos with less flashing or blinking, potentially reducing the chance of seizures in some users, pain in others, and annoyance all around.
<video preload="metadata" controls poster="star-video_poster.jpg">
<source src="star-video_motion.mp4" type="video/mp4" media="(prefers-reduced-motion: reduce)">
<source src="star-video.mp4" type="video/mp4">
<track label="English" kind="subtitles" srclang="en-us" src="star-video_base.vtt" default>
Sorry, your browser doesn’t support embedded videos, but don’t worry, you can <a href="star-video.mp4">download it</a>. A <a href="star-video_motion.mp4">less animated version</a> is available. The <a href="star-video_base.vtt">caption file</a> is also available in case your video player can import it.
</video>
If the video was exactly the same as my starting video, then…
The reduced motion video
prefers-color-scheme
Supporting a dark theme on your site only to embed a bright white video can be jarring for users. You can use prefers-color-scheme
to provide an emo version.
<video preload="metadata" controls poster="star-video_poster.jpg">
<source src="star-video_dark.mp4" type="video/mp4" media="(prefers-color-scheme: dark)">
<source src="star-video.mp4" type="video/mp4">
<track label="English" kind="subtitles" srclang="en-us" src="star-video_base.vtt" default>
Sorry, your browser doesn’t support embedded videos, but don’t worry, you can <a href="star-video.mp4">download it</a>. A <a href="star-video_dark.mp4">darker version</a> is available. The <a href="star-video_base.vtt">caption file</a> is also available in case your video player can import it.
</video>
If the video was exactly the same as my starting video, then…
The darker video
orientation
My videos are traditional desktop-first, but if I wanted to make the experience better for portrait users (mobile, tablet, my laptop on its side) then orientation
will let me serve a version that doesn’t require them to rotate the device or deal with letterbox black bars.
<video preload="metadata" controls poster="star-video_poster.jpg">
<source src="star-video_portrait.mp4" type="video/mp4" media="(orientation: portrait)">
<source src="star-video.mp4" type="video/mp4">
<track label="English" kind="subtitles" srclang="en-us" src="star-video_base.vtt" default>
Sorry, your browser doesn’t support embedded videos, but don’t worry, you can <a href="star-video.mp4">download it</a>. A <a href="star-video_portrait.mp4">portrait version</a> is available. The <a href="star-video_base.vtt">caption file</a> is also available in case your video player can import it.
</video>
If the video was exactly the same as my starting video, then…
The portrait video
prefers-reduced-data
Support for the experimental prefers-reduced-data
is poor, but it might be worth tracking it and offering these videos so your users aren’t paying as much to be frustrated by your media.
<video preload="metadata" controls poster="star-video_poster.jpg">
<source src="star-video_data.mp4" type="video/mp4" media="(prefers-reduced-data: reduce)">
<source src="star-video.mp4" type="video/mp4">
<track label="English" kind="subtitles" srclang="en-us" src="star-video_base.vtt" default>
Sorry, your browser doesn’t support embedded videos, but don’t worry, you can <a href="star-video.mp4">download it</a>. A <a href="star-video_data.mp4">lower bandwidth version</a> is available. The <a href="star-video_base.vtt">caption file</a> is also available in case your video player can import it.
</video>
If the video was exactly the same as my starting video, then…
The reduced bandwidth video
Combining Media Queries
The browser will stop at the first <source>
that matches the media query.
If you have multiple media queries then you can make one video for each permutation. If you don’t have the budget or time, then you can use existing videos that match some of those media queries, but now you have to make a decision about which video is prioritized.
I am arbitrarily choosing the media queries here. Remember to either update your browser / system settings or emulate the media queries within your browser dev tools, or these examples may look like they don’t work.
orientation
and prefers-color-scheme
In this video, the dark video is first in the source order:
<video preload="metadata" controls poster="star-video_poster.jpg">
<source src="star-video_dark.mp4" type="video/mp4" media="(prefers-color-scheme: dark)">
<source src="star-video_portrait.mp4" type="video/mp4" media="(orientation: portrait)">
<source src="star-video_portrait_dark.mp4" type="video/mp4" media="(prefers-color-scheme: dark) and (orientation: portrait)">
<source src="star-video.mp4" type="video/mp4">
<track label="English" kind="subtitles" srclang="en-us" src="star-video_base.vtt" default>
Sorry, your browser doesn’t support embedded videos, but don’t worry, you can <a href="star-video.mp4">download it</a>. A <a href="star-video_portrait_dark.mp4">dark portrait version</a> is available. A <a href="star-video_portrait.mp4">portrait version</a> is also available. A <a href="star-video_dark.mp4">dark version</a> is also available. The <a href="star-video_base.vtt">caption file</a> is also available in case your video player can import it.
</video>
Here, I reference the portrait video first:
<video preload="metadata" controls poster="star-video_poster.jpg">
<source src="star-video_portrait.mp4" type="video/mp4" media="(orientation: portrait)">
<source src="star-video_dark.mp4" type="video/mp4" media="(prefers-color-scheme: dark)">
<source src="star-video_portrait_dark.mp4" type="video/mp4" media="(prefers-color-scheme: dark) and (orientation: portrait)">
<source src="star-video.mp4" type="video/mp4">
<track label="English" kind="subtitles" srclang="en-us" src="star-video_base.vtt" default>
Sorry, your browser doesn’t support embedded videos, but don’t worry, you can <a href="star-video.mp4">download it</a>. A <a href="star-video_portrait_dark.mp4">dark portrait version</a> is available. A <a href="star-video_portrait.mp4">portrait version</a> is also available. A <a href="star-video_dark.mp4">dark version</a> is also available. The <a href="star-video_base.vtt">caption file</a> is also available in case your video player can import it.
</video>
If you have a video for a more specific or combined media query, put it first in the DOM order. This combined dark portrait video is referenced in the two prior videos, but it wasn’t first in the DOM:
<video preload="metadata" controls poster="star-video_poster.jpg">
<source src="star-video_portrait_dark.mp4" type="video/mp4" media="(prefers-color-scheme: dark) and (orientation: portrait)">
<source src="star-video_portrait.mp4" type="video/mp4" media="(orientation: portrait)">
<source src="star-video_dark.mp4" type="video/mp4" media="(prefers-color-scheme: dark)">
<source src="star-video.mp4" type="video/mp4">
<track label="English" kind="subtitles" srclang="en-us" src="star-video_base.vtt" default>
Sorry, your browser doesn’t support embedded videos, but don’t worry, you can <a href="star-video.mp4">download it</a>. A <a href="star-video_portrait_dark.mp4">dark portrait version</a> is available. A <a href="star-video_portrait.mp4">portrait version</a> is also available. A <a href="star-video_dark.mp4">dark version</a> is also available. The <a href="star-video_base.vtt">caption file</a> is also available in case your video player can import it.
</video>
If the video was exactly the same as my starting video or either of the prior videos, then…
The combined portrait and dark video
orientation
and prefers-color-scheme
and prefers-reduced-motion
Now the permutations are getting to be a bit much. I have 6 videos referenced in here, and I could add more variations. It also gets a bit wordy for the fallback text.
<video preload="metadata" controls poster="star-video_poster.jpg">
<source src="star-video_motion_dark_portrait.mp4" type="video/mp4" media="(prefers-color-scheme: dark) and (orientation: portrait) and (prefers-reduced-motion: reduce)">
<source src="star-video_portrait_dark.mp4" type="video/mp4" media="(prefers-color-scheme: dark) and (orientation: portrait)">
<source src="star-video_motion.mp4" type="video/mp4" media="(prefers-reduced-motion: reduce)">
<source src="star-video_portrait.mp4" type="video/mp4" media="(orientation: portrait)">
<source src="star-video_dark.mp4" type="video/mp4" media="(prefers-color-scheme: dark)">
<source src="star-video.mp4" type="video/mp4">
<track label="English" kind="subtitles" srclang="en-us" src="star-video_base.vtt" default>
Sorry, your browser doesn’t support embedded videos, but don’t worry, you can <a href="star-video.mp4">download it</a>. A <a href="star-video_motion_dark_portrait.mp4">reduced motion dark portrait version</a> is available. A <a href="star-video_portrait_dark.mp4">dark portrait version</a> is also available. A <a href="star-video_portrait.mp4">portrait version</a> is also available. A <a href="star-video_dark.mp4">dark version</a> is also available. The <a href="star-video_base.vtt">caption file</a> is also available in case your video player can import it.
</video>
If the video was exactly the same as my starting video, then…
The combined reduced motion, portrait, dark video
Other Considerations
Not all media queries are a fit. For example, print
would be novel if you want to choose the single frame that will be printed but that is really the job of the poster
attribute.
Speaking of which, you cannot choose a poster
based on this media query approach. The poster
is on the <video>
element, while media
is on all its child <source>
elements. That means the same poster
image will display for all users.
Fallback text, what will show if the browser does not display videos inline, also does not respond to these inline media queries. While you may believe users of modern browsers will never see this text, consider folks who visit in their RSS readers. Or some browser reader modes.
The <track>
does not accept a media
attribute, but it would not offer much value anyway since styling is done in your regular CSS with ::cue
. MDN is less confusing on this.
There is at least one media query where it is a bad idea to try to create custom videos — forced-colors
. I have written extensively on how forced-colors
is the standardized version of -ms-high-contrast
, which is triggered when a Windows user enters High Contrast Mode (WHCM), now Contrast Themes in Windows 11 (or via browser preferences). Since video frames cannot take system colors, this is moot. However, if you decide you want to send a user a high-contrast video when this is active, beware that some Contrast Themes users do it to set colors to low contrast to help reduce pain that can come from high contrasty bits on the screen.
Wanted
I would like a no-visuals media query. Perhaps triggered when the screen is dark but the video is still playing (power-saving mode or screen curtain from a screen reader). Or maybe when a video is playing in another tab or behind another window. When the media query is triggered, it could load a video with audio description (AD). While not as nice as letting a user choose a different audio track, unless <video>
is updated to support multiple audio tracks this could at least provide the option natively.
Yes, the <track>
element has a descriptions
attribute for synthesized audio description. In practice it does not work (that is worth a separate post).
Wrap-up
I am not telling you that you have to do any of this. You probably do not need to make a different video for every possible media query. You might not need to do it for any. But it is nice to know that you can adapt to user preferences and circumstances when you know it will benefit your fellow human.
The star field used in the video is one of the first images revealed to the public from NASA’s James Webb Space Telescope (image credit: NASA, ESA, CSA, and STScI). The music is Andy You’re a Star by The Killers.
Yes, you’re a star.
3 Comments
This is great, Adrian! Thanks for publishing all of these examples.
In response to .Thanks for writing the patch to stuff it (back) into Firefox!
Great article, Adrian! It really got me thinking about a way to accomplish the dark version w/o a separate video file. Our custom video player has the controls separate from the video tag to give it more features than the stock HTML5 player, so we can set a combination of filters like brightness and sepia on the video tag, itself, based on the learner’s needs and profile settings, without the controls fading as well.
In my testing, the following worked pretty well, but I only tested one video and I don’t prefer dark mode, so my perception may not be accurate.
@media (prefers-color-scheme: dark) {
.thevideotagclass{
filter:brightness(0.75) sepia(.6);
}
}
Leave a Reply to Mike Cancel response