Blog · 8 min read
No OS Toggle Needed, High Contrast CSS Patterns Developers Can Drop In

Forced colors mode replaces your carefully chosen palette with a set the browser or operating system controls, and the fix starts with detection. Use @media (forced-colors: active) to know when it’s on, then reach for CSS system color keywords instead of hex values. Keep your baseline design WCAG-compliant regardless, since forced colors is a user preference layered on top of your work, not a replacement for it.
TL;DR:
- Using
@media (forced-colors: active)allows detection of high-contrast mode to adapt styles without removing the default design, which must still meet WCAG contrast standards.- Replace custom colors with CSS system keywords like Canvas, CanvasText, and ButtonFace to ensure readability and consistency in forced colors mode.
- Be cautious with
forced-color-adjust: none, using it sparingly only when specific brand elements or visual cues rely on fixed hues that should not change.- Fix common component issues such as disappearing focus outlines, lost button hover states, and unreadable text over images by using system colors and solid outlines.
- Regularly test with both DevTools emulation and real OS high-contrast settings, and incorporate automated contrast checks for ongoing compliance.
Table of Contents
- What forced colors and related CSS features actually do
- Core patterns for detecting forced colors and using system keywords
- Fixing buttons, links, focus states, and text over images
- Testing and debugging your forced-colors implementation
- Why forced colors is not a shortcut around WCAG
- What AccessWiser has learned from scanning for contrast issues
- How AccessWiser supports ongoing contrast accessibility
- Where to go deeper on forced colors and contrast
- Sources
- FAQ
What forced colors and related CSS features actually do
Forced colors mode is a setting, usually triggered by a Windows high-contrast theme, that tells the browser to override author-defined colors with a limited, user-chosen palette. The @media (forced-colors: active) media feature lets you detect this state and write targeted styles for it, rather than guessing at what the browser has already changed.
This is different from prefers-contrast, which signals that a person wants more (or less) contrast without forcing a specific palette. The two work through what MDN describes as a broader color adjustment model, alongside prefers-color-scheme, that governs how the browser negotiates color choices between your styles and the user’s settings.
Support for forced colors is strong across Chromium and Firefox-based browsers, and OS-level high-contrast themes propagate automatically once enabled. You don’t need to build a toggle for it. The browser handles activation. Your job is making sure your interface still works once it happens.

