Accessibility · Perception

Forced Colors Mode and the System Color Keywords

How forced colors replaces author colors with a user-chosen palette, which properties survive, and the design habits that break when backgrounds are overridden but borders are not.

An operating system high-contrast theme and the CSS forced-colors mode are the same mechanism viewed from two sides. The user picks a palette — a foreground, a background, a link color, a highlight — and the browser substitutes those values for the author’s wherever it can, so that a page which was designed around a light neutral background is rendered in the user’s black-on-yellow or white-on-black instead. The author’s colors are not adjusted; they are replaced. Everything the design did with color has to survive that replacement, and much of it does not.

What is replaced and what is not

The substitution is property-specific rather than a filter over the final image. Background colors, border colors, outline colors, text colors, and text decoration colors are among the properties forced to values drawn from the user’s palette, and shadows are removed rather than recolored. Properties the forcing does not cover remain as authored: opacity, the pixels of images, and the presentation attributes of some embedded content.

This asymmetry is the source of nearly every defect. A design that distinguishes a card from the page by a subtle background tint loses that distinction entirely, because both become the same forced background. A design that distinguishes it by a 1 px border keeps the border but the border becomes the forced foreground color, which is by construction high contrast against the forced background — so the card survives, and survives better than it did before. The rule of thumb that follows is that structure expressed as borders and text survives, and structure expressed as fill does not.

Opacity is the trap in the other direction. It is not forced, so a 20 percent white overlay used to lighten a background becomes 20 percent of whatever the forced background is. On a light forced palette the overlay is nearly invisible; on a dark one it is a barely visible lightening. Either way the visual hierarchy it encoded is gone, and the element it was lightening may now be indistinguishable from its neighbors.

Why backgrounds are forced but images are not

The forcing of backgrounds is what makes the mode useful: a page cannot defeat a user’s palette by painting a large opaque rectangle behind everything. But images are not forced, because forcing them would require either discarding them or remapping their pixels, and neither is acceptable. The result is that a page can be mostly forced and still contain bright photographic regions.

Text over such a region is a known failure. The forced foreground may be white and the image may be bright, and no author rule can fix it because the author’s scrim color has been replaced. The available remedies are structural: place the text on a solid forced background by giving it a background color (which becomes the forced background), or use a border to delimit a panel. Both work because they convert the text’s local backdrop into something the palette controls.

forced-color-adjust: none opts an element out of the substitution entirely, and it exists for the small set of cases where the author’s colors carry information that the palette cannot express — a color swatch picker, a chart legend where color is the data, a syntax-highlighting theme where the token colors are the content. It is an escape hatch with a real cost: the opted-out region is now the author’s responsibility, and it must remain legible against whatever palette the user has chosen, which the author cannot see. Using it for a whole page is a way of ignoring the user’s setting while appearing to support it.

System color keywords as the intended fix

The system color keywords are the mechanism for writing rules that participate in the user’s palette instead of fighting it. Canvas is the background, CanvasText the foreground, LinkText and VisitedText for links, Highlight and HighlightText for selection, ButtonFace and ButtonText for controls, and GrayText for disabled content. These keywords resolve to the user’s chosen values in forced-colors mode and to reasonable defaults otherwise.

Using them converts a hard-coded design into one that follows the palette by construction. A selected row styled with background: Highlight; color: HighlightText is legible in every palette because the pair is chosen by the same authority that chose the palette. A selected row styled with a pale blue tint is legible in the default palette and invisible in the forced one, since the tint is replaced by the plain background and the only remaining signal is gone.

.selected {
  background: Highlight;
  color: HighlightText;
}

@media (forced-colors: active) {
  /* A tinted card loses its edge; a border keeps it. */
  .card {
    border: 1px solid CanvasText;
  }
  /* Decorative gradients vanish; keep a visible boundary. */
  .hero {
    border-bottom: 1px solid CanvasText;
  }
}

The media query is the second half of the technique. Rules inside @media (forced-colors: active) apply only when the substitution is on, so an author can add the borders and outlines that the tinted design did not need, without changing the default rendering. The query is a capability test, not a preference test: it reports that the user agent is forcing colors, not which palette is in use.

Controls and the appearance interaction

Native form controls are painted by the platform, and in forced-colors mode they are typically drawn in the palette’s own style. An author who has replaced a native checkbox with a div and a background image has removed the control from the platform’s painting, and the background image is not forced. The result is an invisible or unreadable checkbox in exactly the mode where visibility matters most, and the same substitution that removes a fill also removes the boundary that non-text contrast requires.

The usual repair is to give the custom control a border and to draw the check mark with a border-based shape or with a text character, both of which are forced. A check mark made from two borders of a rotated box is a well-known technique for this reason: the shape is made of properties that follow the palette. A check mark made from a background image of a tick is not.

Disabled states have a related problem. The forced palette provides GrayText for disabled content, and a design that expresses disabledness by lowering opacity to 40 percent ends up with a control whose text is forced to the normal foreground color and then faded — which can put it below any reasonable contrast threshold against the forced background. Using GrayText directly expresses the same state in the palette’s own vocabulary.

Testing the mode honestly

The mode can be toggled in a browser’s rendering settings, and the useful test is to look at the page with it on and with at least two different palettes. A single palette hides the defects that depend on polarity: a border that is invisible against a light forced background is fine against a dark one, and the page that only gets tested on dark looks correct.

Automated checks generally do not evaluate the forced state at all; they compute against the declared colors. That gap means a page can be fully conformant and completely unusable in the mode, which is one of the concrete arguments for manual checking alongside automated tools. The check itself is visual and quick: enable the mode, scan for regions that have lost their boundaries, and look specifically at anything that was distinguished by a fill rather than a stroke.

The same reasoning extends to other user-driven overrides. A user stylesheet that raises the base font size interacts with fixed-height containers; a dark-mode preference interacts with images that carry baked-in light backgrounds. Forced colors is the most complete override and therefore the best stress test: if a layout survives having all its colors replaced, it is probably relying on structure rather than on paint, and structure is what every other override also preserves.

References

  1. CSS Color Adjustment Module Level 1 — W3C
  2. CSS Color Module Level 4 — System Colors — W3C
  3. Web Content Accessibility Guidelines (WCAG) 2.2 — W3C