All documents

Contrast, measured

Every ratio below was computed from the resolved token values (WCAG 2.1 relative luminance). Thresholds: 4.5:1 for normal text, 3:1 for large text (≥18.66px bold or ≥24px) and for the visual boundary of a control.

Light

InkSurfaceRatio
body #1C1C1E
cream #FAF8F5
16.05:1
AA
body #1C1C1E
card #F5F3EF
15.35:1
AA
body #1C1C1E
zebra #F1EEE8
14.69:1
AA
body #1C1C1E
row hover #DAECE2
13.84:1
AA
body #1C1C1E
row selected #DED8CE
12.01:1
AA
secondary #636366
card #F5F3EF
5.40:1
AA
secondary #636366
zebra #F1EEE8
5.17:1
AA
tertiary #8E8E93
card #F5F3EF
2.94:1
disabled / hint only
accent ink iq #036B4E
zebra #F1EEE8
5.64:1
AA
accent ink rei #036EBC
zebra #F1EEE8
4.58:1
AA
accent ink roof #B84A19
zebra #F1EEE8
4.50:1
AA
header ink #FAF8F5
header band #18181B
16.71:1
AA
destructive #A4231C
card #F5F3EF
6.68:1
AA
caution #8A5A00
card #F5F3EF
5.35:1
AA

Dark

InkSurfaceRatio
body #F5F3EF
charcoal #18181B
15.99:1
AA
body #F5F3EF
card #2C2C2E
12.58:1
AA
body #F5F3EF
zebra #29292B
13.10:1
AA
body #F5F3EF
row hover #293D38
10.42:1
AA
body #F5F3EF
row selected #434346
8.90:1
AA
secondary white 60%
card #2C2C2E
≈6.65:1
AA
tertiary white 40%
card #2C2C2E
≈4.04:1
large / labels only
accent ink iq #34D399
card #2C2C2E
7.25:1
AA
accent ink rei #0495FE
card #2C2C2E
4.47:1
AA-large
accent ink roof #FF6723
card #2C2C2E
4.79:1
AA
header ink #F5F3EF
header band #0F0F11
17.28:1
AA
destructive #FF5F57
card #2C2C2E
4.66:1
AA
caution #FEBC2E
card #2C2C2E
8.26:1
AA

Two known gaps. Do not "fix" them silently

1. White on the accent fill fails. The primary button paints --primary-foreground: #FFFFFF on the brand accent:

BrandWhite on accentInk #1C1C1E on accent
iq #10B981
2.54:1
6.71:1
rei #0495FE
3.12:1
5.46:1
roof #FF6723
2.91:1
5.85:1

The accents are mid-lightness, so white loses on all three and ink wins on all three. Switching --primary-foreground to the ink token fixes every brand at once and changes how every primary button looks — that is a brand-owner decision, not a developer one. It is recorded here with the numbers so the decision can be made rather than inherited. The kit ships the current values so a new app matches the live product.

Where tertiary is allowed, and where it is a bug. #8E8E93 is 3.08:1 on cream — the disabled tier, which WCAG exempts. It is correct for a glyph (a breadcrumb separator, a chevron), for placeholder text, and for a badge on a row that is itself disabled. It is WRONG for anything a reader has to read: a specimen's label, a finding like "Non-metro", a count, a field name. Four of those were caught and lifted on 2026-08-20 — each had been 3.08:1 and is now 5.65:1.

The test is one question: if this text went missing, would the reader lose something? If yes, it is secondary.

2. Tertiary ink is below 4.5:1 in light. #8E8E93 on card is 2.94:1. It is correct for genuinely disabled text and placeholder hints (which WCAG exempts) and wrong for anything a reader has to read. If a label matters, it is secondary, not tertiary.

The one thing the kit did change

--accent-ink — the accent as TEXT — was hard-coded to emerald in the source app for both themes. Correct for iq, and it painted green ink on a Roof or REI app, which breaks the system's central promise. In this kit it is per-brand, darkened in light mode until it clears 4.5:1 against the zebra row (the worst of the three light surfaces): iq #036B4E, rei #036EBC, roof #B84A19. Dark mode keeps the raw accent, which already clears it on titanium.

The guide's own display value (--brand-accent-text) is untouched and still used for large type, where 3:1 applies.

Rules that follow from the numbers

  • Never use --brand-accent as text. It fails in all three brands. Use --accent-ink.
  • Any new surface must keep body ink ≥4.5:1 and hold the row ladder's floor (≥8.9:1 was the measured worst case across the four row states).
  • A control's boundary needs 3:1 against what is behind it — that is why the select chevron is a full-opacity muted token and not opacity-50 on top of one.
  • Colour is never the only cue: selection also has an inset bar, an error also has a title and an icon, a soon row also has a badge.
  • Recompute rather than eyeball. The script that produced these numbers is twenty lines of luminance arithmetic; the ratios in this file were not estimated.