Design System · Foundations

Foundations

The non-negotiable base layer — color, type, spacing and borders. Motion, iconography and overlays live on their own linked pages. Everything else in the system is built on these tokens, and every swatch and ramp below renders from the registry — the same data that generates the stylesheet and feeds consumer apps.

01

Color system

A grayscale semantic foundation. Roles communicate hierarchy and state; client identity is supplied by a versioned recipe rather than embedded product color.

Grayscale identity foundation

Locked as the neutral contract. Every core role is grayscale so the system can prove hierarchy, contrast, focus, status, and interaction without relying on a client's brand. A consumer recipe may remap semantic color roles, but it cannot bypass the token contract or alter component structure.

--ss-color-brand
#181818
Primary neutral brand and action role
--ss-color-brand-active
#272727
Active neutral brand and action role
--ss-color-brand-subtle
#F4F4F4
Subtle neutral brand surface
--ss-color-on-brand
#FFFFFF
Foreground on the brand fill
--ss-color-accent
#525252
Secondary neutral wayfinding role
--ss-color-accent-subtle
#F4F4F4
Subtle secondary wayfinding surface

Ink & neutral surfaces

--ss-color-ink-900
#181818
Primary text
--ss-color-ink-800
#272727
Active neutral fill and strongest hover
--ss-color-ink-700
#3F3F3F
Strong body / headings on surface
--ss-color-ink-600
#525252
Supporting body text
--ss-color-ink-500
#626262
Secondary text
--ss-color-ink-400
#767676
Muted / placeholder
--ss-color-ink-inverse
#FFFFFF
Text on ink-900 fills
--ss-color-canvas
#F7F7F7
App canvas
--ss-color-surface
#FFFFFF
Cards, panels
--ss-color-surface-subtle
#F4F4F4
Alt rows, inset wells
--ss-color-surface-strong
#E4E4E4
Deeper inset / hover
--ss-color-border
#E4E4E4
Hairline
--ss-color-border-strong
#949494
Emphasized divider / input edge

On-dark — the header’s inverted neutrals

The ink-900 header and nav are the one place the UI sits on near-black, so the usual neutrals invert. Tokenised so chrome on dark is never inline-styled with raw hex — this very app’s left nav uses exactly these tokens for its labels, hover and selection states.

--ss-color-on-dark-line
#3F3F3F
Outlined control border / hairline on dark
--ss-color-on-dark-edge
#090909
Hard edge under the ink-900 header
--ss-color-on-dark-ink
#FFFFFF
Primary text & icons on dark
--ss-color-on-dark-ink-2
#949494
Secondary text on dark — nav links, header actions
--ss-color-on-dark-ink-dim
#B5B5B5
Tertiary / inactive text on dark (resting tabs)
--ss-color-on-dark-ink-mute
#A0A0A0
Muted meta / group labels on dark
--ss-color-on-dark-hover
rgba(255,255,255,0.07)
Subtle fill on hover
--ss-color-on-dark-active
rgba(255,255,255,0.11)
Selected / active fill on dark

Status — color + always a word

Four states cover the whole tool. Status is never color-only: it always ships with a label and (in chips) a dot, so it survives colorblindness and grayscale printing.

--ss-color-status-success
#272727
Published / live / done foreground
--ss-color-status-success-subtle
#F4F4F4
Published / live / done surface
--ss-color-status-success-subtle-hover
#E4E4E4
Published surface hover
--ss-color-status-info
#3F3F3F
In progress / informational foreground
--ss-color-status-info-subtle
#F4F4F4
Informational surface
--ss-color-status-warning
#525252
Attention / pending foreground
--ss-color-status-warning-subtle
#F4F4F4
Attention / pending surface
--ss-color-status-warning-text
#3F3F3F
Attention text on subtle surface
--ss-color-status-warning-border
#767676
Attention border
--ss-color-status-danger
#181818
Blocked / error foreground
--ss-color-status-danger-subtle
#E4E4E4
Blocked / error surface
--ss-color-status-danger-text
#181818
Blocked / error text
--ss-color-status-danger-border
#626262
Blocked / error border
--ss-color-status-danger-pulse-start
rgba(24,24,24,0.5)
Danger pulse start
--ss-color-status-danger-pulse-end
rgba(24,24,24,0)
Danger pulse end
--ss-color-status-neutral
#626262
Neutral / skipped / draft foreground
--ss-color-status-neutral-subtle
#F4F4F4
Neutral / skipped / draft surface
Published Manual task Blocked Draft
02

Typography

The native system font (San Francisco · Segoe · Roboto) for everything readable; the matching system monospace (SF Mono · Consolas) for IDs, labels, timestamps, and technical metadata. Headings run heavy (800) with a deliberate size jump to body — but body and labels never drop below 400. Mono signals “machine fact, secondary”. No web fonts load — the UI feels instant and native.

