Accessibility
The floor, not the ceiling:
- Every interactive target is at least 24×24px (WCAG 2.2 SC 2.5.8), and the templates hold to it with none left over. Check the width too — icon-only buttons are square. Where the visual is deliberately smaller — the 20px switch track, the 12px slider thumb, an 11px text link — the templates keep the visual size and grow the hit area with padding plus an equal negative margin, so the target reaches 24 without the row getting taller.
--min-targetis the value they all reference; change it in one place and every one of them follows. - Verify contrast per pairing, never assume it. Recompute the key pairings after a reskin, including the worst case of small text on the control fill.
text-tertiarysits below AA by design, so it never carries a value, a message, or a label a decision rests on. - Keyboard reaches everything a mouse reaches, in visible order. Dialogs trap focus while open and return it to the trigger on close. A hand-built listbox owes the contract a native
<select>gives away; see Components. [hidden]needs restoring. Any component that setsdisplay: flexorgridoverrides the browser default[hidden] { display: none }. Ship[hidden] { display: none !important }in the base stylesheet — a collapsed panel leaking into view is usually this.- Meaning never rides on colour, shape or motion alone. A red border needs a message; a spinner needs text; a selected thumbnail needs its ring, not just a tint.
Key pairings, measured
| Pairing | Measured | Threshold | |
|---|---|---|---|
text-primary on white | 17.90:1 | 4.5:1 | pass |
text-primary on bg-subtlest | 17.46:1 | 4.5:1 | pass |
text-primary on bg-control | 15.56:1 | 4.5:1 | pass |
text-secondary on white | 10.68:1 | 4.5:1 | pass |
text-secondary on bg-control | 9.28:1 | 4.5:1 | pass |
White on brand-default | 14.35:1 | 4.5:1 | pass |
White on bg-inverse (the dark dialog header) | 17.90:1 | 4.5:1 | pass |
stroke-surround (the focus ring) on white | 4.76:1 | 3:1 | pass |
red-default as error text on white | 4.36:1 | 4.5:1 | borderline |
text-tertiary on white | 3.60:1 | 4.5:1 | known exception |
text-tertiary on bg-control | 3.13:1 | 4.5:1 | known exception |
blue-default as link and info text | 3.79:1 | 4.5:1 | known exception |
White on emphasize-default | 3.89:1 | 4.5:1 | known exception |
text-disabled on brand-disabled | 1.57:1 | — | not meant to be read |
The two steps that carry meaning — text-primary and text-secondary — clear the line on every surface in the system. The four rows marked as known exceptions are values the token export gives, not defects: they are listed again below with the reasoning. Recompute this whole table when you reskin.
Known and deliberately accepted deviations
A specification should record its own debts rather than pretend it has none:
| Item | Measured | Threshold | Reasoning |
|---|---|---|---|
text-tertiary as helper text | 3.60:1 | 4.5:1 (1.4.3) | The value the token export gives it. Tertiary therefore carries only text a reader can afford to miss — never a value, a message, or a label a decision rests on |
blue-default as link and info text | 3.79:1 | 4.5:1 (1.4.3) | The export's value. Links keep their underline, so colour is not the only signal |
White on emphasize-default | 3.89:1 | 4.5:1 (1.4.3) | The export's value for the platform red. It appears at most once per screen |
text-disabled on brand-disabled | 1.57:1 | — | A disabled control is deliberately unreadable; the state is also carried by the cursor and by aria-disabled |
stroke-default as a control's only boundary | 1.43:1 | 3:1 (1.4.11) | Controls also differ from their surface by fill; the border is not the only cue |
| Grey control fill on a white card | 1.15:1 | 3:1 (1.4.11) | The whole panel is built on this recess; focus adds a 1px stroke-active border, and the label says it is a field |
The first four come from the token export and are accepted as they are — the way to honour them is to keep the affected text out of load-bearing roles, not to nudge the values. The last two are the source of the overall feel: darkening every border to 3:1 visibly heavies every edge, and giving a control a 3:1 boundary at rest brings back the bordered box the grey fill replaced.