+234 Design System

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
#1a1a18/#f2f0ea17.43:1

Body and headline text. Warm near-black, not pure black, so long serif copy does not vibrate.

--p234-color-ink-muted
#5b584f/#b0aca27.11:1

Bylines, timestamps, source names, captions. Must stay 4.5:1 on paper and paper-alt.

--p234-color-ink-subtle
#6e6b62/#9b978d5.33:1

The lightest text allowed anywhere. Still passes 4.5:1. Below this, use ink-muted.

--p234-color-ink-inverse
#ffffff/#f2f0ea1.00:1

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
#ffffff/#1414131.00:1

Page background.

--p234-color-paper-alt
#faf8f4/#1c1c1a1.06:1

Warm newsprint tint for section bands and pull panels. Subtle on purpose: this is not a cream site.

--p234-color-paper-sunk
#f1eee7/#2424211.16:1

Recessed blocks: data tables, quote blocks, the ad slot frame.

--p234-color-paper-dark
#1a1a18/#0d0d0c17.43:1

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
#e3dfd6/#32312d1.33:1

The hairline that does the layout work. Editorial pages separate with rules, not shadows or cards.

--p234-color-rule-strong
#8c887e/#6b685f3.54:1

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
#417742/#8cc48c5.33:1

Plus234Feed green. Kickers, links, the masthead plus. Carried over from the existing identity.

--p234-color-brand-hover
#356136/#a4d2a47.21:1
--p234-color-on-brand
#ffffff/#1414131.00:1

Text on a brand background. Build verifies 4.5:1.

--p234-color-brand-tint
#eaf1e8/#1e2a1e1.15:1

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
#417742/#8cc48c5.33:1
--p234-color-link-hover
#356136/#a4d2a47.21:1
--p234-color-link-visited
#5b2a86/#cbb0ea9.90:1

Real news readers revisit. Visited state is not optional on an archive this size.

--p234-color-focus
#ffbf47/#ffbf471.64:1

Focus ring. Always drawn with focus-outer behind it so it survives any background.

--p234-color-focus-outer
#1a1a18/#0d0d0c17.43:1

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
#9b2c2c/#eb94947.53:1

Kicker text and section rule only. Never a background.

--p234-color-section-business
#1f4e79/#8fbce88.66:1
--p234-color-section-markets
#8a5700/#e5bc6a6.10:1
--p234-color-section-world
#5b2a86/#c9a9ee9.90:1
--p234-color-section-sport
#0f6e5c/#63c9b06.17:1

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
#15683b/#5cc1846.83:1

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
#b4232a/#ef82886.53:1

Decliners. Paired with an arrow glyph, never colour alone.

--p234-color-data-flat
#5b584f/#b0aca27.11:1

Status

--p234-color-success
#00703c/#5cc1846.21:1
--p234-color-success-tint
#e6f2ec/#15251b1.15:1
--p234-color-warning
#8a5700/#e5bc6a6.10:1
--p234-color-warning-tint
#fdf3e1/#2720151.10:1
--p234-color-error
#b00020/#ef82887.33:1
--p234-color-error-tint
#fbe9ec/#2a17191.17:1
--p234-color-info
#1d70b8/#8fbce85.17:1
--p234-color-info-tint
#e8f1f8/#151f281.14:1

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.