Aa Display
Display · 48/50 · 800
Page title — Overview
Page · 30/36 · 800
Section title
Section · 19/25 · 800
Card title — Example item
Card · 14/20 · 800
Body text. A concise interface keeps the next action clear.
Body · 14/21 · 400
Small body — supporting detail inside dense rows.
Small · 13/19 · 400
Subtext — captions, descriptions & metric labels.
Subtext · 13/19 · 400 · ink-500
Row label
Label · mono 12 · 500 · caps
LOC-0421 · updated 2h ago
Meta · mono 12/16

Real headings vs styled labels

  • Use one real <h1> per route (the page title) and <h2> for genuine sections.
  • Card titles are <h3> only when the card is a true content section; in dense lists, render them as styled text to avoid a heading-soup outline.
  • Row labels, chips, and timestamps are never headings — they’re mono labels.

Reducing heading clutter

  • In a location card, the name is the only “title.” Address, ID, status are labels and metadata — not sub-headings.
  • Prefer a mono caps label + value pair over a bold sub-heading for every field.
  • Cap visible weight: at most one 600 element per card. If everything is bold, nothing is.
  • Secondary captions, descriptions & metric labels use .ss-subtext — a named role, so secondary text is never hand-styled per surface.
03

Spacing & radius

A 4px base scale with two micro steps. Lists live at the tight end (8–12px); detail and intake breathe at 16–24px. Consistency matters more than the exact number.

--ss-space-05 · 2px
--ss-space-1 · 4px
--ss-space-15 · 6px
--ss-space-2 · 8px
--ss-space-3 · 12px
--ss-space-4 · 16px
--ss-space-5 · 20px
--ss-space-6 · 24px
--ss-space-8 · 32px
--ss-space-10 · 40px
--ss-space-12 · 48px
--ss-space-16 · 64px
--ss-radius-xs · 4px
--ss-radius-sm · 9px
--ss-radius-md · 13px
--ss-radius-lg · 18px
  • xs chips, dots, platform tiles.
  • sm buttons, inputs.
  • md cards, panels (the default container).
  • lg large shells, dialogs, the intake column.

Density targets. Location list rows: 12px vertical padding, 16px gutters. Card internal gap: 12px. Detail field rows: 12px vertical. Page gutters: 24–32px. A location card should comfortably show name, address, ID, four platforms, and status without scrolling inside the card.

04

Borders & elevation

Borders first, shadows almost never. A flat, bordered UI scans better at density and prints cleanly. Shadow is reserved for things that float above the page.

Default container — --ss-border-width hairline in --ss-color-border, no shadow. This is 95% of the UI.
Floating only — menus, popovers, the active intake confirmation. --ss-shadow-pop.

Rules

  • Static cards & rows: border only. Never a resting shadow.
  • Hover on interactive cards: border darkens to --ss-color-border-strong + optional --ss-shadow-sm.
  • Overlays (dropdowns, dialogs, toasts): --ss-shadow-pop, because they genuinely float.
  • Separation inside a panel: a hairline divider, not a gap-and-shadow.

Elevation tokens

Use sparingly — borders first. Shadows are reserved for things that float.

TokenValueUse
--ss-shadow-sm0 1px 2px rgba(24,24,24,0.05)Resting card
--ss-shadow-md0 4px 12px rgba(24,24,24,0.10), 0 1px 3px rgba(24,24,24,0.06)Hover lift
--ss-shadow-pop0 8px 28px rgba(24,24,24,0.16)Floating overlays
--ss-shadow-input-insetrgba(24,24,24,0.04)Hairline inset shadow on input edges

Glass material

The premium translucent surface for things that FLOAT over the page — dialogs, menus, popovers. The page behind stays sharp; only a floating surface frosts what is directly beneath it. The input and inset values were promoted to tokens in V2 — they were previously hardcoded inside the dialog stylesheet.

The premium translucent surface for things that FLOAT over the page: dialogs, menus, popovers. The page behind stays sharp; only a floating surface frosts what is directly beneath it.

TokenValueUse
--ss-glass-bgrgba(252,252,252,0.78)Balanced — glassy yet soft over busy content
--ss-glass-bg-solidrgba(252,252,252,0.92)Denser, for tall / legible bodies
--ss-glass-blur50pxBackdrop blur radius
--ss-glass-saturate135%Backdrop saturation boost
--ss-glass-borderrgba(255,255,255,0.6)Bright glass edge
--ss-glass-hirgba(255,255,255,0.6)Inset top highlight
--ss-glass-shadow0 1px 0 var(--ss-glass-hi) inset, 0 16px 44px rgba(24,24,24,0.16), 0 2px 8px rgba(24,24,24,0.06)Composite glass shadow — highlight + deep soft drop
--ss-glass-input-bgrgba(255,255,255,0.62)Frosted input resting on glass
--ss-glass-input-bg-focusrgba(255,255,255,0.92)Frosted input focused — denser
--ss-glass-input-linergba(24,24,24,0.12)Hairline input edge on glass
--ss-glass-inset-linergba(24,24,24,0.07)Hairline divider inside glass
--ss-color-shimmer-highrgba(255,255,255,0.18)Skeleton/meter sheen highlight (lower opacity)
--ss-color-shimmer-midrgba(255,255,255,0.45)Skeleton/meter sheen mid-band highlight (higher opacity)
05

