Tokens

The whole vocabulary a design can draw on. A value that is not here does not exist in the app, and reaching one costs a hand-written rule that then has no dark mode and no responsive behaviour of its own — so design on these steps rather than near them.

Colour

Name the token, never the hex. Each one carries a light and a dark value, so a design that says --color-heading-2 gets both modes and survives a client re-theme, where one that says #0D1420 gets neither. Every swatch below is shown twice — the same token under each colour scheme.

Surfaces

An index page is a grey ground with white cards on it; a reading view is a white sheet.

  • --color-app-backgroundlight-dark(#ffffff, #1B2331)
  • --color-page-groundlight-dark(#F6F7F9, #111826)
  • --color-surface-25light-dark(#FAFBFC, #1B2331)
  • --color-surface-50light-dark(#F6F7F9, #1B2331)
  • --color-block-outlinelight-dark(#E3E7ED, #333C4B)
  • --color-block-outline-activelight-dark(#E9ECF1, #3C4657)

Primary

  • --color-primarylight-dark(#1F5FE0, #9CC2FF)
  • --color-primary-hoverlight-dark(#1749BC, #B9CDF6)
  • --color-primary-activelight-dark(#1743A8, #DCE7FA)
  • --color-primary-contrastlight-dark(#ffffff, #111826)
  • --color-primary-tintlight-dark(#F2F6FE, color-mix(in srgb, #9CC2FF, transparent 88%))
  • --color-primary-tint-activelight-dark(#F2F6FE, color-mix(in srgb, #9CC2FF, transparent 84%))
  • --color-primary-tint-stronglight-dark(#DCE7FA, #1743A8)
  • --color-primary-outlinelight-dark(#DCE7FA, #1743A8)
  • --color-primary-selectedlight-dark(#133A8F, #F2F6FE)

Error

  • --color-errorlight-dark(#ef4444, #f87171)
  • --color-error-hoverlight-dark(#dc2626, #fca5a5)
  • --color-error-activelight-dark(#b91c1c, #fecaca)
  • --color-error-tintlight-dark(#fef2f2, color-mix(in srgb, #f87171, transparent 96%))
  • --color-error-tint-activelight-dark(#fee2e2, color-mix(in srgb, #f87171, transparent 84%))
  • --color-error-outlinelight-dark(#fecaca, #b91c1c)

Success

  • --color-successlight-dark(#22c55e, #4ade80)
  • --color-success-hoverlight-dark(#16a34a, #86efac)
  • --color-success-activelight-dark(#15803d, #bbf7d0)
  • --color-success-tintlight-dark(#f0fdf4, color-mix(in srgb, #4ade80, transparent 96%))
  • --color-success-tint-activelight-dark(#dcfce7, color-mix(in srgb, #4ade80, transparent 84%))
  • --color-success-outlinelight-dark(#bbf7d0, #15803d)

Controls

focus-ring is ours to apply rather than the design's to specify, but it needs 2px of clearance.

  • --color-button-secondarylight-dark(#F2F4F8, #1B2331)
  • --color-button-secondary-hoverlight-dark(#E9ECF1, #2A3444)
  • --color-button-secondary-activelight-dark(#DCE0E7, #3C4657)
  • --color-input-backgroundlight-dark(#ffffff, #0B111C)
  • --color-input-outlinelight-dark(#DCE0E7, #3C4657)
  • --color-input-outline-stronglight-dark(#C6CCD6, #5A6474)
  • --color-focus-ringlight-dark(#1F5FE0, #9CC2FF)
  • --color-focus-ring-washcolor-mix(in srgb, #1F5FE0 13%, transparent)

Contrast

An inverted surface -- a dark bar on a light page, and the reverse in dark mode.

  • --color-contrastlight-dark(black, white)
  • --color-contrast-filllight-dark(#0D1420, #ffffff)
  • --color-contrast-fill-hoverlight-dark(#111826, #E3E7ED)
  • --color-contrast-fill-activelight-dark(#2B3341, #C6CCD6)
  • --color-on-contrastlight-dark(#ffffff, #0B111C)

Text roles

These have no utility class behind them — an implementer writes text-(--color-heading-2). Pick by role rather than by darkness: heading-1 is the page title, heading-5 a caption.

  • Sample textSample text--color-heading-1
  • Sample textSample text--color-heading-2
  • Sample textSample text--color-heading-3
  • Sample textSample text--color-heading-4
  • Sample textSample text--color-heading-5
  • Sample textSample text--color-subtitle
  • Sample textSample text--color-action
  • Sample textSample text--color-filter-option
  • Sample textSample text--color-date
  • Sample textSample text--color-text-default
  • Sample textSample text--color-text-hover

Data-driven colour

Shown as they render rather than as a palette, because these are not a palette: the colour is picked by the content's own name, never by a design, so there is nothing here to choose. Three things follow that do matter. Please don't borrow one as a decorative accent — it would read as a topic to anyone who knows the app. A new topic or tag needs its token added before it can appear, since the lookup has no fallback and an unknown name renders with no colour at all. And two pairs are deliberately identical — diversity and language share a ramp — so a design that needs them to be distinguishable needs to say so.

Topics — 14

populationhouseholdshousingincomeeducationworkforceindustryeconomyimmigrationdiversitylanguagemobilityaffordabilitylocation
populationhouseholdshousingincomeeducationworkforceindustryeconomyimmigrationdiversitylanguagemobilityaffordabilitylocation

Tags — 3

Newly addedPopularRecently updated
Newly addedPopularRecently updated

Notification types — 5, a single colour each rather than a pair. These are theme tokens, unlike the topics and tags above, so they are reached by a utility class instead.

data-update new-feature report story system-update
data-update new-feature report story system-update

Version badge — one fixed pair, the only member of this group not keyed to content. It sits in :root like the topics, but because its name never varies an implementer writes it out: bg-(--color-version-bg) text-(--color-version-text).

v1.18.0
v1.18.0

Type

Ask for a step, not a pixel value. Size, weight and line height travel together under one name, so a request for 13.5px does not land between two steps — it lands outside the system.

  • The quick brown fox text-action 14px · 400 · normal
  • The quick brown fox text-action-sm 13.5px · 600 · 1.2
  • The quick brown fox text-action-strong 14.5px · 600 · 1.2
  • The quick brown fox text-badge 11px · 600
  • The quick brown fox text-body-sm 13.5px · 400 · 1.45
  • The quick brown fox text-body-xs 13px · 400 · 1.5
  • The quick brown fox text-caption 11.5px · 400 · 1.35
  • The quick brown fox text-code 11px · 400 · 1.6
  • The quick brown fox text-count-badge 11.5px · 700
  • The quick brown fox text-date 12.5px · 400 · 1.45
  • The quick brown fox text-display 30px · 700 · 1.15
  • The quick brown fox text-display-sm 25px · 700 · 1.2
  • The quick brown fox text-ext-chip 10.5px · 700
  • The quick brown fox text-eyebrow 11.5px · 700
  • The quick brown fox text-filter-option 14px · 500 · 1.3
  • The quick brown fox text-heading-1 24.5px · 700 · normal
  • The quick brown fox text-heading-2 20px · 700 · normal
  • The quick brown fox text-heading-3 17.5px · 700 · normal
  • The quick brown fox text-heading-4 14px · 600 · normal
  • The quick brown fox text-heading-5 12.5px · 700 · normal
  • The quick brown fox text-subtitle 14.5px · 400 · 1.5
  • The quick brown fox text-table-header 12px · 700
  • The quick brown fox text-topic 11.5px · 600 · normal

Radius

A 19px radius is not available. On a control that short the intent is almost always a full pill, which is a step — rounded-full.

  • rounded-panel 10px
  • rounded-sheet 22px
  • rounded-smTailwind
  • rounded-mdTailwind
  • rounded-lgTailwind
  • rounded-xlTailwind
  • rounded-2xlTailwind
  • rounded-fullTailwind

Spacing

Layout spacing is Tailwind's 4px scale: any multiple of four is free, anything between two steps is not. The named tokens below are the ones a page layout depends on — the gutter a full-bleed row has to reach back through, and the width a card grid has to divide.

  • --spacing-block-outline 1px
  • --spacing-horizontal-page-padding 36px
  • --spacing-horizontal-page-padding-mobile calc(var(--spacing-horizontal-page-padding) / 2)
  • --spacing-page-width 1240px
  • --spacing-topic-x 14px
  • --spacing-topic-y 4px
  • --spacing-topics 12px