User Preference At The CSS Level

Designers and developers make many choices about the interfaces they build, but the people who actually use those interfaces have preferences too. Supporting the needs of individuals with disabilities means offering accessible choices, and one promising approach is through user-focused CSS media features. These features detect a user’s system or browser settings and allow us to customize the experience accordingly.

A handful of these media features currently matter most for building inclusive experiences: prefers-color-scheme for light/dark themes, forced-colors for an enforced limited palette, and inverted-colors for reversed colors. Each targets a different aspect of color rendering and accessibility.

Detecting Light And Dark Themes

The prefers-color-scheme CSS media feature identifies whether users prefer a light or dark color theme, based on their operating system or user agent settings. It accepts two values: light and dark. The default theme presented to users is the light version in most cases, even when the user expresses no preference. However, the opposite can also be true: some sites default to a dark theme and switch to a light one using the media (prefers-color-scheme: light) feature.

For users who opt for dark mode, media (prefers-color-scheme: dark) adjusts theme elements like text, links, and buttons so they stand out against darker backgrounds. There was previously a no-preference value to indicate when users had no particular theme preference, but user agents now treat light themes as the default, making that value obsolete.

This is one of the most widely supported CSS media features, with a very large percentage of browser support at 94%.

@media (prefers-color-scheme: dark) {
  body {
    background-color: #282828;
  }

  .without [data-word="without"] .char:before,
  .without [data-word="without"] .char:after {
    color: #fff;
  }
}
Default color scheme (top) and dark mode (bottom)
Default color scheme (top) and dark mode (bottom). (Demo)

Consult the user setting guides for Mac and Windows to learn how to set this preference in those operating systems.

Pro-tip: A more sophisticated solution for demonstrating user preference settings is Chrome’s Rendering tab combined with a CSS media features emulator. You can easily switch between light and dark modes to emulate prefers-color-scheme exactly as users experience it. This is convenient for live demos where you need to show preference changes quickly or emulate features not fully supported by your OS or browser.

Browser support for <code src=prefers-color-scheme at the time of writing">
Browser support for prefers-color-scheme at the time of writing. (Large preview)

Handling User-Enforced Color Palettes

The forced-colors CSS media feature detects when the user agent enforces a forced colors mode. In that mode, a limited palette chosen by the user is applied to the page. This newer feature provides an alternative approach to color handling for non-Windows devices and is expected to eventually replace Windows High Contrast Mode.

It has two values: none and active. media (forced-colors: none) indicates the mode is inactive and uses the default color scheme, while media (forced-colors: active) signals that the mode is active and the user-selected limited palette is in effect. Enabling this mode does not necessarily imply a preference for higher contrast; the color adjustments align with the specific user choice, which may not fall into low or high-contrast categories.

Note: Certain properties are affected by forced colors mode, so plan your theme accordingly. Eric Bailey’s article on Windows High Contrast Mode, Forced Colors Mode And CSS Custom Properties covers the integration of this feature with custom properties in depth.

@media (forced-colors: active) {
  body {
    background-color: #fcba03;
  }

  .without [data-word="without"] .char:before,
  .without [data-word="without"] .char:after {
    color: #ac1663;
  }

  .without {
    color: #004a72;
  }
}
Forced colors demo
Forced colors active. (Demo)

The forced-colors feature is currently supported by 31% of the most popular browsers — desktop versions of Chrome, Edge, and Firefox. Support is growing, but not all operating systems yet offer a setting to activate forced colors mode. Windows is the sole exception, providing the functionality through High Contrast mode, which allows users to create customized themes that override the defaults.

On non-Windows machines, you can emulate this behavior via Chrome’s Rendering tab and emulator, but instead focus on emulating forced-colors rather than prefers-color-scheme.

Browser support for <code src=@prefers-forced-colors at the time of writing">
Browser support for @prefers-forced-colors at the time of writing. (Large preview)

Managing Inverted Colors

The inverted-colors CSS media feature determines whether content is shown in its standard colors or with colors reversed. It supports two values: none and inverted. The media (inverted-colors: none) value indicates that the inversion mode is not activated and the default color scheme is used. Using media (inverted-colors: inverted), you render an inverted color theme whenever a user chooses that option.

One coding approach for this feature is to write the inverse of what you want the user to see in the code, ensuring correct rendering after the user setting is applied. For instance, if you want an element’s background to be #e87b2d (a tangerine orange), you’d write the opposite color, #1784d2 (powder blue), in the theme code. Incorporating this inverse color into the stylesheet causes the intended tangerine orange to render correctly when users enable the inverted setting.

