← The specification

Brand accessibility

Rendered from spec at76d46f3 (2026-10-25) · its source · Edit this page

Status: Accepted

The contrast table below is generated from tokens.json by scripts/gen_contrast.py — edit the tokens, not the table. just generated rewrites it and refuses a hand edit. The prose around it is written by hand.

Whether the palette meets WCAG AA is a computed property of the tokens, not an opinion. This page states the baseline, with the actual ratios, and the pairings that are unsafe.

Consistent with P3: the contrast is measured, and re-measured by a CI check (ARCH-R40), not assumed.


The measured baseline

Every number below is computed from tokens.json by gen_contrast.py, and the integrity job fails if the page and the tokens disagree. Nobody types a ratio here, because the page’s own premise is that a ratio is not an opinion — and because the numbers that were typed here went wrong three times over without anyone noticing.

AA needs 4.5:1 for body text and 3:1 for large text; AAA needs 7:1. The three surfaces are the grounds anything is ever set on: paper is the default, canvas the darker page behind cards, ink the dark theme.

Token Hex On paper On canvas On ink
ink #17160F 16.92 · AAA 14.67 · AAA —
ink-soft #241F14 15.29 · AAA 13.26 · AAA 1.11 · fails
lemon #F0C419 1.55 · fails 1.35 · fails 10.89 · AAA
lemon-bright #FFD84D 1.29 · fails 1.12 · fails 13.11 · AAA
fiber #E07A17 2.81 · fails 2.44 · fails 6.01 · AA
fiber-deep #9C5411 5.31 · AA 4.61 · AA 3.19 · AA large
fiber-light #F09A3C 2.09 · fails 1.81 · fails 8.11 · AAA
leaf #5B6B2A 5.47 · AA 4.74 · AA 3.09 · AA large
paper #FBF7EA — 1.15 · fails 16.92 · AAA
pith #FBF6E7 1.01 · fails 1.14 · fails 16.78 · AAA
canvas #EDE7D5 1.15 · fails — 14.67 · AAA
line #DAD2BC 1.41 · fails 1.22 · fails 12.02 · AAA
line-soft #E4DCC7 1.28 · fails 1.11 · fails 13.26 · AAA
text-muted #565344 7.21 · AAA 6.26 · AA 2.35 · fails
text-faint #6A6756 5.31 · AA 4.61 · AA 3.18 · AA large
ok #55701E 5.26 · AA 4.56 · AA 3.21 · AA large
alarm #A82717 6.60 · AA 5.73 · AA 2.56 · fails
warn-tint #FBEBD8 1.09 · fails 1.06 · fails 15.52 · AAA
alarm-tint #F7E2DD 1.16 · fails 1.01 · fails 14.57 · AAA

tokens.json here is a distribution copy. The brand maintains it, and shared/assets.sha256 holds this copy byte-identical to it — so a token changing in the brand reaches this page by the copy being refreshed, and the table follows on the next run.

What the measurements decide

The body-safe set is whatever the table says it is. A pairing at AA or better may carry body text; one below it may not, and this page keeps no list beside the table. That is the whole of DES-R15: a list is a second statement of the table, and a second statement is the half that goes stale.

Both surfaces are measured, not just the default. A pairing is about a foreground and a ground, and the same token can clear AA on paper and fail on canvas, which is darker. The table carries a column for each so the question cannot be answered for the wrong one.

Amber is never text on a light ground, and the rule and the meter agree. fiber and fiber-light both fail against paper and canvas, which is the accessibility reason behind the brand rule that amber is signal-only. They clear AA against ink, and that is not a loophole — it is the same finding read from the other side, and it is why the dark theme lightens amber rather than keeping the light-theme value. Amber is fine as a non-text accent anywhere — a fibre core, a focus ring, an active underline — where contrast rules for text do not apply.

text-faint is restrained by emphasis, not by contrast. It clears AA for body text against paper and canvas both. It is still not body copy: it is the lowest-emphasis token in the palette, for eyebrow labels and secondary type, and information a reader needs does not go there. That is a typographic decision rather than a measured one, and saying so is better than implying a meter forbids it.

The dark theme is the comfortable one. The On ink column is generous throughout, which is why the ink theme lightens amber to fiber-light (token contract) — on a dark surface the lighter amber is legible where the same lightening on paper would fail. The theme switch is a contrast decision, not just a mood one.

The contract this creates

The design-token contract requires every body-text pairing to meet AA. Two checks, in two repositories, and neither is a person reading a table: brand:scripts/check_tokens.py fails a token change under which ink, ink-soft, text-muted, leaf or fiber-deep falls below AA on paper, and the integrity job here refuses a page that has drifted from the tokens. The brand check reads that list of five and paper alone; canvas, the ink theme and text-faint are measured in the table above and by nothing in CI.

A new or changed token that would put a failing pairing into body use is a contract violation, not a design preference.

Beyond contrast

Contrast is the measurable part; the web accessibility requirements still apply on top — focus visibility, keyboard operability, reduced-motion, text alternatives for the logo. The brand doesn’t override G3; it must satisfy it.

The logo in particular carries a text alternative (alt="lemonfiber"), and the ink/paper theme honours prefers-color-scheme and the explicit toggle (G3-R5, the token theme attribute).

Requirements

ID Requirement
DES-R15 Body text MUST use only token pairings meeting WCAG AA (4.5:1); the body-safe set MUST be computed from the tokens rather than listed by hand.
DES-R16 Every surface a token may sit on MUST be measured, not only the default one.
DES-R17 text-faint MUST NOT carry information a reader needs; it is the lowest-emphasis token, restrained by emphasis rather than by contrast.
DES-R18 fiber and fiber-light MUST NOT be used as text.
DES-R19 A token change producing a failing body pairing MUST be treated as a contract violation.
DES-R20 The logo MUST carry a text alternative in the web UI.