# 13 · Accessibility Every token pair in the system, scored against WCAG 2.x at build time. Four combinations fail; all four are named here with the substitute to use instead. *Updated 2026-08-17. Computed at build time from the live tokens.* ## Summary 86 pairs measured. 46 pass AA body, 52 pass AA large, 29 pass AAA. ## Known failures These are in the palette as shipped. They are documented rather than removed, because a rule nobody knows they are breaking is not a rule. ### Stone is captioned as text and cannot be text - Claim: The Figma colour sheet labels Stone #AA9F8D "Muted text", and it has been used that way in decks and in early product screens. - Finding: Stone fails AA at every size, on every surface in the system. It is not a marginal miss. On Bone, the warm surface it most often appears over, it is less than half the required ratio. It stays in the palette as a decorative colour: rules, fills, illustration, texture. Never a glyph. - Measured: Stone on Near-white 100 — Stone `#aa9f8d` on Near-white 100 `#fcfcfc` = **2.54:1** (threshold 4.5:1) - Measured: Stone on Bone — Stone `#aa9f8d` on Bone `#ebe2d5` = **2.03:1** (threshold 4.5:1) - Measured: Stone on Sage — Stone `#aa9f8d` on Sage `#e5e5dd` = **2.06:1** (threshold 4.5:1) - Use instead: Near-white 700 `#4d4d4d` on Near-white 100 `#fcfcfc` = **8.24:1** - Still failing: yes ### White on Orange 500 fails AA body - Claim: White text on an orange button is the default instinct, and it is what most of the early mockups do. - Finding: It misses AA body by more than a point. There are two fixes and both are in the token set already: keep the Orange 500 fill and set the label in Near-black, or keep the white label and step the fill to Orange 600. The first is the house style, because --on-accent resolves to Near-black 900 for exactly this reason. - Measured: Near-white 100 on Orange 500 — Near-white 100 `#fcfcfc` on Orange 500 `#ff3c00` = **3.47:1** (threshold 4.5:1) - Use instead: Near-black 900 `#050505` on Orange 500 `#ff3c00` = **5.72:1** - Still failing: yes ### Orange 500 is not a text colour on light surfaces - Claim: The accent is the brand, so it gets reached for as a link colour and as a heading colour. - Finding: As text on Near-white it lands in the same place as white-on-orange: enough to be seen, not enough to be read. Orange 700 carries the same accent identity at more than twice the ratio, and is what --accent-text resolves to. Orange 500 remains correct as a fill, a rule, or a mark. - Measured: Orange 500 on Near-white 100 — Orange 500 `#ff3c00` on Near-white 100 `#fcfcfc` = **3.47:1** (threshold 4.5:1) - Use instead: Orange 700 `#8f2200` on Near-white 100 `#fcfcfc` = **8.54:1** - Still failing: yes ### Silver misses the 3:1 non-text threshold - Claim: Silver #AFAFAF is specified as the border colour, and --border resolved to it. - Finding: Borders and focus rings are held to 3:1 under WCAG 2.x §1.4.11, and Silver does not reach it on Near-white. This is the failure that audits miss, because contrast tooling looks at text. Silver stays as a decorative rule; anything a person has to locate (an input boundary, a focus ring) uses a darker step. - Measured: Silver on Near-white 100 — Silver `#afafaf` on Near-white 100 `#fcfcfc` = **2.14:1** (threshold 3:1) - Use instead: Near-white 500 `#8c8c8c` on Near-white 100 `#fcfcfc` = **3.28:1** - Still failing: yes ## Non-text contrast Borders, focus rings and other interactive boundaries, held to 3:1 under WCAG 2.x 1.4.11. | Role | Foreground | Background | Ratio | Verdict | | --- | --- | --- | --- | --- | | Input border · Silver on Near-white 100 | Silver `#afafaf` | Near-white 100 `#fcfcfc` | 2.14:1 | Fails. Silver is a decorative rule only. Interactive borders use Near-white 500 or darker. | | Substitute · Near-white 500 on Near-white 100 | Near-white 500 `#8c8c8c` | Near-white 100 `#fcfcfc` | 3.28:1 | Passes the 3:1 non-text threshold. Use this for input borders and any boundary a person has to find. | | Divider · Mist on Near-white 100 | Mist `#d4d4d4` | Near-white 100 `#fcfcfc` | 1.44:1 | Below 3:1, and that is acceptable: a divider is decoration, not a control boundary. | | Focus ring · Orange 600 on Near-white 100 | Orange 600 `#c22e00` | Near-white 100 `#fcfcfc` | 5.56:1 | Passes. This is what --focus-ring resolves to in the light theme. | | Focus ring · Orange 500 on Near-white 100 | Orange 500 `#ff3c00` | Near-white 100 `#fcfcfc` | 3.47:1 | Passes 3:1 by a margin thin enough to disappear on a poor display. Orange 600 is the specified ring. | | Focus ring · Orange 300 on Near-black 900 | Orange 300 `#ffb199` | Near-black 900 `#050505` | 11.65:1 | Passes. This is what --focus-ring resolves to in the dark theme. | | Border · Near-black 700 on Near-black 900 | Near-black 700 `#3b3b3b` | Near-black 900 `#050505` | 1.82:1 | Fails 3:1. Dark-theme control borders need Near-black 500 or lighter. | | Substitute · Near-black 500 on Near-black 900 | Near-black 500 `#787878` | Near-black 900 `#050505` | 4.62:1 | Passes. Use for interactive boundaries in the dark theme. | ## Full matrix ### Light theme Every ink the system offers, against every surface it can land on. Ratios are measured against Near-white 100, the actual value of --surface, not against pure white. | Ink | Surface | Ratio | AA body | AA large | AAA | | --- | --- | --- | --- | --- | --- | | Near-black 900 `#050505` | Near-white 100 `#fcfcfc` | 19.87:1 | pass | pass | pass | | Near-black 900 `#050505` | Cloud `#eaeaea` | 16.94:1 | pass | pass | pass | | Near-black 900 `#050505` | Sage `#e5e5dd` | 16.09:1 | pass | pass | pass | | Near-black 900 `#050505` | Bone `#ebe2d5` | 15.89:1 | pass | pass | pass | | Near-black 900 `#050505` | Orange 500 `#ff3c00` | 5.72:1 | pass | pass | fail | | Near-black 900 `#050505` | Orange 600 `#c22e00` | 3.58:1 | FAIL | pass | fail | | Umber `#674e42` | Near-white 100 `#fcfcfc` | 7.46:1 | pass | pass | pass | | Umber `#674e42` | Cloud `#eaeaea` | 6.36:1 | pass | pass | fail | | Umber `#674e42` | Sage `#e5e5dd` | 6.04:1 | pass | pass | fail | | Umber `#674e42` | Bone `#ebe2d5` | 5.96:1 | pass | pass | fail | | Umber `#674e42` | Near-black 900 `#050505` | 2.66:1 | FAIL | FAIL | fail | | Umber `#674e42` | Orange 500 `#ff3c00` | 2.15:1 | FAIL | FAIL | fail | | Umber `#674e42` | Orange 600 `#c22e00` | 1.34:1 | FAIL | FAIL | fail | | Near-white 700 `#4d4d4d` | Near-white 100 `#fcfcfc` | 8.24:1 | pass | pass | pass | | Near-white 700 `#4d4d4d` | Cloud `#eaeaea` | 7.03:1 | pass | pass | pass | | Near-white 700 `#4d4d4d` | Sage `#e5e5dd` | 6.68:1 | pass | pass | fail | | Near-white 700 `#4d4d4d` | Bone `#ebe2d5` | 6.59:1 | pass | pass | fail | | Near-white 700 `#4d4d4d` | Near-black 900 `#050505` | 2.41:1 | FAIL | FAIL | fail | | Near-white 700 `#4d4d4d` | Orange 500 `#ff3c00` | 2.37:1 | FAIL | FAIL | fail | | Near-white 700 `#4d4d4d` | Orange 600 `#c22e00` | 1.48:1 | FAIL | FAIL | fail | | Stone `#aa9f8d` | Near-white 100 `#fcfcfc` | 2.54:1 | FAIL | FAIL | fail | | Stone `#aa9f8d` | Cloud `#eaeaea` | 2.17:1 | FAIL | FAIL | fail | | Stone `#aa9f8d` | Sage `#e5e5dd` | 2.06:1 | FAIL | FAIL | fail | | Stone `#aa9f8d` | Bone `#ebe2d5` | 2.03:1 | FAIL | FAIL | fail | | Stone `#aa9f8d` | Near-black 900 `#050505` | 7.82:1 | pass | pass | pass | | Stone `#aa9f8d` | Orange 500 `#ff3c00` | 1.37:1 | FAIL | FAIL | fail | | Stone `#aa9f8d` | Orange 600 `#c22e00` | 2.19:1 | FAIL | FAIL | fail | | Orange 500 `#ff3c00` | Near-white 100 `#fcfcfc` | 3.47:1 | FAIL | pass | fail | | Orange 500 `#ff3c00` | Cloud `#eaeaea` | 2.96:1 | FAIL | FAIL | fail | | Orange 500 `#ff3c00` | Sage `#e5e5dd` | 2.81:1 | FAIL | FAIL | fail | | Orange 500 `#ff3c00` | Bone `#ebe2d5` | 2.78:1 | FAIL | FAIL | fail | | Orange 500 `#ff3c00` | Near-black 900 `#050505` | 5.72:1 | pass | pass | fail | | Orange 500 `#ff3c00` | Orange 600 `#c22e00` | 1.60:1 | FAIL | FAIL | fail | | Orange 600 `#c22e00` | Near-white 100 `#fcfcfc` | 5.56:1 | pass | pass | fail | | Orange 600 `#c22e00` | Cloud `#eaeaea` | 4.74:1 | pass | pass | fail | | Orange 600 `#c22e00` | Sage `#e5e5dd` | 4.50:1 | pass | pass | fail | | Orange 600 `#c22e00` | Bone `#ebe2d5` | 4.44:1 | FAIL | pass | fail | | Orange 600 `#c22e00` | Near-black 900 `#050505` | 3.58:1 | FAIL | pass | fail | | Orange 600 `#c22e00` | Orange 500 `#ff3c00` | 1.60:1 | FAIL | FAIL | fail | | Orange 700 `#8f2200` | Near-white 100 `#fcfcfc` | 8.54:1 | pass | pass | pass | | Orange 700 `#8f2200` | Cloud `#eaeaea` | 7.28:1 | pass | pass | pass | | Orange 700 `#8f2200` | Sage `#e5e5dd` | 6.92:1 | pass | pass | fail | | Orange 700 `#8f2200` | Bone `#ebe2d5` | 6.83:1 | pass | pass | fail | | Orange 700 `#8f2200` | Near-black 900 `#050505` | 2.33:1 | FAIL | FAIL | fail | | Orange 700 `#8f2200` | Orange 500 `#ff3c00` | 2.46:1 | FAIL | FAIL | fail | | Orange 700 `#8f2200` | Orange 600 `#c22e00` | 1.54:1 | FAIL | FAIL | fail | | Near-white 100 `#fcfcfc` | Cloud `#eaeaea` | 1.17:1 | FAIL | FAIL | fail | | Near-white 100 `#fcfcfc` | Sage `#e5e5dd` | 1.23:1 | FAIL | FAIL | fail | | Near-white 100 `#fcfcfc` | Bone `#ebe2d5` | 1.25:1 | FAIL | FAIL | fail | | Near-white 100 `#fcfcfc` | Near-black 900 `#050505` | 19.87:1 | pass | pass | pass | | Near-white 100 `#fcfcfc` | Orange 500 `#ff3c00` | 3.47:1 | FAIL | pass | fail | | Near-white 100 `#fcfcfc` | Orange 600 `#c22e00` | 5.56:1 | pass | pass | fail | ### Dark theme The derived dark token set. The accent moves from Orange 500 to Orange 400 here, which is the whole reason the dark theme needed its own audit rather than an assumption. | Ink | Surface | Ratio | AA body | AA large | AAA | | --- | --- | --- | --- | --- | --- | | Near-white 100 `#fcfcfc` | Near-black 900 `#050505` | 19.87:1 | pass | pass | pass | | Near-white 100 `#fcfcfc` | Near-white 900 `#121212` | 18.26:1 | pass | pass | pass | | Near-white 100 `#fcfcfc` | Near-black 800 `#212121` | 15.69:1 | pass | pass | pass | | Near-white 100 `#fcfcfc` | Espresso `#3b2f28` | 12.61:1 | pass | pass | pass | | Near-white 100 `#fcfcfc` | Orange 500 `#ff3c00` | 3.47:1 | FAIL | pass | fail | | Near-white 100 `#fcfcfc` | Orange 400 `#ff774d` | 2.56:1 | FAIL | FAIL | fail | | Near-white 300 `#cccccc` | Near-black 900 `#050505` | 12.69:1 | pass | pass | pass | | Near-white 300 `#cccccc` | Near-white 900 `#121212` | 11.67:1 | pass | pass | pass | | Near-white 300 `#cccccc` | Near-black 800 `#212121` | 10.03:1 | pass | pass | pass | | Near-white 300 `#cccccc` | Espresso `#3b2f28` | 8.06:1 | pass | pass | pass | | Near-white 300 `#cccccc` | Orange 500 `#ff3c00` | 2.22:1 | FAIL | FAIL | fail | | Near-white 300 `#cccccc` | Orange 400 `#ff774d` | 1.63:1 | FAIL | FAIL | fail | | Near-white 400 `#adadad` | Near-black 900 `#050505` | 9.08:1 | pass | pass | pass | | Near-white 400 `#adadad` | Near-white 900 `#121212` | 8.35:1 | pass | pass | pass | | Near-white 400 `#adadad` | Near-black 800 `#212121` | 7.18:1 | pass | pass | pass | | Near-white 400 `#adadad` | Espresso `#3b2f28` | 5.76:1 | pass | pass | fail | | Near-white 400 `#adadad` | Orange 500 `#ff3c00` | 1.59:1 | FAIL | FAIL | fail | | Near-white 400 `#adadad` | Orange 400 `#ff774d` | 1.17:1 | FAIL | FAIL | fail | | Orange 400 `#ff774d` | Near-black 900 `#050505` | 7.76:1 | pass | pass | pass | | Orange 400 `#ff774d` | Near-white 900 `#121212` | 7.13:1 | pass | pass | pass | | Orange 400 `#ff774d` | Near-black 800 `#212121` | 6.13:1 | pass | pass | fail | | Orange 400 `#ff774d` | Espresso `#3b2f28` | 4.93:1 | pass | pass | fail | | Orange 400 `#ff774d` | Orange 500 `#ff3c00` | 1.36:1 | FAIL | FAIL | fail | | Orange 300 `#ffb199` | Near-black 900 `#050505` | 11.65:1 | pass | pass | pass | | Orange 300 `#ffb199` | Near-white 900 `#121212` | 10.71:1 | pass | pass | pass | | Orange 300 `#ffb199` | Near-black 800 `#212121` | 9.20:1 | pass | pass | pass | | Orange 300 `#ffb199` | Espresso `#3b2f28` | 7.39:1 | pass | pass | pass | | Orange 300 `#ffb199` | Orange 500 `#ff3c00` | 2.03:1 | FAIL | FAIL | fail | | Orange 300 `#ffb199` | Orange 400 `#ff774d` | 1.50:1 | FAIL | FAIL | fail | | Near-black 900 `#050505` | Near-white 900 `#121212` | 1.09:1 | FAIL | FAIL | fail | | Near-black 900 `#050505` | Near-black 800 `#212121` | 1.27:1 | FAIL | FAIL | fail | | Near-black 900 `#050505` | Espresso `#3b2f28` | 1.58:1 | FAIL | FAIL | fail | | Near-black 900 `#050505` | Orange 500 `#ff3c00` | 5.72:1 | pass | pass | fail | | Near-black 900 `#050505` | Orange 400 `#ff774d` | 7.76:1 | pass | pass | pass |