@media (inverted-colors: inverted) {
  body {
    background-color: #99cc66;
  }

  .without [data-word="without"] .char:before,
  .without [data-word="without"] .char:after {
    color: #ee1166;
  }

  .without {
    color: #111111;
  }
}
The top (demo) is what the code looks like before it is inverted. The image below (demo) is what can actually be seen when the filter is activated. (Image credit: ) (Large preview)

Current browser support for inverted-colors is 20% for Safari desktop and iOS browsers. Chrome’s Rendering tab and emulator cannot simulate this feature, but you can emulate it in Firefox (version 114 or newer):

  1. Open a new tab in Firefox, type or paste about:config into the address bar and press Enter/Return. Acknowledge the warning if prompted.
  2. Search for layout.css.inverted-colors in the search box.
  3. Toggle the preference from false to true.
  4. Enable the inverted colors setting in your operating system and navigate to a page with an inverted-colors theme to see the effect.

The inverted-colors setting is offered on Mac and Windows.

Browser support for <code src=inverted-colors at the time of writing">
Browser support for inverted-colors at the time of writing. (Large preview)

Looking Closer at Contrast and Motion Preferences

Beyond color itself, the amount of contrast used to display that color is vital for readability. Insufficient contrast can make it difficult to distinguish text, icons, and key graphics from their backgrounds. This is a problem for the roughly 46 million people worldwide with low vision, but it also affects older adults, those using monochrome displays, and people in sub-optimal environments, like a room with poor lighting.

The prefers-contrast Media Feature

The prefers-contrast CSS media feature detects a user's system-level preference for higher or lower contrast. This setting can inform design decisions beyond just color ratios, such as adjusting the visual prominence of UI elements by altering border widths or shadows.

There are four distinct values for this feature:

  • no-preference: The user has not made a specific choice (this is the default).
  • less: The user prefers an interface with lower contrast.
  • more: The user prefers an interface with stronger visual contrast.
  • custom: The user has defined a specific set of colors or a palette, which could include themes like monochrome blue or high-contrast primary colors.

Note: The custom value aligns with the color palette defined by users of the forced-colors: active value. Any CSS written for one should account for the other.

@media (prefers-contrast: more) {
  .title2 {
    color: var(--clr-6);
  }

  .aurora2__item:nth-of-type(1),
  .aurora2__item:nth-of-type(2),
  .aurora2__item:nth-of-type(3),
  .aurora2__item:nth-of-type(4) {
    background-color: var(--clr-6);
  }
}

@media (prefers-contrast: less) {
  .title {
    color: var(--clr-5);
  }

  .aurora__item:nth-of-type(1),
  .aurora__item:nth-of-type(2),
  .aurora__item:nth-of-type(3),
  .aurora__item:nth-of-type(4) {
    background-color: var(--clr-5);
  }
}

@media (prefers-contrast: custom) {
  .aurora2__item:nth-of-type(1) {
    background-color: var(--clr-1);
  }
  .aurora2__item:nth-of-type(2) {
    background-color: var(--clr-2);
  }
  .aurora2__item:nth-of-type(3) {
    background-color: var(--clr-3);
  }
  .aurora2__item:nth-of-type(4) {
    background-color: var(--clr-4);
  }
}
Comparing contrast preferences, clockwise starting at top-left: Default, Less, More, and Custom
Comparing contrast preferences, clockwise starting at top-left: Default, Less, More, and Custom. (Demo) (Large preview)

Currently, around 91% of widely used browsers support this media feature. However, most of that support is focused on the more value, while support for less and custom is less comprehensive. For testing, Chrome's DevTools Rendering tab offers an emulation option that allows you to preview all variations of the feature.

Browser support for <code src=@prefers-contrast at the time of writing">
Browser support for @prefers-contrast at the time of writing. (Large preview)

The prefers-reduced-transparency Media Feature

The prefers-reduced-transparency CSS media feature checks if a user has requested fewer transparent or translucent layer effects on their operating system. This is a boolean-style check with only two possible values:

  • no-preference: The user has not specified a preference (default).
  • reduce: The user wants an interface that minimizes the use of transparency effects.
@media (prefers-reduced-transparency: reduce) {
  .title,
  .title2 {
    opacity: 0.7;
  }
}

Be aware that this feature currently has 0% browser support and is considered highly experimental. It should not be used in production yet. If you want to test how it works, you can emulate its behavior in Firefox 113 or newer.

  1. Open a new tab in Firefox, type about:config in the address bar, and press Enter/Return. Acknowledge the warning to continue.
  2. In the search box, type layout.css.prefers-reduced-transparency and wait for the filtered results.
  3. Toggle the preference from false to true.
  4. Adjust your operating system’s transparency settings and reload any page that uses the media feature to observe the effects.
Comparing transparency preferences: Default (top) and Reduced (bottom)
Comparing transparency preferences: Default (top) and Reduced (bottom). (Demo) (Large preview)

