CSS forced-colors in Windows High Contrast Mode

The CSS forced-colors media feature detects whether a user agent has enforced a restricted, user-chosen color palette across the interface, as seen in Windows High Contrast Mode (now called Contrast Themes in Windows 11). This article explains how the forced-colors media query works, why it is critical for web accessibility, how the browser modifies standard CSS styles under this mode, and how developers can write targeted CSS rules to ensure custom user interfaces remain functional and legible.

What is the forced-colors Media Feature?

The forced-colors media query is defined in the CSS Media Queries Level 5 specification. It evaluates whether the operating system or browser is forcing a limited color palette onto the page to improve visual contrast and readability.

The query accepts two values:

  • none: The operating system has not imposed a restricted color palette. Web pages render with their standard authored styles.
  • active: A restricted color palette is enforced by the operating system or browser. Author-defined colors are overridden by system-level color choices.
@media (forced-colors: active) {
  /* High contrast specific styles go here */
}

How Windows High Contrast Mode Interacts with the Browser

When a user enables a Contrast Theme in Windows, the browser enters forced colors mode. To guarantee minimum contrast ratios for people with low vision or photophobia, the browser automatically strips or overrides specific CSS properties:

  • color and background-color are mapped to system values (such as Canvas and CanvasText).
  • border-color is normalized to the system foreground color.
  • box-shadow and text-shadow are completely removed.
  • fill and stroke on SVGs may be overridden with system text colors.
  • Background images set via background-image are often hidden unless they are explicitly defined as CSS gradients or content images.

While these automated overrides make plain text readable, they can inadvertently break custom interactive components where visual boundaries or indicators rely on shadows, background colors, or subtle color contrasts.

Common Use Cases for forced-colors: active

Developers use the @media (forced-colors: active) block to address interface elements that lose meaning or visibility when the browser overrides colors.

1. Restoring Lost Visual Boundaries

Modern designs frequently use box-shadow instead of borders for cards, modals, or form controls. Because forced colors mode disables shadows, these elements blend into the background. Developers can use forced-colors: active to apply explicit borders:

.card {
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

@media (forced-colors: active) {
  .card {
    border: 1px solid CanvasText;
  }
}

2. Ensuring Transparent Borders for Focus States

A common pattern is using transparent borders on elements in default states so that adding a border on :focus or :hover does not cause layout shifting. In forced colors mode, transparent borders (border: 1px solid transparent) are rendered as solid system-colored lines. To prevent unexpected visible borders, use border-color: transparent carefully or ensure they render properly using system keywords.

3. Preserving SVG Icons

Inline SVGs using custom fills can disappear if the icon's background and fill both resolve to the same system color. By scoping SVG styles inside a forced-colors: active block, you can map fills directly to CSS system keywords:

@media (forced-colors: active) {
  .icon {
    fill: LinkText;
  }
}

Working with CSS System Colors

When adapting styles inside forced-colors: active, hardcoded hex or RGB colors should not be used. Hardcoded values defeat the purpose of the user's custom high-contrast settings (e.g., white-on-black vs. yellow-on-black). Instead, use standard CSS system color keywords that adapt dynamically to the user's selected theme:

  • Canvas: Background of the application or document.
  • CanvasText: Main text color on the canvas.
  • LinkText: Text color for unvisited hyperlinks.
  • Highlight: Background color for selected items or text.
  • HighlightText: Text color for selected items.
  • ButtonFace: Background color for buttons.
  • ButtonText: Text color for buttons.
  • Mark: Background color for highlighted text (such as the <mark> tag).

Opting Out with forced-color-adjust

In rare scenarios, an element must retain its original authored colors even when forced colors mode is active. Examples include color pickers, brand identity swatches, or photographic image filters. The forced-color-adjust property allows developers to exempt specific elements from browser overrides:

.color-swatch-red {
  background-color: #e63946;
  forced-color-adjust: none;
}

Setting forced-color-adjust: none prevents the browser from changing the element's colors. This property should be used sparingly and only on elements where the preservation of true color is essential to the functionality of the interface.