Design system

The system this site is made of.

Every value on saer.app comes from one token file. This page is generated from it, and Saer audits the result. Two colours carry meaning, one typeface carries the voice, and every element earns its place.

Dogfood

This site passes its own audit.

Saer 0.2.0-beta.3-rc.9 on this website's source, 30 October 2026. Accessibility checks are static: not a statement of WCAG conformance.

Colour and type
Colour tokens reconstructed37
References resolved134 of 134
Exact colour drift0
Near-matches0
Dead colour tokens0
Type: exact drift and off-scale sizes0
Foundations: exact drift and off-scale values
Spacing and sizing0
Radius0
Shadowsno scale declared
Z-index0
Breakpoints0
Border widths0
Opacity0
Motion0
Accessibility (static checks)
Failing contrast pairs, every context0
Passing contrast pairs18
Focus outline removed with no replacement0 of 134
Targets written below 24 px0
Motion with no reduced-motion fallback0
Terminal
saer scan packages/saer-web --output /tmp/saer-site

Shadows are not part of this system: depth comes from surfaces and rules, so Saer reports no shadow scale, and says so rather than inventing one.

Colour

Two colours that mean something.

Green marks what is verified, shipped, or the next action. Lavender marks inference, testing and intelligence. Charcoal and cream are the surfaces. Where a brand colour fails contrast, a darker or lighter variant is derived in OKLCH at the same hue, and tested.

  • --green-400#CCF676Kawasaki green. Brand. Means verified, shipped, go. A fill or a mark, never text on a light surface (1.13:1 on cream).
  • --green-700#4A6300Derived from green 400 in OKLCH (same hue, lower lightness) so green can carry text on light surfaces: 6.25:1 on cream, 5.20:1 on neutral.
  • --lavender-400#A99FECSoft lavender. Brand. Means inferred, in testing, intelligence. A fill or a mark on light surfaces; text on charcoal (7.79:1).
  • --lavender-700#5F4FB0Derived from lavender 400 in OKLCH for text on light surfaces: 5.95:1 on cream, 4.95:1 on neutral.
  • --charcoal-600#6A6F72Derived. Strong boundary on charcoal (3.63:1), for inputs and controls that need a visible edge (WCAG 1.4.11).
  • --charcoal-700#2E3236Derived. Hairline rule on charcoal. Decorative only.
  • --charcoal-800#1C1F22Derived. Raised surface on charcoal.
  • --charcoal-900#121416Deep charcoal. Brand primary dark surface and primary text on light surfaces (16.90:1 on cream).
  • --cream-50#F6F5EFWarm cream. Brand primary light surface and primary text on charcoal.
  • --neutral-200#DEE2D9Soft neutral. Sunken surface and hairline rule on light surfaces. Decorative contrast only (1.20:1 on cream).
  • --grey-300#959B9ADerived from grey 500, lighter, for muted text on charcoal: 6.54:1 on charcoal, 5.86:1 on raised charcoal.
  • --grey-500#6F7574Muted grey. Brand. Strong boundary on light surfaces (3.57:1 on neutral). Fails 4.5:1 for body text on cream (4.30:1), so muted text uses grey 600.
  • --grey-600#5A605FDerived from grey 500, darker, for muted text on light surfaces: 5.87:1 on cream, 4.88:1 on neutral.
  • --red-300#FF8A80Functional, not brand. Error text on charcoal (7.25:1 on raised charcoal). Only for validation errors.
  • --red-700#B42318Functional, not brand. Error text on light surfaces (6.02:1 on cream). Only for validation errors.
  • --white#FFFFFFRaised surface on cream: cards, inputs, the report frame.

Semantic roles, per surface

Components use roles, never the palette. Setting data-theme="dark" on any element remaps every role for that subtree.

Semantic colour roles on the cream and charcoal surfaces
RoleCream surfaceCharcoal surfaceMeaning
--color-bg#F6F5EF#121416Page surface.
--color-bg-raised#FFFFFF#1C1F22Cards, inputs, panels.
--color-bg-sunken#DEE2D9#121416Wells, code, quiet bands.
--color-fg#121416#F6F5EFPrimary text.
--color-fg-muted#5A605F#959B9ASupporting text, captions, metadata.
--color-border#DEE2D9#2E3236Hairline rules. Decorative.
--color-border-strong#6F7574#6A6F72Control boundaries that must be seen (3:1).
--color-rule#121416#F6F5EFStructural rules: the top of a grid, a table head.
--color-action#CCF676#CCF676Primary action fill.
--color-on-action#121416#121416Text on the primary action.
--color-action-edge#121416#CCF676Edge of the primary action on cream, where green alone has no contrast with the surface.
--color-verified#CCF676#CCF676Fill for verified, deterministic, shipped.
--color-verified-fg#4A6300#CCF676Text that means verified.
--color-on-verified#121416#121416Text on a verified fill.
--color-inferred#A99FEC#A99FECFill for inferred, in testing, in development.
--color-inferred-fg#5F4FB0#A99FECText that means inferred.
--color-on-inferred#121416#121416Text on an inferred fill.
--color-focus#5F4FB0#CCF676Focus ring. Passes 3:1 against every light surface.
--color-error-fg#B42318#FF8A80Validation error text and the invalid control edge.
--color-mark-verified#CCF676#4A6300Highlight band under verified words in display type; text crosses it.
--color-mark-inferred#A99FEC#5F4FB0Highlight band under inferred words in display type; text crosses it.

Contrast, computed

