Rubric: control state contrast
Written by a person. Last read by a person on 2026-09-08, 20 days ago. Its facts were checked by the eval suite on 2026-09-28.
Enforced by check_control_contrast.
The claim
Every state a control can be in is legible, in both themes and in every stylesheet.
Observable evidence
- The declarations each stylesheet sets on the reader-level chip.
- The custom properties those declarations resolve to, in the light block and the dark one.
- The specificity and order of the rules, which decide which declaration wins.
The pass bar
The chip's label and its fill are at least 4.5:1 apart in each of four states: pressed, pressed and hovered, pressed and focused, pressed and keyboard-focused. Both themes. Every stylesheet the contract lists, not only the one the site ships.
Why this rubric exists
A control has more colors than the one it rests in, and the rest of them are seen only by somebody who is using it. Nobody photographs a button mid-press, so those colors get no review at all.
This site shipped one. .js .level:focus painted the label in the accent color, and
.level[aria-pressed="true"] filled the chip with the same color. The first selector is more
specific, so it won. Clicking a chip focuses it. The label and the fill were then the same
color, a contrast ratio of 1.00, and the chip read as blank.
Three reviews looked at that page and passed it. Each one looked at a chip nobody had pressed.
Ambiguous cases
Reading the declarations, not the render. The check parses CSS instead of measuring a browser. That keeps it in the eval suite and lets it cover the three design directions as well as the shipped stylesheet, which is where a fix applied to one leaves the other three. It cannot see a color that arrives from an image, a gradient or a filter.
A selector the check cannot parse is a finding. It understands the shapes the chip rules use and refuses the rest of them. Skipping what it cannot read is how the faulty rule got past the three reviews that did look at the file.
4.5:1, not 3:1. The chip label is .78rem, which does not count as large text under the guideline, so it takes the higher of the two bars. Raising the label's size would lower the bar and would have to change this rubric with it.