Core patterns for detecting forced colors and using system keywords
Once you know forced colors is active, replace author colors with system keywords that map to whatever palette the user has chosen. A minimal pattern looks like this:
@media (forced-colors: active) {
.card {
background-color: Canvas;
color: CanvasText;
border: 1px solid CanvasText;
}
}
The system color keywords documented in MDN’s system-color reference and in the W3C CSS Color Module exist in matched pairs, and pairing them correctly is what keeps text legible:
- Canvas / CanvasText: the default page background and its readable foreground.
- ButtonFace / ButtonText: background and text for button-like controls.
- LinkText: the color the browser assigns to unvisited links under forced colors.
- GrayText: reserved for disabled or de-emphasized content, and often rendered with lower contrast by design.
- Highlight / HighlightText: background and text for selected or active states.
Mix these correctly, meaning background and foreground from the same pair, and you inherit a palette that’s already built for legibility.
forced-color-adjust is the escape hatch, and it deserves restraint. Setting forced-color-adjust: none tells the browser to leave your original colors alone even when forced colors is active, which is useful for logos, brand marks, or data visualizations where meaning depends on specific hues. Reach for it only when the user-agent override actually breaks something. For everything else, let the browser’s adjustments do their job. Overusing forced-color-adjust: none quietly defeats the entire feature for the people who turned it on.
Fixing buttons, links, focus states, and text over images
Certain components break in predictable ways once forced colors kicks in, mostly because they lean on visual tricks that forced colors mode strips out.
- Buttons: box-shadow-based hover and active states often disappear entirely; rebuild them with borders and
ButtonFace/ButtonTextpairings so state changes stay visible. - Links: use
LinkTextfor unvisited links and, where it matters to your design,VisitedTextfor visited ones, rather than hardcoded blues and purples. - Focus indicators: a WebAIM discussion thread notes that box-shadow focus rings vanish under forced colors, so solid
outlinestyles that map to system tokens are safer; set a transparent base outline so the system reveals its own when forced colors is active. - Text over images: add a solid backplate behind text rather than depending on the browser to suppress the background image, since forced colors doesn’t remove images the way it removes decorative color.
Gradients, drop shadows, and background-image-only buttons are the recurring failure points. If a component’s meaning depends on a gradient or a shadow alone, give it a border or text fallback too.
Pro Tip: Test your focus ring first. If keyboard focus disappears under forced colors, everything else you fix afterward matters less.
Testing and debugging your forced-colors implementation
A repeatable test pass catches most regressions before they reach production.
- Open Chrome DevTools, go to the Rendering tab, and emulate
forced-colorsandprefers-contrastdirectly, a workflow Chrome’s own documentation walks through in detail. - Enable a Windows high-contrast theme manually at least once per release cycle; emulation is fast, but real OS behavior occasionally surfaces quirks that DevTools won’t, and mobile support for forced colors remains inconsistent.
- Add automated color-contrast checks to your pipeline, and treat forced-colors states as their own QA checklist item rather than an afterthought.
- Layer in scheduled scanning, the kind AccessWiser’s accessibility solutions run on a recurring basis, so contrast regressions introduced by a later commit get caught instead of shipping quietly.
Manual and automated checks catch different things. Automated scans flag code-level contrast failures at scale; a person clicking through with a high-contrast theme enabled catches the visual breakage that only shows up in context.
Why forced colors is not a shortcut around WCAG
Forced colors mode is a user preference, not a compliance strategy. It only activates for people who’ve turned it on, so your default theme still needs to meet WCAG contrast requirements on its own. A W3C presentation on forced colors puts it plainly: treat forced colors as an enhancement layered on an already-accessible base, not as a patch for one that isn’t.
Semantic color tokens make this easier to sustain. Microsoft’s Fluent UI guidance recommends mapping tokens like “surface” or “primary-text” to system colors under forced colors, rather than hardcoding hex values throughout a codebase. A design system built this way, the kind Summit Studio’s brand guide frames around reusable tokens, adapts to forced colors with far less custom override code.
A short checklist keeps this manageable:
- Confirm baseline contrast meets WCAG AA before touching forced-colors styles.
- Fix only the components forced colors actually breaks, not everything by default.
- Consider an optional on-page contrast switch for readers who want more control without changing OS settings.
What AccessWiser has learned from scanning for contrast issues
Color and contrast failures show up constantly in AccessWiser’s scans, and they rarely come from missing effort. They come from components built before forced colors was a consideration. Pairing detection with plain-language, code-level fix guidance turns a one-time scan into something a development team can actually act on. Scheduled re-checks catch the regressions that creep in after a redesign or a new component ships, which is where most contrast issues quietly return.
— The AccessWiser Team
How AccessWiser supports ongoing contrast accessibility
Building forced-colors support into your components is one piece of a larger accessibility effort, and keeping it consistent across a growing site is where most teams lose ground. AccessWiser scans against WCAG 2.2 AA success criteria, with mappings to Section 508 and EN 301 549, and ties each finding to the specific element and rule it affects.
What that looks like in practice:
- Code-level detection of contrast and color issues, tied to the exact element involved.
- Plain-language remediation guidance written for your own codebase, not a generic checklist.
- Scheduled re-checks that catch regressions after future updates.
- Dated records of scans, fixes, and an accessibility statement for documentation purposes.
- An optional visitor-facing widget offering adjustable contrast, themes, and text settings for people who want direct control.
Plans start with Starter at $24 per month, with Basic, Pro, and Business tiers available as needs grow, all detailed on the pricing page. Explore the full solutions overview or start a trial to see how scanning fits your workflow.
Where to go deeper on forced colors and contrast
For implementation details, see MDN’s forced-colors reference, the system color keyword docs, Chrome DevTools emulation guidance, and quick fixes from Forge Web Studio’s contrast guide.
Sources
- @media (forced-colors: active) - MDN Web Docs
- Emulate CSS media feature
forced-colors| Chrome DevTools - CSS Color Module Level 4 - W3C
FAQ
How do I turn on high contrast mode?
High contrast mode is enabled through your operating system’s accessibility settings rather than through an individual website. On Windows, it’s found under Ease of Access or Accessibility settings, and once turned on, it applies across supporting browsers automatically.
Why might someone use high contrast settings in a browser?
People with low vision often rely on high contrast settings to make text and interface boundaries easier to distinguish. WebAIM’s low-vision survey found that about 30.6% of respondents use high contrast mode or settings, making it a meaningfully common accommodation rather than an edge case.
How do I turn on high contrast mode in Chrome?
Chrome itself doesn’t have a separate high-contrast toggle; it follows the high-contrast theme set at the operating system level. Developers who want to preview the effect without changing OS settings can emulate it directly through the Rendering tab in Chrome DevTools.
What does high contrast mode do?
High contrast mode overrides a website’s author-defined colors with a limited, user-chosen palette designed for maximum legibility. Developers can detect this state with @media (forced-colors: active) and use CSS system color keywords like Canvas and CanvasText to keep components readable under the override, as described in MDN’s system-color documentation.
Recommended
Articles on this blog are general information about web accessibility, not legal advice. Laws change and their application depends on your specific situation — for decisions with legal consequences, consult a qualified legal professional.
Articles on this blog are general information about web accessibility, not legal advice. Laws change and their application depends on your specific situation — for decisions with legal consequences, consult a qualified legal professional.