Handling Motion and Data for Comfort and Performance

Motion on the web, whether from video, GIFs, or SVGs, can be engaging but also problematic. Vestibular disorders, seizure disorders, and migraines can all be triggered by moving content. Media features designed for motion allow us to deliver a rich, dynamic experience to those who want it while providing a safe, static experience for those who need it.

The prefers-reduced-motion Media Feature

The prefers-reduced-motion CSS media feature identifies users who have requested to minimize non-essential animation. Its values are:

  • no-preference: The user has not specified a preference (default).
  • reduce: The user wants an interface that eliminates or substitutes motion-based animations that may cause discomfort or distraction.
@media (prefers-reduced-motion: reduce) {
  .bg-rainbow {
    animation: none;
  }

  .perfection {
    .word {
      .char {
        animation: slide-down 5s cubic-bezier(0.75, 0, 0.25, 1) both;
        animation-delay: calc(#{$delay} + (0.5s * var(--word-index)));
      }
    }

    [data-word="perfection"] {
      animation: slide-over 4.5s cubic-bezier(0.5, 0, 0.25, 1) both;
      animation-delay: $delay;

      .char {
        animation: none;
        visibility: hidden;
      }

      .char:before,
      .char:after {
        animation: split-in 4.5s cubic-bezier(0.75, 0, 0.25, 1) both alternate;
        animation-delay: calc(
          3s + -0.2s * (var(--char-total) - var(--char-index))
        );
      }
    }
  }
}
Default motion
Reduced motion

This is currently the best-supported CSS media feature for user choice, with support reaching 94% across major browsers. Most operating systems include a setting for reduced motion that can trigger this. Chrome's Rendering tab also offers a specific emulation option for this feature.

Support is not the primary debate for this feature. Instead, experts discuss the degree of reduction: should motion be slowed, stopped entirely, or simply prevented from infinite looping? The answer will depend on the specific component, but a useful heuristic is that less is often more when users make this request.

Browser support for <code src=prefers-reduced-motion at the time of writing">
Browser support for prefers-reduced-motion at the time of writing. (Large preview)

The prefers-reduced-data Media Feature

Finally, the prefers-reduced-data CSS media feature lets us serve lighter, more optimized content to users who want to minimize data usage. It accepts only a no-preference (default) or reduce value. The preference can be set at the operating system level or by the user agent based on the data-saving preferences of the user. In the case of a user agent setting, it often derives from the same preference that controls the Save-Data HTTP request header.

Even though the Save-Data header is still considered experimental, it already has 72% browser support. The same cannot be said for the CSS media feature itself. Like prefers-reduced-transparency, prefers-reduced-data is experimental with 0% support and should be avoided in production code.

@media (prefers-reduced-data: reduce) {
  .bg-rainbow {
    animation: none;
  }

  .perfection {
    .word {
      .char {
        animation: none;
      }
    }

    [data-word="perfection"] {
      animation: none;

      .char {
        animation: none;
        visibility: hidden;
      }

      .char:before,
      .char:after {
        animation: none;
      }
    }
  }
}
Visual rendering of <code src=@media (prefers-reduced-data: reduce">
Visual rendering of @media (prefers-reduced-data: reduce. (Demo) (Large preview)

To test the feature, you can use Chrome version 85 or newer to enable it. This flag also works on Edge, Opera, and Android Chrome, though emulation tools are only readily available in the desktop version.

  1. Open a new tab in Chrome, type chrome://flags in the address bar, and press Enter/Return.
  2. Search for experimental-web-platform-features in the search box.
  3. Change the dropdown preference from disabled to enabled.
  4. Use the Chrome Rendering tab to select the prefers-reduced-data media feature for emulation.
Browser support for <code src=prefers-reduced-data at the time of writing">
Browser support for prefers-reduced-data at the time of writing. (Large preview)

The Multiplier Effect of User Choice

Accessibility in design is sometimes framed as a trade-off between user needs and visual flare or technical innovation. That framing is a false dichotomy. By using these CSS media features that respond to color, contrast, and motion, we can build experiences that are both technically advanced and aesthetically pleasing for everyone on the spectrum of needs.

With browser support exceeding 90% for the currently functional features, we can already offer users a combined 16 different preference combinations today. As experimental features like prefers-reduced-data and prefers-reduced-transparency gain operating system and browser support, that number skyrockets to a staggering 256 possible combinations. This explosion of options highlights the potential impact we have on the user experience.

Digital products should be for all individuals. By working with the system settings that users have already chosen, we are not limiting design; we are creating flexible systems that grant agency to the user. The appropriate strategy for designers and developers is to provide options and then allow each person to choose their own path. (yk, il)