Hero image for The palette the platform overrides — forced colors voids your contrast math (2026)

The palette the platform overrides — forced colors voids your contrast math (2026)

A test: hypothesis, method, results, conclusion — design-agent.dev, 2026-10-01.

Hypothesis: forced-colors substitution voids contrast math

Forced colors voids an agent’s normal-mode contrast result when the operating system replaces applicable author colors with user-chosen system colors, as specified by MDN’s forced-colors reference and the CSS Color Adjustment specification. In the fetched framework files, 16/19 contain no forced-colors block; measured author-color contrast cannot establish the substituted palette’s contrast.

A contrast calculation is meaningful only for the colors it measures. In forced-colors mode the browser can override author-specified foreground and background colors with system colors such as Canvas, CanvasText, and LinkText, and it removes box-shadows and background images. An agent that reports contrast from the ordinary stylesheet has measured a palette that may never appear in this state.

This differs from the unrendered interaction state in the hover post: there, the agent misses a state because it does not trigger it. Here the mechanism is substitution — the declarations are present, but the platform replaces their colors. The claim is not that normal-mode contrast math is generally useless; it is that it cannot certify the colors after substitution.

What changes for agents: from theme selection to counterpart coverage

Forced colors is substitution, not theme selection: the dark-mode post’s mechanism is selection, while this state replaces applicable author colors with user-chosen system colors, as described by MDN’s forced-color-adjust reference. The agent-visible question therefore shifts from “which theme resolved?” to “does each author color have a forced-colors counterpart?”

In ordinary theme analysis an agent can see which theme is active and compare its rendered values. Forced colors changes the target: a declaration may be present in the fetched CSS and still not control the displayed color. The relevant evidence is whether the stylesheet supplies intentional replacements for that state, and whether information encoded in backgrounds or shadows survives. A page can hold strong normal-mode contrast and still offer no author-defined forced-colors handling — that is a limit on the evidence, not proof that the product lacks support.

Method: a static fetch of server-sent HTML and CSS

The audit is a literal count of fetched text, not a rendered-browser result: plain HTTP GET requests collected 19 framework files, 14 site homepages, and 31 linked CSS files, about 64 requests in total, with the measurement limits documented through the arena. Every count is a regex count over those responses.

This post is desk research plus a static fetch of server-sent HTML and CSS — no rendering, no JavaScript, no forced-colors emulation, no pointer input and no assistive-technology testing — and every number is reproducible from the published measurement script. Files were requested with a browser User-Agent and bounded byte caps; all requests returned 200 except MVP.css (404) and adobe.com (read timeout), both dropped. For live sites the fetch preferred same-origin linked stylesheets, used cross-origin stylesheets as a fallback, read up to 3 CSS files per site, and scanned homepage HTML for inline matches. This describes what a no-JavaScript fetch can see; it does not establish what a browser renders after scripts execute.

Framework result: 16 of 19 files ship zero forced-colors blocks

The base-file count is 16/19 with zero literal @media (forced-colors: active) blocks, or 84.2%; 3/19, or 15.8%, contain at least one, according to the fetched USWDS, GOV.UK Frontend and Primer CSS sources. This measures CSS text, not the quality or completeness of any product’s accessibility.

Source Fetched set forced-colors blocks forced-color-adjust declarations
USWDS 3.8 base file 113 2 (auto ×1, none ×1)
GOV.UK Frontend 5.6 base file 8 (includes legacy -ms-high-contrast: active) 5 (auto ×3, none ×2)
Primer CSS 21 base file 4 0
Bootstrap 5.3, Bootstrap 4.6, Bulma 1.0, Foundation 6.8, Materialize 1.0, Pure 3.0, Spectre 0.5, Milligram 1.4, Pico 2, Water 2, Open Props 1.7, Sakura, Simple.css 2, New.css 1.1, Tailwind 3.4 preflight, Tailwind 4.0 base files 0 0
USWDS site homepage + up to 3 CSS 101 2
Carbon homepage + up to 3 CSS 20 8
GOV.UK Design System homepage + up to 3 CSS 13 6
Primer homepage + up to 3 CSS 7 0
shadcn/ui homepage + up to 3 CSS 4 0
Fluent 2 homepage + up to 3 CSS 2 (file named accessibility.*.css) 0
MUI, Polaris, Atlassian Design, Chakra UI, Radix UI, MDN, WebAIM, NN/g homepages + up to 3 CSS 0 0

A match count needs context. Foundation 6.8, Materialize 1.0, and Pure 3.0 each contain ButtonText exactly once, but each occurrence is in the classic :-moz-focusring { outline: 1px dotted ButtonText; } normalize rule outside any forced-colors block. Those tokens therefore do not indicate forced-colors handling.

Live-site result: 6 of 14 carry handling, with a shell-only caveat

The live-site fetch found forced-colors blocks in 6/14 homepages and their linked CSS, or 43%; 8/14, or 57%, had none in that server-sent material, including the MUI, Chakra UI, Radix UI, Atlassian Design and Polaris sites. These counts describe fetched CSS, not complete product support.

