Detecting and designing for contrast levels
With prefers-contrast, CSS can detect whether a user has requested more or less contrast in the operating system settings, and adjust colors, borders and focus indicators accordingly. The media feature closes a real accessibility gap that used to be solved awkwardly with separate high contrast stylesheets.
Table of Contents
- 1. Why users have different contrast needs
- 2. Syntax: more, less, custom and no-preference
- 3. Which OS settings trigger prefers-contrast
- 4. Building high contrast themes with Custom Properties
- 5. prefers-contrast versus forced-colors explained
- 6. Practical use: forms and focus indicators
- 7. Testing prefers-contrast in browsers and DevTools
- 8. Combining with prefers-color-scheme and reduced motion
- 9. prefers-contrast compared to other A11y media features
- 10. Summary
- 11. FAQ
1. Why users have different contrast needs
Contrast perception is not a binary trait. People with low vision, age related macular degeneration, or certain neurological conditions often need markedly higher contrast to reliably distinguish text and controls. Conversely, some users with sensory processing conditions or migraines find very high contrast unpleasantly harsh and prefer a softer, lower contrast appearance. The CSS media feature prefers-contrast makes exactly this preference usable for websites, straight from the operating system settings.
Before prefers-contrast, the only way to respond to contrast needs was a manually toggleable high contrast mode built into the website itself, which many users never found because it was hidden in a settings menu. With prefers-contrast, the website automatically reacts to a system wide decision the user has already made, without any extra click. This significantly lowers the barrier to accessible design.
2. Syntax: more, less, custom and no-preference
The media feature prefers-contrast supports four possible values. more signals that the user wants increased contrast, less signals the opposite, reduced contrast. The value custom occurs when the operating system offers a user defined contrast setting with its own color values that is neither clearly "more" nor "less". If no preference is set, the query returns no-preference, which in most rule sets corresponds to the default behavior without additional adjustment.
The base syntax follows the familiar pattern of other user preference media features: @media (prefers-contrast: more) { ... } groups all rules that only apply when the contrast preference is active. It is important to think of prefers-contrast additively, as a reinforcement of existing rules rather than a completely separate stylesheet. That keeps maintenance manageable and prevents contrast adjustments and base styles from drifting apart over time.
/* Base styles */
.card {
border: 1px solid #e2e8f0;
color: #334155;
background: #ffffff;
}
/* Enhanced contrast: thicker borders, darker text */
@media (prefers-contrast: more) {
.card {
border: 2px solid #0f172a;
color: #000000;
}
}
/* Reduced contrast: softer palette for light sensitivity */
@media (prefers-contrast: less) {
.card {
border-color: #cbd5e1;
color: #475569;
background: #f8fafc;
}
}
3. Which OS settings trigger prefers-contrast
On macOS, the "Increase contrast" setting under Accessibility, Display activates the value more for all browsers on the system. Windows offers a similar toggle under "Ease of Access, Contrast themes", which depending on the chosen theme triggers either more or, in certain configurations, custom, since Windows contrast themes come with their own color palettes. Mobile devices running iOS and Android have comparable options under their accessibility settings, which trigger the same media query logic.
An important practical note: prefers-contrast is supported by current versions of all major browsers, though with slightly different interpretations of what exactly counts as "increased contrast" at the operating system level. Developers should not assume that more and custom represent exactly the same underlying system setting in every browser, but should treat both values equivalently in their own rules where it makes sense.
4. Building high contrast themes with Custom Properties
The most maintainable approach to prefers-contrast is to consistently define color values through CSS Custom Properties and reassign them inside the media query, instead of duplicating every single component rule. A central set of variables such as --text-color, --border-color and --focus-ring-color is defined at the root level, and every component exclusively references these variables. Inside @media (prefers-contrast: more), the same variable names are overridden with reinforced values, and every component reacts automatically, without its own contrast logic.
This pattern drastically reduces the maintenance burden for prefers-contrast, because new components automatically inherit the contrast adjustment as long as they stick to the central Custom Properties. It also prevents the common mistake where individual components get forgotten during a redesign and therefore respond inconsistently to the user preference.
:root {
--text-color: #334155;
--border-color: #e2e8f0;
--focus-ring-color: #93c5fd;
--focus-ring-width: 2px;
}
@media (prefers-contrast: more) {
:root {
--text-color: #000000;
--border-color: #000000;
--focus-ring-color: #1d4ed8;
--focus-ring-width: 3px;
}
}
/* Every component simply consumes the variables */
.button, .input, .card {
color: var(--text-color);
border: var(--focus-ring-width) solid var(--border-color);
}
.input:focus-visible {
outline: var(--focus-ring-width) solid var(--focus-ring-color);
outline-offset: 2px;
}
5. prefers-contrast versus forced-colors explained
A common misunderstanding is treating prefers-contrast and forced-colors as the same feature. prefers-contrast is a pure preference query, the website retains full control over its color palette and merely adjusts it, while with forced-colors: active the operating system, usually Windows high contrast mode, enforces its own limited system palette and overrides most custom colors entirely. prefers-contrast is thus a gentle design decision, forced-colors a hard system behavior.
In practice this means: a website can use prefers-contrast: more to offer darker borders and stronger text colors, while additionally using its own forced-colors rules to ensure usability is preserved even when Windows takes over the colors entirely. Both media features complement each other, but they do not replace one another, and a detailed technical comparison of both features would go beyond the scope of this article, though it deserves its own, deeper treatment.
/* prefers-contrast: soft, opt-in enhancement, full color control retained */
@media (prefers-contrast: more) {
.alert {
border: 2px solid #7c3aed;
color: #000000;
}
}
/* forced-colors: system takes over the palette, use system keywords instead */
@media (forced-colors: active) {
.alert {
border: 2px solid CanvasText;
forced-color-adjust: none;
}
}
6. Practical use: forms and focus indicators
Forms benefit particularly strongly from prefers-contrast, because input fields with faint, light gray borders are often barely distinguishable from the background for users with low vision. A combination of reinforced borders, darker labels and a clearer focus indicator via @media (prefers-contrast: more) makes forms considerably more usable for exactly this user group, without changing the design for everyone else.
Focus indicators are a second critical use case for prefers-contrast. A thin, pale focus ring that is still acceptable under normal contrast is often completely missed once the contrast preference is active. A widened outline with higher color contrast against the background, controlled through the same Custom Property strategy as in the previous section, ensures that keyboard users with increased contrast needs can always tell which element currently has focus.
7. Testing prefers-contrast in browsers and DevTools
Chrome and Edge DevTools offer a direct emulation of prefers-contrast through the rendering panel: under "Rendering", "Emulate CSS media feature prefers-contrast" can be set to more, less or custom, without changing the actual operating system setting. This significantly speeds up development, since no switch to the system settings and no browser restart is needed to test different contrast levels.
Firefox does not currently offer a comparable emulation with the same depth, which is why an additional manual test with an actually activated system setting, such as "Increase contrast" on macOS, remains worthwhile before every release. Automated visual regression tests can cover prefers-contrast through the emulation API of Playwright or Puppeteer, enabling consistent verification in CI pipelines without manual intervention.
8. Combining with prefers-color-scheme and reduced motion
prefers-contrast can be combined with other user preferences in a single media query, for example @media (prefers-color-scheme: dark) and (prefers-contrast: more), to provide a dedicated palette specifically for users with a dark color scheme and increased contrast needs at the same time. This combination is particularly relevant because a high contrast value that works well in light mode often needs entirely different color values in dark mode to achieve the same perceived contrast strength.
Combining with prefers-reduced-motion also makes sense when contrast adjustments are tied to transition effects such as color change animations. A user who wants both increased contrast and reduced motion should see the contrast change directly, without an animated transition. Media queries can be nested arbitrarily, as long as the resulting CSS specificity of the selectors is kept in view.
9. prefers-contrast compared to other A11y media features
Several CSS media features address different aspects of accessibility, prefers-contrast is only one of them.
| Media Feature | Addresses | Developer Control | Typical Use |
|---|---|---|---|
| prefers-contrast | Low vision, light sensitivity | Full, own palette stays active | Reinforce borders, text and focus colors |
| forced-colors | Windows high contrast mode | Limited, system enforces palette | System color keywords and forced-color-adjust |
| prefers-reduced-motion | Vestibular disorders | Full | Reduce animations and transitions |
| prefers-color-scheme | Brightness preference, eye strain | Full | Light and dark mode palettes |
| prefers-reduced-transparency | Readability with transparency effects | Full | Disable glassmorphism backgrounds |
The key difference between prefers-contrast and most other features in this table is the degree of control the developer retains. While forced-colors forces the website into a system provided palette, prefers-contrast keeps full creative freedom, only the starting values get adjusted based on the user preference. This makes prefers-contrast the most flexible, but also the most dependent on the developer's own diligence, tool in this list.
Mironsoft
Accessible CSS architecture and contrast audits
Implementing contrast requirements cleanly in CSS?
We build prefers-contrast themes through central Custom Properties, test against real operating system settings, and align them with your WCAG requirements.
Contrast Audit
Reviewing the existing color palette for contrast issues and WCAG compliance
Custom Property Themes
Implementing maintainable contrast levels through central variables
Test Automation
Visual regression tests for every contrast level in the CI pipeline
10. Summary
prefers-contrast detects a user's contrast preference directly from the operating system settings and exposes it to CSS through the values more, less, custom and no-preference. The most maintainable implementation path goes through central Custom Properties that get reassigned inside the media query, instead of duplicating every component rule individually. Forms and focus indicators benefit particularly strongly from this adjustment.
The clear distinction from forced-colors matters: prefers-contrast is a gentle, fully developer controlled design decision, while forced-colors represents a hard system behavior with an enforced palette. Both features can be combined but address different user scenarios and each deserve their own careful testing in DevTools and with real system settings.
The prefers-contrast Media Feature — The Essentials at a Glance
Values
more, less, custom and no-preference reflect the user's system setting.
Implementation
Central Custom Properties instead of component duplicates, for minimal maintenance effort.
Distinction
Not identical to forced-colors, there the system enforces the palette, here the website retains control.
Testing
Chrome DevTools rendering panel emulates all four values without changing the system setting.