Forced colors
Forced Colors Mode — Windows High Contrast — replaces author colours with the user's own palette and strips box-shadow outright. That second part is what makes it more than a colour question here: every ring in this library is a box-shadow, composed in a slot so that a ring and a drop shadow can share the property, and it sits on top of outline-none. Left alone, a focused control would have no indicator at all.
If you are only using the components, there is nothing to do — see the guarantee in Accessibility. This page is for writing components against this stylesheet.
What is already covered
The three focus utilities — focus-ring, focus-field-ring, invalid-field-ring — draw an outline back in Highlight under forced-colors: active. So any component that goes through status-focused, status-focused-field or status-invalid-field is covered without a line of its own. status-disabled picks up GrayText the same way.
Selection carried only by a background
The override flattens a background-color into its surroundings. Where selection is just a background — a tag, a calendar day, a table row — restate the state in system colours:
.thing[data-selected="true"] {
background-color: var(--rp-accent);
color: var(--rp-accent-foreground);
@media (forced-colors: active) {
forced-color-adjust: none;
background-color: Highlight;
border-color: Highlight;
color: HighlightText;
}
}All four lines earn their place. Highlight and HighlightText are the pair the platform reserves for a selected control; border-color joins them because a border left in an author colour is repainted to the user's text colour and reads as an edge you never drew. The one that looks least necessary is forced-color-adjust — see the backplate.
Where selection also moves a thumb or shows a glyph, the component writes its own forced-colors block, because those parts need colours of their own.
Use the system keywords
Highlight / HighlightText for a selected control, CanvasText on Canvas for ordinary content, ButtonBorder for a control's edge, GrayText for disabled. They are the only colours exempt from the override — a token resolves to an author colour and is thrown away.
Two traps
transparent used as a spacer
Forced colors preserves a transparent background, but a transparent border-color is turned opaque. A border held open purely to reserve layout space stops being invisible and renders as a solid block. Restate those as Canvas inside the media query.
The backplate, which getComputedStyle cannot see
Chromium paints a Canvas-coloured backplate behind the text of any element that has text, so text over an image stays legible. It lands on top of that element's own background — so a Highlight fill carrying HighlightText renders as a solid plate with the label invisible inside it. Every colour is correct and the component is unreadable.
forced-color-adjust: none suppresses the backplate. Anything pairing a Highlight fill with HighlightText needs it, including the case where the fill sits on a different element than the text.
Only alongside explicit system colours
On its own, forced-color-adjust: none just hands the author's palette back — which is the opposite of the point.
Structure has to survive too
A component that is only a tinted fill and a shadow — a card, an alert, a text field, a chip — renders as loose text once both are taken, which reads as no component at all.
So every container carries an inset CanvasText outline under the mode and every control a ButtonBorder one, on the variants that actually paint. A transparent surface is left alone, since framing it would invent a box that was never there. A separator is the exception that proves it: the whole thing is its background, so it takes a colour rather than an edge.
Looking at it by hand
Chromium's rendering panel — DevTools, Cmd+Shift+P, "Show Rendering", then Emulate CSS media feature forced-colors.
macOS has no Forced Colors Mode at all
"Increase contrast" maps to prefers-contrast: more, so switching it on tests nothing.
There is deliberately no toolbar control for it in the workbench. data-reduce-motion is an attribute a decorator can set, but this is a browser-level media feature an iframe cannot turn on for itself — and faking it with a stylesheet of system colours would miss the box-shadow removal, the part that actually breaks.