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:

A 10 second video of nonsense. Maybe turn your audio down a bit.

In each of the examples below I will:

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.

Less of the annoying animation but all the annoying imagery.
<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.

Darker, like my holiday mood.
<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.

If we make all videos square we won’t have this problem, but they will be boring.
<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.

Mmmm… chunky like salsa or holiday milk.
<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:

The dark scheme is referenced first.
<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:

The portrait orientation is referenced 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:

The video coming both a dark scheme and portrait sizing is first.
<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.

The combined reduced motion, portrait, dark video is referenced first.
<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

Reply

This is great, Adrian! Thanks for publishing all of these examples.

In response to Scott. Reply

Thanks for writing the patch to stuff it (back) into Firefox!

Reply

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);
}
}

Mike; . Permalink

Leave a Reply to Mike Cancel response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>