Colour
Thirty-five colours in two themes. Each swatch shows light and dark side by side, and its contrast against paper in the light theme.
Text
ink-subtle is the lightest text the system allows anywhere, and it still clears 4.5:1. There is no colour below it, deliberately: grey-on-grey metadata is how a news page stops looking authoritative.
--p234-color-ink
Body and headline text. Warm near-black, not pure black, so long serif copy does not vibrate.
--p234-color-ink-muted
Bylines, timestamps, source names, captions. Must stay 4.5:1 on paper and paper-alt.
--p234-color-ink-subtle
The lightest text allowed anywhere. Still passes 4.5:1. Below this, use ink-muted.
--p234-color-ink-inverse
Text on paper-dark and on brand.
Surfaces
paper-alt is a warm newsprint tint, deliberately subtle. This is not a cream site. paper-sunk is for things that recede: data tables, quote blocks, the ad frame.
--p234-color-paper
Page background.
--p234-color-paper-alt
Warm newsprint tint for section bands and pull panels. Subtle on purpose: this is not a cream site.
--p234-color-paper-sunk
Recessed blocks: data tables, quote blocks, the ad slot frame.
--p234-color-paper-dark
Footer, masthead inverse, section dividers on dark.
Rules
An editorial page separates with rules, not with cards and shadows. rule does the layout work; rule-strong is for section rules and form borders and meets the 3:1 non-text ratio.
--p234-color-rule
The hairline that does the layout work. Editorial pages separate with rules, not shadows or cards.
--p234-color-rule-strong
Section rules and form control borders. Non-text, needs 3:1.
Brand
The identity green, carried over unchanged at #417742. It clears 4.5:1 on paper at 5.33:1, which is what lets it be a link and a kicker rather than decoration. A test pins the value: if it drifts, the masthead, kickers and links all need rechecking, not just this token.
--p234-color-brand
Plus234Feed green. Kickers, links, the masthead plus. Carried over from the existing identity.
--p234-color-brand-hover
--p234-color-on-brand
Text on a brand background. Build verifies 4.5:1.
--p234-color-brand-tint
Selected rows, followed-topic chips, the live band.
Links and focus
Visited links are not optional on an archive this size. A reader who has been through forty visa articles should be able to see which ones.
The focus ring is two colours: focus with focus-outer drawn around it. Neither half clears 3:1 alone, which is the point of the pattern. The amber is 1.64:1 on white and relies on the dark outer ring there; on a dark page the outer ring recedes and the amber carries it. The build checks that at least one half clears 3:1 on every surface. See Focus.
--p234-color-link
--p234-color-link-hover
--p234-color-link-visited
Real news readers revisit. Visited state is not optional on an archive this size.
--p234-color-focus
Focus ring. Always drawn with focus-outer behind it so it survives any background.
--p234-color-focus-outer
The ring drawn outside the focus colour. Dark on light pages, so the amber reads; in dark it recedes and the amber does the work alone.
Sections
Five section colours, and they are kicker text and section rules only. Never a background, never a badge fill. Five coloured blocks on one page is what makes a news site look like a dashboard. Each one is checked against both paper and the newsprint band.
--p234-color-section-politics
Kicker text and section rule only. Never a background.
--p234-color-section-business
--p234-color-section-markets
--p234-color-section-world
--p234-color-section-sport
Data
Direction is never carried by colour alone: the delta component prepends an arrow glyph. data-up was darkened from the first draft because it missed 4.5:1 inside a sunk table by 0.17.
--p234-color-data-up
Gainers. Paired with an arrow glyph, never colour alone. Dark enough to clear 4.5:1 inside a sunk data table, which the first draft missed.
--p234-color-data-down
Decliners. Paired with an arrow glyph, never colour alone.
--p234-color-data-flat
Status
--p234-color-success
--p234-color-success-tint
--p234-color-warning
--p234-color-warning-tint
--p234-color-error
--p234-color-error-tint
--p234-color-info
--p234-color-info-tint
How the gate works
Forty-four pairs are declared in scripts/tokens.mjs as [foreground, background, ratio, why], and every pair is checked in every theme. The build exits non-zero on failure and names the pair, the measured ratio and the reason it exists.
light: color.data-up on color.paper-sunk is 4.33:1, needs 4.5:1 (gainers inside a data table)
dark: color.ink-inverse on color.paper-dark is 1.05:1, needs 4.5:1 (footer text)
Those are two of the three failures from the first run. The second was a semantic error, not a shade problem: in dark the footer is still a dark surface, so text on it has to stay light, and text on the brand colour belongs to on-brand instead.