The six nonzero sources were USWDS, Carbon, GOV.UK Design System, Primer, shadcn/ui, and Fluent 2; the remaining fetched set was MUI, Polaris, Atlassian Design, Chakra UI, Radix UI, MDN, WebAIM, and NN/g. Atlassian’s two fetched files were font-rules only. MUI, Chakra, Radix, Atlassian and Polaris ship component styles through CSS-in-JS or runtime injection on their docs sites, so the CSS a no-JavaScript fetch can see is a shell; their components may still emit forced-colors rules at runtime. The honest claim is “zero forced-colors blocks in server-sent CSS”, never “no support in the product”.

Edge cases: forced-color-adjust none, and background or shadow signals

Two details affect how an agent should interpret palette substitutions: forced-color-adjust: none preserves author choices rather than supplying a counterpart, while background images and box-shadows can disappear, as explained by MDN’s forced-color-adjust reference. Neither detail makes normal-mode contrast a reliable proxy.

In the base files, forced-color-adjust appears in 2/19: USWDS has 2 declarations, GOV.UK Frontend has 5, and Primer has 0. A none value opts out of the platform’s forced-color adjustments for that element, and the agent cannot infer that the author’s chosen colors will contrast with an unknown user-selected palette. Treat forced-color-adjust: none as an uncomputable exception, not as coverage.

Background-only and shadow-only signals need separate attention: a status, focus cue, or boundary communicated only through background-color or box-shadow may not survive substitution, exactly as a form audit must treat a field’s name and machine purpose as independent channels. The removal of shadows parallels the missing-state concern in the hover analysis, but the risk here is that a visual signal is removed, or that its color is replaced rather than merely withheld.

Computable rules: PVC and SIC

Two computable rules can turn source inspection into an audit: PVC counts color counterparts within forced-colors blocks, and SIC counts background and shadow declarations with replacements for the same selector. Their formulas can be applied to stylesheet text, using MDN’s system-color reference to identify system-color keywords; low values mean audit risk, never site quality.

PVC (Palette-Void Coverage) = (distinct CSS system-color keywords inside @media (forced-colors: active) blocks) / (distinct author color tokens in normal-mode color declarations). Low PVC means most of the audited palette has no forced-colors counterpart and will be replaced wholesale; it is audit risk, never site quality.

SIC (Substitution Information Coverage) = (distinct normal-mode declarations using background-color or box-shadow that have a replacement declaration for the same selector inside a @media (forced-colors: active) block) / (distinct normal-mode declarations using background-color or box-shadow). Low SIC means background/shadow state signals are likely dropped; it is audit risk, never site quality.

These are coverage measures, not contrast scores: the denominator records what an agent must account for, the numerator the explicit counterparts. The base-file system-color counts show that the numerator is measurable — USWDS has 7 distinct system colors, GOV.UK Frontend 1, Primer 2 — but they are not PVC values on their own, because the normal-mode author-color-token denominator is also required.

FAQ

The findings answer questions about block counts, runtime CSS, and opt-outs without equating fetched source with a rendered product. The MDN forced-colors reference describes the state being counted; the answers below keep the measurement boundary explicit and distinguish evidence of declarations from proof of end-to-end behavior.

Doesn’t GOV.UK’s block count prove real forced-colors support?

It proves that the fetched GOV.UK Frontend 5.6 base file contains 8 matching blocks, including queries that pair -ms-high-contrast: active with forced-colors: active. That is evidence of authored handling in the file, not proof that every component state is covered or that the resulting interface is usable with every user palette.

Aren’t MUI, Chakra, Radix, Atlassian and Polaris false negatives, because their CSS is injected at runtime?

They are false negatives for a claim about all runtime CSS, but not for the narrower count reported here: zero forced-colors blocks in server-sent CSS. Their documentation sites use CSS-in-JS or runtime injection, so a no-JavaScript fetch may see only a shell. The count cannot establish whether their rendered components emit matching rules.

Does forced-color-adjust: none fix the contrast problem?

No. none keeps author colors from being adjusted, but those colors may not contrast with the user’s chosen system palette. It is an uncomputable exception, not a counterpart and not coverage. An agent should report the opt-out distinctly instead of counting it as evidence that the substitution state has been addressed.

Bottom line

A perfect light- and dark-mode audit can still be voided by forced-colors substitution, so agents should add PVC and SIC before reporting contrast confidence. The fetched base files show 84.2% with zero blocks and 15.8% with some handling; live homepages show 43% with any server-sent block, as the agent-visible resolution guide helps frame.

Those percentages measure the sampled text, not design quality. The practical conclusion is narrower: normal-mode contrast math cannot certify colors that the platform is about to replace. A report should state whether counterpart declarations and replacement signals were found, identify opt-outs, and avoid turning absence from a static fetch into a claim about a complete product.

How this guide was built

This guide combines desk research with a static fetch of server-sent HTML and CSS, and every number returns to the script that produced it. The fetch used plain HTTP GET with a browser User-Agent and bounded byte caps; it did not render pages, execute JavaScript, emulate forced colors, provide pointer input, or test with assistive technology, and all counts are literal regex counts over fetched text. The measurement covers about 64 HTTP GETs across 19 framework files, 14 homepages and 31 linked CSS files, all returning 200 except MVP.css (404) and adobe.com (read timeout), both dropped.

The scope matters most for runtime-injected styles and for user-selected palettes that were not emulated. The results describe what the server sent and what the measurement script matched — a repeatable source-coverage audit, not a claim of rendered accessibility or a judgment about any named design system.