Beyond responsive design: new and future media queries

Hey everyone.

First off, thanks hover for having me.

This conference has a truly stellar lineup and I hope you're all having a good time.

The media queries you use today are almost certainly all about screen sizes, and indeed that's what responsive design is mostly about.

However, browsers haven't stood still in the decades since the term responsive design was coined, and new media queries let us adapt websites not just to device properties like screen width and height, but also to user preferences, and, as we will discuss later in this presentation, even their surroundings.

Some of what we'll go over today is already implemented.

Other things are nearly here, and others still are pretty far out.

Those will probably change between now and when browsers start implementing them.

This also won't be a comprehensive list of all new media queries coming up.

Just specifically the ones I think you as a developer will find useful.

With those caveats out of the way, let's look at a timeline of what we'll go over today.

This timeline is a bit of speculation on my part, and will probably be wrong already by the time this talk gets broadcast, but that's how it goes.

Reduce-motion, color-scheme, color-gamut, and force-colors are already here.

Contrast and reduced-data have implementations in various browsers, even if they're not exposed yet.

From there though, any of those might be changed, replaced, or even never get implemented.

Similarly, we might see new media queries come up, like prefers-reduce-complexity, but none of that is clear at this point in time.

Every now and then I'll have examples up on the screen using the browser I'm making, called "Polypane".

It lets me show the same sites with different media queries side-by-side, so if you see a cool looking screenshot that's Polypane.

We'll start with everyone's favorite: Dark-mode! Or as the media query is called: prefers-color-scheme.

Your users can have a preference for a darker UI or equally important, a preference for a lighter UI.

There's many reasons for wanting a dark screen.

From light sensitivity, to having to look at screens in darker environments.

Dark themes tend to have less contrast though, and can be hard to see for people that don't have perfect vision.

For them, the increased contrast in light modes is much easier to see.

Both modes can improve your site for different people, or the same person, but in different situations.

The media query is called "prefers-scholar-scheme", and not "prefers-dark-mode", even though it only has two values - light, and dark.

That's because the spec writers want you to keep the option open to extend this media query in the future.

For example, with a sepia value.

Creating a dark mode is essentially double the work for all your UI colors.

While this can be planned for new websites, for existing sites, this might be a difficult task.

On those, you can implement something I call "cheap dark-mode".

It works like this.

First, we use a CSS filter to invert all colors, so that everything that's light becomes dark, and the other way around.

Then we hue shift by 180 degrees to go back to the original hue.

While this won't match your brand colors perfectly, they'll still be close enough.

At this point, text and colors will look pretty good, but all your images and videos will have turned into negatives, which is not so nice.

We can invert and hue shift the images and videos again, reverting them to the original color.

While this isn't perfect, it's a lot simpler than redefining all the colors across your site for dark mode.

As described earlier, dark mode can be considered an accessibility feature.

So consider trying the cheap dark mode trick and see if it works for your site.

Another accessibility feature worth taking into account is "prefers-reduced-motion".

Since animation has become so much easier in recent years, with CSS Animations, transitions, and scroll behavior, many websites have added these animations to provide a nicer experience - from fading hover styles to slide-out menus.

And while these definitely improve websites, large motion where it's not expected can negatively affect a group of people with vestibular disorders, for which unexpected movements can be disorienting, dizzying or otherwise unpleasant.

In practice, the prefers-reduced-motion media query can tell you if people appreciate the animations and transitions you made.

Note that the media query says "prefers- reduced-motion", not "prefer-no-motion".

When something happens in direct response to user inputs, or it happens with a subtle color change rather than actual motion, it tends to be less of a problem.

So this is something you can and should take into account, even without the media query.

Like any type of design element, try to apply animations tastefully.

If you want a handy reminder, think: "Less swoosh, more fade".

In general, you'll find that animations and transitions are rarely essential to a site, regardless of how strongly the designer insists.

Indeed, the web went literal decades without them.

When you work on a new website, the best course of action is to begin with a non-animated version of your design, and only when "@media: (prefers-reduced- motion: no preference)" matches, add your animations.

This progressively enhances the design in modern browsers, and browsers that don't understand the media query similarly don't get the animations.

Since they were nice to have to begin with, this is more than fine.