Structure tokens

V2 finished the token layer: stroke weights, control heights, stacking order and the focus ring are decisions, not magic numbers. All were promoted from previously-hardcoded values.

Border widths

Stroke weights are a decision, not a magic number.

TokenValueUse
--ss-border-width1pxStandard stroke — borders, dividers, outlines
--ss-border-width-hairline0.5pxHairline on glass surfaces
--ss-border-width-focus2pxFocus ring line weight

Control sizes

The few fixed heights everything aligns to — header, controls, tiles.

TokenValueUse
--ss-size-control34pxStandard control height — buttons, inputs, facets
--ss-size-control-small30pxSmall control height
--ss-size-tile26pxCompact choice indicator and mark size
--ss-size-touch-target44pxMinimum square touch target
--ss-size-dialog-sm400pxSmall dialog inline maximum
--ss-size-dialog-md520pxMedium dialog inline maximum
--ss-size-dialog-lg680pxLarge dialog inline maximum
--ss-element-button-icon-padding11pxIcon-button inline inset
--ss-element-button-icon-gap7pxIcon-button internal gap
--ss-element-chip-height22pxStatus chip minimum block size
--ss-element-status-indicator-size6pxStatus and meter indicator thickness
--ss-element-dialog-icon-size36pxDialog icon tile
--ss-element-dialog-close-offset-block-3pxDialog close optical block offset
--ss-element-dialog-close-offset-inline-6pxDialog close optical inline offset
--ss-element-control-padding-inline10pxNative control inline inset
--ss-element-control-icon-offset11pxNative control trailing icon offset
--ss-element-segment-inset3pxSegmented-control thumb inset
--ss-element-segment-option-padding14pxSegmented-control option inline inset
--ss-element-dropdown-menu-offset-6pxDropdown menu closed-state block offset
--ss-element-textarea-min-height88pxTextarea default minimum block size
--ss-element-drawer-width360pxDrawer preferred inline size
--ss-element-check-glyph-size22pxCompletion check glyph
--ss-element-text-gap5pxTight helper-text separation
--ss-element-halo-spread14pxCompletion halo maximum spread
--ss-size-layout-grid-sm12remSmall intrinsic grid item minimum
--ss-size-layout-grid-md16remMedium intrinsic grid item minimum
--ss-size-layout-grid-lg22remLarge intrinsic grid item minimum
--ss-size-layout-content70remReadable centered content maximum
--ss-size-layout-wide90remWide centered content maximum
--ss-size-layout-full100%Uncapped centered content width
--ss-size-layout-sidebar18remIntrinsic sidebar preferred width
--ss-size-layout-sidebar-threshold50%Content threshold before sidebar wraps
--ss-size-layout-switcher-sm24remSmall switcher stack threshold
--ss-size-layout-switcher-md36remMedium switcher stack threshold
--ss-size-layout-switcher-lg48remLarge switcher stack threshold

Z-index

Stacking order is a named scale, not an arms race.

TokenValueUse
--ss-z-pop600Popovers, dropdown menus
--ss-z-overlay1000Dialog scrim + dialog
--ss-z-toast1400Toasts / audit badges above everything

Focus

One focus ring everywhere — surface gap + brand line.

TokenValueUse
--ss-focus-ring0 0 0 2px var(--ss-color-surface), 0 0 0 4px var(--ss-color-brand)One focus ring everywhere — surface gap + brand line
--ss-focus-ring-width3pxInner focus wash spread
06

More foundations

Three foundations have grown large enough to earn their own pages. They follow the same token discipline as everything above.

07

Toggle axes

Global customization knobs. One knob writes a data-attribute on the <html> root, the token layer swaps values, and every element renders under the new tier without any markup or behavior change. Motion pace is the reference `approved` axis; the five below (Density, Weight, Radius, Elevation, Contrast) ship `proposed` this round for evaluation on real UI.

Click a tier below to swap it live on this app; the choice persists in localStorage, no-flash on reload. Consumer apps read the same registry via /api/axes.json or /api/registry.json under an axes key.

Motion Approved data-ss-motion
Fast / Smooth / Calm — the single global pace knob. Easing never changes.
Density Proposed data-ss-density
Spacing scale multiplier — how tightly the UI packs. Compact suits information-dense rows and dashboards; Airy suits reading-first surfaces and phone widths.
Weight Proposed data-ss-weight
Border and divider stroke weight — how "structural" the UI feels. Hairline reads as glass; Bold reads as bureaucratic-precision.
Radius Proposed data-ss-radius
Corner rounding across every surface. Sharp reads more architectural / print-y; Round reads more toylike / iOS-y.
Elevation Proposed data-ss-elevation
Shadow depth and layering emphasis. Flat leans on borders alone; Deep leans harder on shadow for floating surfaces.
Contrast Proposed data-ss-contrast
Ink / surface / border contrast steps. Quiet reads calmer; Vivid pushes harder for outdoor / accessibility / print-heavy contexts. Accent hues stay theme-controlled and are NOT touched by this axis.