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.
Color system
A compact semantic palette. Brand is rationed; neutrals do the heavy lifting; status and module hues are the only other colors allowed, and each has a fixed job.
Brand — use sparingly
Locked. This orange + black is the approved internal brand for the Design Manager. (Krown’s public mark is yellow/gold; internal tools use this.) Orange appears on the brand mark and wayfinding; black gives the app its spine; everything sits on a warm off-white canvas. Orange is never a button fill.
Ink & neutral surfaces
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.
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.
Module accents — quiet wayfinding
Each module gets one muted hue, used only in section headers, the active label, or a 4px card left-spine — never as a fill. They share a quiet register so no module shouts over another; Locations is the one bright note, carrying the Krown brand yellow. Intake reuses the brand mark behind the scenes (the front door) and isn’t shown as its own module hue.
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.
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
.km-subtext— a named role, so secondary text is never hand-styled per surface.
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.
xschips, dots, platform tiles.smbuttons, inputs.mdcards, panels (the default container).lglarge 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.
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.
--border-w hairline in --border, no shadow. This is 95% of the UI.--shadow-pop.Rules
- Static cards & rows: border only. Never a resting shadow.
- Hover on interactive cards: border darkens to
--border-strong+ optional--shadow-sm. - Overlays (dropdowns, dialogs, toasts):
--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.
| Token | Value | Use |
|---|---|---|
--shadow-sm | 0 1px 2px rgba(27,26,24,0.05) | Resting card |
--shadow-md | 0 4px 12px rgba(27,26,24,0.10), 0 1px 3px rgba(27,26,24,0.06) | Hover lift |
--shadow-pop | 0 8px 28px rgba(27,26,24,0.16) | Floating overlays |
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.
| Token | Value | Use |
|---|---|---|
--glass-bg | rgba(252,251,249,0.78) | Balanced — glassy yet soft over busy content |
--glass-bg-solid | rgba(252,251,249,0.92) | Denser, for tall / legible bodies |
--glass-blur | 50px | Backdrop blur radius |
--glass-saturate | 135% | Backdrop saturation boost |
--glass-border | rgba(255,255,255,0.6) | Bright glass edge |
--glass-hi | rgba(255,255,255,0.6) | Inset top highlight |
--glass-shadow | 0 1px 0 var(--glass-hi) inset, 0 16px 44px rgba(27,26,24,0.16), 0 2px 8px rgba(27,26,24,0.06) | Composite glass shadow — highlight + deep soft drop |
--glass-input-bg | rgba(255,255,255,0.62) | Frosted input resting on glass |
--glass-input-bg-focus | rgba(255,255,255,0.92) | Frosted input focused — denser |
--glass-input-line | rgba(27,26,24,0.12) | Hairline input edge on glass |
--glass-inset-line | rgba(27,26,24,0.07) | Hairline divider inside glass (receipt rows) |
--glass-inset-bg | rgba(255,255,255,0.16) | Inset well on glass (receipt card) |
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.
| Token | Value | Use |
|---|---|---|
--border-w | 1px | Standard stroke — borders, dividers, outlines |
--border-w-hairline | 0.5px | Hairline on glass surfaces |
--border-w-focus | 2px | Focus ring line weight |
Control sizes
The few fixed heights everything aligns to — header, controls, tiles.
| Token | Value | Use |
|---|---|---|
--size-header | 56px | App header height |
--size-ctl | 34px | Standard control height — buttons, inputs, facets |
--size-ctl-sm | 28px | Small control height |
--size-tile | 26px | Platform tile / brand mark |
--size-sidenav | 232px | Left rail width — the .km-sidenav column |
Z-index
Stacking order is a named scale, not an arms race.
| Token | Value | Use |
|---|---|---|
--z-shell | 100 | App shell chrome — header, nav |
--z-pop | 600 | Popovers, dropdown menus |
--z-overlay | 1000 | Dialog scrim + dialog |
--z-toast | 1400 | Toasts / audit badges above everything |
Focus
One focus ring everywhere — surface gap + brand line.
| Token | Value | Use |
|---|---|---|
--focus-ring | 0 0 0 2px var(--surface), 0 0 0 4px var(--brand) | One focus ring everywhere — surface gap + brand line |
More foundations
Three foundations have grown large enough to earn their own pages. They follow the same token discipline as everything above.