Skip to content

Editorial Ink has one accent (clay, terracotta) and three status hues (olive success, brick danger, ochre attention) on warm paper neutrals. App code never touches a hue directly: it picks a semantic token or a component variant, and the token flips between data-color-mode="light" and data-color-mode="dark" on <html>. Every pair listed here meets the WCAG 2.2 AA contrast minimum in both themes (SC 1.4.3: 4.5:1 for normal text, 3:1 for large text); any pairing not listed here is unverified and an audit should flag it.

Layer
Foundation
Rules
14 numbered
Examples
8 do and don’t
Updated
Source
color.md

Base palette

Neutral
  1. 50
  2. 100
  3. 200
  4. 300
  5. 400
  6. 500
  7. 600
  8. 650
  9. 700
  10. 800
  11. 900
  12. 950
  13. 1000
Clay accent
  1. 300
  2. 400
  3. 500
  4. 600
Olive success
  1. 300
  2. 400
  3. 500
  4. 600
Ochre attention
  1. 300
  2. 400
  3. 500
  4. 600
Brick danger
  1. 300
  2. 400
  3. 500
  4. 600

Status sets: tint and solid

  • AccentAccentaccent
  • SuccessSuccesssuccess
  • AttentionAttentionattention
  • DangerDangerdanger

A tint always takes its hairline and its text token. Ochre's solid takes dark text.

Surfaces nest inward

bgColor-inset
bgColor-default
bgColor-muted

Page, then card, then a recessed strip inside it.

Every swatch is a token value. Hover a chip for its CSS variable; the tints and surfaces follow the site theme.
  1. App code uses only semantic tokens: --fgColor-*, --bgColor-*, --borderColor-*. It never references a --base-color-* primitive (neutral, clay, olive, brick, ochre, white, black) and never writes a hex, rgb(), or hsl() literal for a colour.
  2. Text on surfaces: --fgColor-default and --fgColor-muted are the only body-text colours. They sit on --bgColor-default, --bgColor-muted, --bgColor-inset, or a soft --bgColor-<status>-muted tint. They are never placed on a solid -emphasis background.
  3. Status text (--fgColor-accent, --fgColor-success, --fgColor-danger, --fgColor-attention) sits on the three neutral surfaces or on its own soft tint (--bgColor-<status>-muted, matched by meaning). It is never placed on a solid -emphasis background.
  4. --fgColor-onEmphasis (white) is used only on --bgColor-accent-emphasis, --bgColor-success-emphasis, and --bgColor-danger-emphasis. It is never used on --bgColor-emphasis and never on --bgColor-attention-emphasis.
  5. The ochre attention solid takes dark text: text on --bgColor-attention-emphasis is always --fgColor-onAttention. White on ochre fails AA.
  6. Text on --bgColor-emphasis (the ink surface) is --bgColor-default, not --fgColor-onEmphasis. The two tokens are designed as inverses per theme, so the pair stays high-contrast in both modes. Tooltip owns this pattern; do not rebuild it.
  7. Soft tints come in pairs: a --bgColor-<status>-muted fill always takes its matching --borderColor-<status>-muted hairline and --fgColor-<status> text. Never compute a translucent fill (rgba(...), color-mix(...), or opacity on a status colour) to fake a tint.
  8. The accent appears once per surface as an action: exactly one Button or IconButton with variant="primary" per page, dialog, card, or toolbar. Link text and Tag variant="accent" (a selected or active token) are the only other accent uses.
  9. Status colours carry status meaning only: success means done, passing, or published; danger means failed, blocked, or destructive; attention means a warning or something that needs action. Categories, teams, and brands are never coloured with a status variant; use default or muted.
  10. Banner and Toast variants are chosen by meaning, not by the colour they produce: info (clay), success (olive), warning (ochre), danger (brick).
  11. Surfaces nest inward: the page is --bgColor-inset, a Card variant="default" on it is --bgColor-default, and a recessed strip inside that is Card variant="muted" (--bgColor-muted). A default surface is not placed on another default surface without a --borderColor-default edge.
  12. Borders: --borderColor-default for control outlines and card edges, --borderColor-muted for row and list separators. --borderColor-accent-emphasis is the focus and selected-state colour and is applied only by components.
  13. Colour is never the only signal. A status is a coloured word (Tag), a coloured message (FormControl.Validation, Banner), or an icon plus text, never a bare coloured dot with no name.
  14. Theming is not branched in JavaScript. Code never reads data-color-mode to pick a colour; it uses the token and lets the theme resolve it.

Pairs that pass AA for normal text in both themes. The machine-readable form is $extensions.tenet.contrast on each fgColor token in tokens/tokens.json (aa and aaLarge lists per theme); a pair must be in the aa list of both themes to appear here.

Foreground tokenAllowed backgrounds
--fgColor-default--bgColor-default, --bgColor-muted, --bgColor-inset, any --bgColor-<status>-muted tint
--fgColor-muted--bgColor-default, --bgColor-muted, --bgColor-inset, any --bgColor-<status>-muted tint
--fgColor-accentthe three neutral surfaces, --bgColor-accent-muted
--fgColor-successthe three neutral surfaces, --bgColor-success-muted
--fgColor-dangerthe three neutral surfaces, --bgColor-danger-muted
--fgColor-attentionthe three neutral surfaces, --bgColor-attention-muted
--fgColor-onEmphasis--bgColor-accent-emphasis, --bgColor-success-emphasis, --bgColor-danger-emphasis only
--fgColor-onAttention--bgColor-attention-emphasis only
--bgColor-default (as text)--bgColor-emphasis only (the inverse pair)

Do

<Text tone="muted" size="small">Updated 3 minutes ago</Text>

Secondary copy through the token, not a literal

<Tag variant="success">Published</Tag>

Status word gets the tint, hairline, and text tokens as a set

<Stack direction="horizontal" gap="condensed"><Button variant="invisible">Cancel</Button><Button variant="primary">Save changes</Button></Stack>

One accent action

Don’t

<span style={{ color: '#6e6049' }}>Updated</span>

A neutral.650 literal; breaks in dark mode

<span style={{ background: 'var(--bgColor-attention-emphasis)', color: 'var(--fgColor-onEmphasis)' }}>Pending</span>

White on ochre fails AA; the text token is --fgColor-onAttention

<div style={{ background: 'rgba(194, 80, 46, 0.12)' }}>

A computed tint; use --bgColor-accent-muted or Tag variant="accent"

<Button variant="primary">Save</Button><Button variant="primary">Publish</Button>

Two accents on one surface

<Tag variant="danger">Design</Tag>

A team name coloured as a failure