WCAG 2.x ratios for every pair the system promises, computed from tokens.json on build. A test fails the build if any falls below its threshold (4.5:1 text, 3:1 boundaries and focus).

Contrast ratios for promised colour pairs on both surfaces
Foreground on backgroundNeedsCreamCharcoal
fg on bg4.5:116.9016.90
fg on bg-raised4.5:118.4615.16
fg on bg-sunken4.5:114.0616.90
fg-muted on bg4.5:15.876.54
fg-muted on bg-raised4.5:16.425.86
fg-muted on bg-sunken4.5:14.886.54
on-action on action4.5:114.9614.96
on-verified on verified4.5:114.9614.96
on-inferred on inferred4.5:17.797.79
verified-fg on bg4.5:16.2514.96
verified-fg on bg-raised4.5:16.8313.41
verified-fg on bg-sunken4.5:15.2014.96
inferred-fg on bg4.5:15.957.79
inferred-fg on bg-raised4.5:16.506.98
inferred-fg on bg-sunken4.5:14.957.79
fg on mark-verified4.5:114.966.25
fg on mark-inferred4.5:17.795.95
error-fg on bg4.5:16.028.09
error-fg on bg-raised4.5:16.577.25
border-strong on bg3:14.303.63
border-strong on bg-raised3:14.703.26
action-edge on bg3:116.9014.96
focus on bg3:15.9514.96
focus on bg-raised3:16.5013.41
focus on bg-sunken3:14.9514.96
Typography

Geometric, and exactly as loud as it needs to be.

Jost, an open-source geometric sans in the Futura tradition, carries the voice; Futura itself is not licensed for the web. Martian Mono carries data: tokens, counts, paths. Sizes are fluid between a 375 and a 1440 viewport, and every size on this site is one of these nine.

  1. --text-hero · 32 to 72px. The home statement below 64rem, set in four lines: sized to the viewport so its longest line fits at 320px. From 64rem it is two lines at text-4xl.Design intelligence
  2. --text-display · 48 to 144px. Display numerals. One per page at most.Design intelligence
  3. --text-4xl · 40 to 80px. H1 on inner pages.Design intelligence
  4. --text-3xl · 32 to 56px. H2.Design intelligence
  5. --text-2xl · 24 to 36px. H3.Design intelligence
  6. --text-xl · 20 to 26px. H4, card titles.Design intelligence
  7. --text-lg · 18 to 22px. Lede, H5.Design intelligence
  8. --text-base · 16 to 18px. Body.Design intelligence
  9. --text-sm · 14px. Metadata, supporting copy, form help.Design intelligence
  10. --text-xs · 12px. Mono labels, captions.Design intelligence

Line height

  • --leading-display0.92
  • --leading-tight1.05
  • --leading-snug1.25
  • --leading-normal1.5
  • --leading-relaxed1.65

Tracking

  • --tracking-display-0.035em
  • --tracking-tight-0.02em
  • --tracking-normal0em
  • --tracking-mono-0.01em
  • --tracking-caps0.12em

Weight

  • --font-weight-light300
  • --font-weight-regular400
  • --font-weight-medium500
  • --font-weight-semibold600
Space and geometry

A 4px base, a 12-column grid, square corners.

Spacing steps grow from 4px. The page sits on a 12-column grid with a fluid gutter. Corners are square; circles are reserved for inference and status, so shape carries meaning too.

  1. --space-14px
  2. --space-28px
  3. --space-312px
  4. --space-416px
  5. --space-524px
  6. --space-632px
  7. --space-748px
  8. --space-864px
  9. --space-996px
  10. --space-10128px
  11. --space-gutter16 to 40px. Page edge and grid gap.
  12. --space-section64 to 128px. Vertical rhythm between sections.

Radius

  • --radius-none0
  • --radius-sm2px
  • --radius-full999px

Motion

  • --duration-instant1ms
  • --duration-fast120ms
  • --duration-base240ms
  • --duration-slow480ms
  • --duration-reveal800ms
  • --ease-standardcubic-bezier(0.2, 0, 0, 1)
  • --ease-outcubic-bezier(0.16, 1, 0.3, 1)

Motion explains a change of state, once. Under reduced motion every animation and transition is cut to a single frame.

Layering and borders

  • --z-behind-1
  • --z-sticky50
  • --z-overlay60
  • --z-skip100
  • --border-width-hairline1px
  • --border-width-rule2px
  • --opacity-disabled0.5
Components

Few components, each with one job.

Every interactive element has a visible focus ring on both surfaces. Try Tab.

Button

Button, on charcoal

Status

In the betaBuilt, in testingIn developmentPlanned

Certainty

ExactInferred

Status, on charcoal

In the betaBuilt, in testingIn developmentPlanned
ExactInferred

Callout

Note

Neutral information.

Today

True of the beta download.

In testing

Built, not yet shipped.

Planned

Not built.

Form controls and validation

Please check your email address.

Feedback

Code

Terminal
saer scan .
# report: .saer/report.html

Inline --color-fg sits on the sunken surface.

For agents

Rules an agent can follow.

The same system, written for Claude, Codex and every other agent that edits this site: tokens as JSON, rules as a short list, and a lint that fails when they are broken.

Terminal
npm run lint      # design rules: no raw colour, no off-scale size, no serif, no em dash
npm test          # tokens.css matches tokens.json; every contrast pair passes
npm run tokens    # regenerate tokens.css after editing tokens.json

Read the agent rules and tokens.json. The full human documentation lives in packages/saer-web/design-system/.