For existing sites, however, going over all animations and adding the media query can be a lot of work.

Instead we can add - here we go again - "cheap reduced motion".

This just turns all animations and transitions off, as well as disabling fixed backgrounds and smooth scrolling.

There's still some animation duration, even if it's way too short to see, so that any JavaScript that depends on animationEnd and transitionEnd events will still be executed.

Speaking of JavaScript, you can use the "window.mtchMedia" API to check for this media query and prevent JavaScript animations, or auto playing video.

Out of all the media queries we'll discuss today, "prefers-reduced-data:" its the one I'm most excited about.

I think it can have a really big impact when it finds its way into browsers, and that might be sooner than you think.

In Chromium, it's already available for testing behind a flag, and Polypane lets you test it with a single toggle.

Reduced-data tells you that a user wants to use less data when they visit your site.

This can be because they have a data cap on their internet connection, or they have slow internet, or even that they just appreciate a faster website.

In short, it will make your site accessible to a wider range of people that do not have unlimited fast internet.

A related http header called "Save-Data" is already widely used, especially outside of the western world.

With the header, you can send alternative resources from your server, but in Chromium and Firefox, you can also access it through JavaScript and prevent loading resources there.

Prefers-reduced-data is also a prime candidate to implement as a progressive enhancement.

By default, everyone gets the lean and fast experience, and only when there is no preference, do users get the full-fat experience.

Here's some things you can put into a "no-preference" media query.

You can add your custom web fonts in an no-preference media query, and fall back to system fonts by default.

Large background images can also be switched for gradients or smaller images.

Additionally, you can use it in picture elements to load small source images and in JavaScript to prevent preloading or auto-playing videos using the "window.matchMedia" API.

With no support at the moment, that would mean that everyone gets the lean version, so this isn't a strategy that can work for everyone.

Luckily, you can already implement the inverse today, where "(prefers-reduced-data: reduce)" overwrites the font, large background images, et cetera.

They won't work now, but when browsers come out with support for reduced data, these will automatically start working.

I'd encourage you to start implementing this today.

Prefers-contrast can tell you if a user wants more, or less contrast.

It's not currently available in browsers ,though "prefers-contrast: more" is implemented in the latest Safari technology preview, where it maps to the "increase contrast" accessibility option in MacOS.

It's also secretly implemented in most other browsers due to its proximity to forced-colers, which is a way of forcing a certain contrast.

But more on that later.

Increased contrast intuitively makes sense.

More contrast usually means things are easier to read, but who wants less contrast? Well, have you ever turned on your phone screen in the middle of the night only to recoil away at the bright light? That's too much contrast.

People with light sensitivity or photophobia can experience this throughout the day.

It can also be around certain colors.

Very highly saturated colors have more contrast, which is why yellow text on red backgrounds is used to often draw attention.

On screens that can be downright unpleasant.

So what should we do with more and less contrast? For more, we can take a page out of Apple's book: increase contrast on macOS does increase the contrast between background and text, but not hugely so (they already have good contrast}}.

What does end up happening is that rather than depend on shading to convey different areas, everything additionally gets a clear border.

Less contrast is a little harder to envision, but consider something like night-mode that exists on most devices now.

By dimming the blues, you already end up with less contrast.

It will probably make sense to consider desaturating your design here.

Like reduced data, you could implement this now, though it's definitely more difficult to understand how this will impact people and, how people will want to be impacted by this setting.

Forced-colors maps to the Windows "high contrast mode", which is a feature that lets you reconfigure all the colors on your device.

This means you get to choose the default text, background, button colors, and more.

It overwrites everything.

It's available in Edge and Chrome, and behind a flag in Firefox.

While forced-colors usually means people end up with a white-on-black or black-on-white color scheme, there's also plenty of people that use it to dim the screen or render it in a sepia-like color scheme, depending on their personal preferene.

That's also how forced-colors affect your sites.

The layout will be preserved, but all colors will be overwritten.

When there's text on images, that text will get a background color called a "backplate" automatically.

Because forced-colors already overwrites all your colors, there's not much for you to do other than make sure that any elements that haven't changed along in the right way, like svg icons, are changed along with it.

You can do this with a set of CSS Color Names that map to certain types of content, like windowText and buttonface.

Y ou can also use the media query to set certain colors back to the original ones where important, for example, when people need to choose a color on an e-commerce website.

Speaking of color, Color-gamut lets you detect if your site is being shown on a screen with a wider gamut.

Most screens out there operate in sRGB by default.

But for example, P3 screens on Apple devices can show more colors than you can describe in sRGB, with more vivid colors in particular.

The third possible value: "rec2020" is the widest color space we currently have available, though, you won't be able to find that on devices anywhere soon.

You can use this media query in Safari, Edge, and Chrome to serve up images in P3, or use colors that are outside of the sRGB color space, while providing the right fallbacks for regular screens.

You can use the CSS color function to describe colors in P3, where the "R", "G", and "B" channels are described in decimal numbers between 0 and 1.

While the previous media queries I mentioned all had some support or are about to get wider support, the rest of this talk will mention media queries that have no support yet, and no indication that support is coming.

Explaining them will be a little more theoretical and we'll spend a little less time on them.

A common pattern to make designs more interesting is to place text over images or layer transparencies to create depth.

However, this can make it difficult for people to read, for example, when they have visual impairments.

But it can also be distracting to people.

The prefers-reduced-transparency media query should be used to tone that transparency or translucency down, especially around text over images.

Light-level comes with three possible values: dim, normal, and washed - for dark, "normal" and bright environments respectively.

When these get triggered, specifically, will be left up to operating systems, with the assumption that they will use built-in light sensors to detect the light levels in the environment.

Operating systems already compensate for light levels, of course, by dimming and brightening the screen when they think that's needed.

For your website, you might choose to decrease contrast and overall brightness for dim situations and bump up the text contrast for washed situations so that it remains readable on the screen even when shown in bright sunlight.

Environment-blending describes how the screen is "blended" with the environment.

This sounds like some sci-fi level stuff, but there's already devices out there where this makes sense.

"Opaque" is the default, and it's like your regular monitors and phones.

They don't blend in with the environment at all, you can't see through them.

"Additive" is when what's on the screen is added to the environment.

For example, this is how the HoloLens works.

Light is projected on the HoloLens screen and anywhere where there isn't light, you can see your environment instead.

"Subtractive" is the opposite, and it's how LCD works.

You can see through an LCD, until it turns a pixel on, then it becomes black and you can no longer see what's behind it - Gameboys worked like this.

With CSS custom media queries, we can improve our CSS by only defining a media query once and then using that property everywhere.

If we ever need to change that value, we only have to update it in a single place.

Just like how we use CSS custom properties already.

And you can already use this with postcss-preset-env.

That brings us to the end of this presentation.

There are a number of media queries we don't have time to discuss.

Among them: resolution, screen-spanning, and the rest of the items on screen.

If you want to learn about those, I wrote a huge guide available at

I hope this tour gave you a taste of what's to come, and inspiration to start implementing prefers-color-scheme, prefers-reduced-motion, and even prefers-reduced-data in the websites you're working on today.

Throughout this talk, I've used Polypane, the browser for developers that I make to illustrate some of these media queries.

I'm biased, but I think it's an excellent way to develop websites, in particular to test across different media queries.

If it looks interesting to you, please check it out at, and start a free trial.

If you end up implementing anything mentioned during this presentation, please let me know! I'm @Kilian Valkhof on Twitter and would love to see what you made.

Beyond responsive
design: new and future
media queries

Kilian Valkhof

Responsive design

The way a website adapts to different screen sizes,
by reflowing and repositioning content as the
available space allows

New responsive design

The way a website adapts to...

devices, user preferences, and surroundings

Image of a timeline of a range of media queries, delineated into three sections: Past, Now, and Future, with each query falling at some point along the spectrum.

Between the Past and Now categories are (in order from closest to the past to closest to now) are:


Between the Now and Future categories are (in order from closest to now to closest to future) are:


@media (prefers-color-scheme: light) {

Screenshot from the Polypane browser interface demonstrating how dark mode creates a dark background screen

@media (prefers-color-scheme: light) {

Code demo adding the sepia value to the prefers-color-scheme query in the Polypane browser interface

Cheap dark mode

         @media (prefers-color-scheme: dark) {
           :root {
             background: #111;
             filter: invert(1) hue-rotate(180deg);

             img, video {
               filter: invert(1) hue-rotate(180deg);

Code demo of how to implement "cheap dark mode"

Side by side Polypane demo of the Web Directions Hover Conference homepage as it currently appears compared with how it appears with "cheap dark mode" applied. The main body block on the original page has a blue background color palette applied to an image element, overlaid with mostly white type. The header and footer banners have a white background with black type and colored images. In the dark mode version, shades of blue in the main background image have been inverted, as has the type, which is now black in the body text and white in the header and footer.

@media (prefers-reduced-motion: no-preference) {

Side by side Polypane demo of the Apple iphone 12 homepage before and after the prefers-reduced-motion:reduce media query is applied. The original page opens with an animation of the text "Say hello to", followed by the 5G logo, followed by a single iphone morphing into a fan of additional multicolored iphones. At various points, the animation elements change size, color, and screen position. The animation ends with a static image of five colored phones in a fan shape.

In the same page with the reduce query applied, the animation is replaced with the static image only of the five phones at the end of the animated version

Repeat of the prior side by side demo highlighting the query's request for "reduced" motion rather than "no" motion (which is what we get when the code is applied to the iphone 12 homepage, no motion at all

/* Regular CSS */

@media (prefers-reduced-motion: no-preference) {
  /* Only add in animations here */

Code demo highlighting where the animations should optimally fall in the code hierarchy of a new site. Animations should be added only after the prefers-reduced-motion query has been set to :no-preference so that modern browsers recognize the query and older

    @media (prefers-reduced-motion: reduce) {
      *, ::before, ::after {
        animation-delay: -1ms !important;
        animation-duration: 1ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 1ms !important;
        transition-delay: -1ms !important;
        background-attachment: initial !important;
        scroll-behavior: auto !important;

Code demo of how to apply Kilian's "Cheap reduced motion" fix for existing websites whose animations may be too complex to retroactively apply the prefers-reduced-motion query code to. This "cheap" method involves adding a 1ms value and and !mportant tag to animation and transition elements and disabling smooth scrolling and fixed backgrounds without overriding any Javascript that may require animationend or transitionend events from these elements to execute

Cheap reduced motion in Javascript

    const video = document.createElement('video');
    const canAutoPlay = window.matchMedia(
        '(prefers-reduced-motion: no-preference)’

    video.setAttribute('autoplay', canAutoPlay);

Code demo of how to use the window.matchMedia API in JavaScript to check for the prefers-reduced-media query and apply "cheap reduced motion" in JavaScript

@media (prefers-reduced-data: no-preference) {

Code demo of the prefers-reduced-data media query, which is used primarily to enhance accessibility

@media (prefers-reduced-data: no-preference) {
@media (prefers-reduced-data: no-preference) {

Save-Data: on;

Code demo of the http "save-data" header with the prefers-reduced-data query

Prefers Reduced Data using save-data header

  if ('connection' in navigator) {
       if (navigator.connection.saveData) {
         // prevent loading resources here
Simplified. in reality you need
navigator.webkitConnection and
navigator.mozConnection (behind flag)

Code demo of how to use the save data header using Javascript if you are using Chromium or Firefox

reduced data in CSS

    body {
      font-family: system-ui, sans-serif;
      background: #103478;

    @media (prefers-reduced-data: no-preference) {
      @font-face {
        font-family: Inter;
        src: url(inter.woff2) format(‘woff2’);

        body {
          font-family: Inter, system-ui, sans-serif;
          background-image: url(my-big-image.png);

Code demo of how to use the prefers-reduced-data:no-preference query in CSS to implement progressive enhancements into your code hierarchy so that by default all users get the fast and lean version of your site and only when: no preference is selected do users get the larger data volume experience

reduced data in picture element and JS

    media=“(prefers-reduced-data: no-preference)” />

    src=“my-small-image.webp” />

              const canAutoplayAndPreload = window.matchMedia(
                  '(prefers-reduced-data: no-preference)'

              video.setAttribute('autoplay', canAutoplayAndPreload);
              video.setAttribute('preload', canAutoplayAndPreload);

Demo of using prefers-reduced-data within the picture element to load smaller images by default and with window.matchMedia API in JavaScript to stop preloading or autoplay videos

reduced data in CSS, inverted

    @font-face {
      font-family: Inter;
      src: url(inter.woff2) format(‘woff2’);

    body {
      font-family: Inter, system-ui, sans-serif;
      background: #103478 url(my-big-image.png);

    @media (prefers-reduced-data: reduce) {
      body {
        font-family: system-ui, sans-serif;
        background: #103478;

Code demo of using prefers-reduced-data: reduce in CSS to override fonts, large background images and other large data elements. Currently no support for this but the demo code can be implemented now and will go live when browsers are have support for reduced data.

@media (prefers-contrast: no-preference) {

Demo of the prefers-contrast:more or prefers-contrast:less media query which sets contrast preferences. Currently isn't supported in browsers but available in Safari Tech preview and is secretly available in most browsers via proximity to forced-colors

@media (prefers-contrast: no-preference) {

Image of a timeline delineated by Less on the left of the scale, no-preference in the centre, and more on the far right. A "shrugs" emoji is on the 'less' side and a "thumbs-up" emoji is on the 'more' side

@media (prefers-contrast: more) {

Side by side of the MacOS Accessibility menu with the Display submenu page open. The image on the left shows the "Display Contrast" scrollbar set to normal (from a scale of normal to maximum) and the "increase contrast" checkbox left blank. The image on the right shows the same menu with "Increase contrast" checked and "Reduce transparency" greyed out

@media (prefers-contrast: less) {

Side by side Polypane demo of the Figma homepage before and after night-mode is applied. After night-mode is applied, the screen is much dimmer and has a yellowish hue

@media (forced-colors: none ) {

Side by side comparison of the Facebook/react page before and after the forced-colors query has been applied (via high-contrast mode). The page on the left has a white background and many different colors. The same page on the right has the layout preserved, but now has a black background with only two main type colours used and is much cleaner

@media (forced-colors: active) {

    	        windowText  color of text content.
             highlightText  color of selected text.
                 highlight  background color of selected text.
               	buttonFace  color of a <button> element's text.
              window color  of the background.
           The <a> element  color of links.

List of elements the forced-colors:active query controls

Disabling forced colors

                   @media (forced-colors: active) {
                     .preserve-style {
                       forced-color-adjust: none;

Code demo of how to disable forced-colors for situations where the color needs to be preserved in certain areas of the site

@media (color-gamut: srgb    ) {

Demo of how the color-gamut media query can test the range of colors a screen will allow

@media (color-gamut: srgb    ) {
 p {
   color: rgb(255 0 0); /* the reddest red in sRGB */

 @media (color-gamut: p3) {
   p {
     /* the reddest red the display can show */
     color: color(display-p3 1 0 0);

Code demo of how the color-gamut query can be used in Edge, Safari, and Chrome to use colors outside the sRGB color space while preserving defaults for smaller gamuts

                                  Here be dragons 🐉

Dragon emoji representing entering murky waters without support

@media (prefers-reduced-transparency: no-preference) {

Side by side Polypane demo of the Web Directions Conffab homepage before and after the prefers-reduced-transparency: reduce query applied. The page is typical of many web designs, with a range of brightly colored bars that some users may find distracting and may desire to tone down. The image on the right shows the page with reduced transparency applied. NB:(Image may not appear as clear in conference talk as in slide deck)

@media (light-level: normal) {

Three side by side images of Kilain's homepage with dim, normal, and washed effects applied, demonstrating the limits of current light-level effects

@media (environment-blending: opaque      ) {

Image demo of the Microsoft HoloLens mixed reality device with additive objects appearing on the screen wherever there is an absence of light

Image of a vintage Nintendo Gameboy handheld video gaming device demonstrating how subtractive interfaces work

Custom media

@custom-media --small-screen (max-width: 768px);

@media (—small-screen) {
  /* your CSS */


There’s much more to discover

display mode
comparison notation


Get started with these today




Image of the Polypane browser interface comparing a range of versions of the Web Directions hover conference homepage with various properties and effects applied to illustrate the media queries and their potential

wavy goodbye hand emoji

Kilian Valkhof - @kilianvalkhof -