Health
Design system
Tokens and component specimens, organised by Atomic Design — Foundations, Atoms, Molecules, Organisms — mirroring what ships in the app and the Figma library. Space Mono (monospace-first), sharp corners, severity-driven colour.
Extracted from app/globals.css, lib/design-tokens.ts, and dashboard components. Update this page when durable tokens or patterns change.
Tokens & rules
Foundations
The palette, type ramp, spacing grid, and system rules every atom is built from. Change these in tokens, not in components.
Colors
Surfaces & borders
bg
#071a2b
Page, deep cobalt-navy field (panels lift above it)
surface
#0c2839
Cards, inputs, sticky chrome, lifted cobalt panel
surface-alt
#143650
Hover states, one step lifted cobalt
ancient
#0a2333
Reserved deep neutral (caption, not row wash)
border
#244a6b
Borders, dividers, hairline whisper on dark
dot
rgba(255,255,255,0.03)
Background dot pattern (reserved)
Text on surface
text-1
#ffffff
Primary on surface, sharp white ink (AAA, 15.2:1 on surface)
text-2
#b3cde4
Secondary body on surface, cool light blue (AAA, 9.3:1 on surface)
text-3
#93aec6
Labels, muted legends, verified AA (6.6:1 on surface, 5.3:1 on healthy tile)
Text on field (`bg`)
on-bg
#ffffff
Titles / primary on page bg, sharp white ink
on-bg-muted
#b3cde4
Leads, loading, secondary on bg
on-bg-subtle
#93aec6
Kickers, captions on bg
Brand
brand
#33e1ff
Mark, focus, active nav/filters, confirm, loading sweep, vivid aqua-cyan identity
Emphasis (restrained)
accent-ink
#d0e4f7
Cool ink emphasis, quiet lit detail, not a colour punch
accent-green
#33e1ff
Vivid aqua-cyan, echoes the mark / healthy; the one accent
accent-stone
#93aec6
Blue-grey stone, softest neutral emphasis (AA on bg/surface/surface-alt)
Severity
crit
#ff7284
rgba(255,114,132,0.16)
Critical alerts, EOL, hot rose-red (rail + bold label); AA as text on its wash (4.65:1)
high
#ff9d3d
rgba(255,157,61,0.16)
High severity, unsupported runtime, vivid cobalt orange (pops AAA on dark)
mod
#ffc933
rgba(255,201,51,0.16)
Moderate, EOL soon, vivid cobalt yellow (pops AAA on dark)
low
#93aec6
Low-severity alert counts, blue-grey stone (AA everywhere, incl. tile washes)
healthy
#33e1ff
rgba(51,225,255,0.16)
Clear, supported, vivid aqua-cyan (Health identity)
Typography
next/font. Roles: text-title · text-body · text-ui · text-caption.Title
16px · text-title
The quick brown fox, project-name
Project names
Body
14px · text-body
The quick brown fox, project-name
Default UI text; bold + tabular for lit data (versions, euros, counts)
UI
13px · text-ui
The quick brown fox, project-name
Nav, filters, buttons, section labels; regular for secondary captions
Caption
12px · text-caption
The quick brown fox, project-name
Status, dimension labels, timestamps, hours, kbd; regular when not a label
Spacing
gap-2, 4, 6…). Half-step 1 (4px) only when density requires it. Generous whitespace is intentional brand, never pack flush to panel edges or leave sparse-to-broken voids. Bars below are true width.Half-step · accent bar, dense gaps
Compact stacks · dense list rhythm
Icon + label · secondary gaps
Default card / tile grid · section insets
Tile / panel padding (corner-tick air)
Card padding · page / section padding
Large section gaps · expand column air
Overview band separation
Page-level stacks
Icon + label · gap-3 (never stuck)
Stack rhythm · gap-2 / gap-4 / gap-6
gap-2 · 8px
gap-4 · 16px
gap-6 · 24px
Layout tokens
Grid base
8px (8pt)
Primary spacing unit, prefer multiples of 8
Half-step
4px (4pt)
Only allowed subdivision, accent bar, tight gaps
Breathing room
prefer +1 step
Generous whitespace, calm density; never flush to edges or sparse-to-broken
Content max width
max-w-6xl
All screens, dashboard, brand, design system
Chrome bar height
h-14 (56px)
Shared AppChromeBar on every page
Header / filter row
min 56px
Fits 44px controls
Card gap
gap-4 (16px)
Between project cards / list stacks
Card padding
px-6 py-6
Collapsed / expanded card chrome
StatTile padding
px-5 py-5
Overview tiles, calm interior air
Tile / card grid
gap-4 (16px)
StatTile grids, craft tiles, guide stacks
Overview bands
gap-10 (40px)
Vertical separation between Portfolio · Security · …
Accent bar
before:w-1 (4px)
Left severity rail on cards / tiles (::before)
Hit target
min-h-11 (44px)
Primary interactive controls
Icon + label
gap-3 (12px)
Never stuck, minimum between glyph and text
Border radius
none (sharp)
Square corners except status dots
Control field
FIELD_CONTROL
Bordered surface input on the cobalt field
Focus & hit targets
min-h-11 (44px). Visible :focus-visible, 2px brand ring at 2px offset; inputs use inset brand shadow (no heavy outer cage). See app/globals.css. Field variants live under Atoms § Fields.Status meaning always uses label + colour, never colour alone. Prefer sr-only labels on icon-adjacent controls.
Motion
prefers-reduced-motion.sweep
animate-sweep
Loading bar on scan / empty states, brand cyan tint (bg-brand)
blink
animate-blink
Active phase step dot
reveal
animate-reveal
List items after refresh completes
confirm
animate-confirm
Copy / Slack / Updated feedback
segment-slide
segment-thumb
Report-language segmented thumb: 180ms snap translateX; engage + press scale; instant under reduced motion
Loading sweep
Confirm pulse
Report-language segments keep segment-slide. Locale strings and euro sell ranges have no scramble or count-up, they swap to the final value immediately.
Indivisible
Atoms
The smallest building blocks — icons, buttons, fields, links, status tags, and marks. Every larger pattern is assembled from these.
Icons
components/ui-icons.tsx. Always pair with a visible label except ExpandChevron and mobile logout. No emoji, no brand logos. Language segments use visible EN | NL text , not flags, not emoji.Fields
FIELD_CONTROL. Text, select, and disabled — all h-11 (44px), inset brand focus ring, no heavy outer cage. Used on login and settings forms.Links
text-2 that warms to brand on hover with a 4px underline offset — never a second button. External links pair a label with IconExternal; inline recovery reuses BTN_TOOLBAR_TEXT.Status tags
runtimeStatusTag and vulnerabilityStatusTag: a caps dimension label + coloured status text. Colour and label together, never colour alone.Marks
Small compositions
Molecules
Atoms combined into a single working control — navigation, filters, the report-language segment, scan phases, and runtime rows.
Filters
surface-alt + bold label; coloured counts. No ink invert. Refresh is text + sharp icon, same h-10 as search, trailing-aligned, no box. Absolute last-refreshed time on hover/focus title, plus that a full org scan can take several minutes; after a scan the label briefly becomes Updated.Segmented controls
SegmentedToggle + SEGMENT_* with a sliding brand-wash thumb (segment-slide). Not two loose buttons. Used for report language (EN|NL); role="group" + aria-pressed + arrow keys. One dark system, no theme toggle.SEGMENT_GROUP
h-11 · border · surface · sm:h-8
Outer shell, one control
segment-slide
180ms snap · engage · press
Thumb translateX; reduced-motion = jump
SegmentedToggle
EN|NL · thumb
Report language prefs
Live: ReportLanguageToggle
Anatomy (static)
Phase & counts
PhaseStep (label + blink dot) forms the scan strip; the alert grid tallies crit / high / mod / low with lit tabular counts — coloured text only, no filled cells.Scan phase strip
Scan phase strip · failed step
Alert count grid
EOL runtime rows
EolRuntimeRow, status badge uses eol→EOL, unsupported→Unsupported, eol-soon→EOL soon, ok→Supported, unknown→Unknown.EOL 2022-11-28 · PHP 7.4 (7.4.33) · via composer.json
EOL 2025-04-30 · Node 18 (18.20.4) · via package.json
Support 2025-02-04 · Laravel 10 (10.48) · via composer.json
Whole regions
Organisms
Self-contained regions of the app. The full page organisms — app header, upgrade block, login and settings — are catalogued under Reference to avoid duplicating live chrome and context here.
Empty · loading · error
work-shell.tsx : WorkLoading (brand sweep + phase message), WorkEmpty (calm panel), and WorkError ( role="alert" + retry that reuses Refresh). No decorative chrome; Cobalt throughout.Loading · WorkLoading
Checking runtime and security…
Empty · WorkEmpty
No sellable upgrades right now
Visible projects are current, or everything outdated is hidden.
Scan failed · WorkError (role=alert + retry)
Security check unavailable
Click Refresh to retry, scans use GraphQL to avoid rate limits.
Project cards
surface, with a left severity rail on ::before (before:w-1) only when severity signals, a neutral card carries no rail. Cards float with gap-4 breathing room.example-eol
client, production
example-healthy
client, production
ancient-stack
Long past EOL
Catalogue & guardrails
Reference
The full component catalogue (including page-level organisms) and the anti-pattern list that keeps the system honest.
Components
components/*.tsx, export name and file — including the page-level organisms (app header, upgrade block, login, settings). Keep this list in sync when adding durable UI.AppChromeBar
Shared max-width chrome row shell
app-chrome-bar.tsx
AppHeader
HealthLogo + quiet tagline + nav + logout
app-header.tsx
AppNav
Overview · Opportunities · Dashboard · Settings + quiet Brand OS
app-nav.tsx
ReportLanguageToggle
Segmented EN | NL for Copy / Slack reports
locale-provider.tsx
SegmentedToggle
Shared 2-option chrome segment (border + sliding brand thumb)
segmented-toggle.tsx
HealthLogo
Wide chrome card, cobalt + cyan rail + white HEALTH inside
health-logo.tsx
FaviconMark
Square app mark, cobalt tile + cyan rail + geometric H
favicon-mark.tsx
LogoutButton
Session sign-out when auth required
logout-button.tsx
Dashboard
Triage list, filters, refresh
dashboard.tsx
OverviewPage
Org rollup stats
overview-page.tsx
OpportunitiesPage
Sellable upgrades: € first, largest first
opportunities-page.tsx
ProjectCard
Repo row + expand detail; `/dashboard?project=` deep link
project-card.tsx
EolRuntimeRow
Runtime version / EOL line
eol-runtime-row.tsx
UpgradeBlock
Status, €/hr, lines, quiet copy/share
upgrade-block.tsx
EstimateMethodPage
Dedicated sell-hour method page
estimate-method-page.tsx
EstimateMethodNote
How hours are estimated: /estimate-method
estimate-method-note.tsx
ShareUpgradeSlackButton
Post upgrade to Slack
share-upgrade-slack-button.tsx
HourlyRateControl
€/hr estimate pref
upgrade-hourly-rate-context.tsx
EffortControl
Lean · Standard · Heavy
upgrade-hourly-rate-context.tsx
BufferControl
Uncertainty buffer on/off
upgrade-hourly-rate-context.tsx
LongPastControl
Long past EOL threshold
upgrade-hourly-rate-context.tsx
EstimateControls
Rate + effort toolbar cluster
upgrade-hourly-rate-context.tsx
PhaseStep
Scan phase label + status dot
phase-step.tsx
ExpandChevron
Card expand affordance
expand-chevron.tsx
WorkShell
Work page chrome + title; StatTile rails
work-shell.tsx
WorkLoading
Loading state, phase message + brand sweep
work-shell.tsx
WorkEmpty
Empty state, calm centered panel
work-shell.tsx
WorkError
Error state, role=alert + optional retry (banner/block)
work-shell.tsx
LoginPage
Session login form
login-page.tsx
SettingsPage
Server + estimate prefs; quiet Brand OS link
settings-page.tsx
BrandOsPage
Living Brand OS hub (guide index)
brand-os-page.tsx
ProductPage
Living Product brief
product-page.tsx
UxPage
Living Experience brief
ux-page.tsx
BrandGuidePage
Living brand guide
brand-guide-page.tsx
DesignSystemPage
This page
design-system-page.tsx
GuideAtoms
GuideChrome, sections, specimens…
guide-atoms.tsx
Surface classes
DIAG_PANEL · DIAG_SEVERITY_RAIL · field labels
lib/diag-classes.ts
Button classes
BTN_* · SEGMENT_* · FIELD_CONTROL
lib/button-classes.ts
UI icons
Sharp 16px stroke set
ui-icons.tsx
Don’t
Never
- Gradients, glassmorphism, soft multi-layer shadows
- Rounded marketing cards, pill clusters, floating badges
- Filled severity washes on rows or filters
- Colour, borders, lines, or dividers that carry no meaning
- Registration ticks, HUD marks, or diagrammatic chrome
- Vivid neon accents or cockpit theatre
- Emoji, hype, or alarmist punctuation in product UI
- A second typeface or “friendly” sans takeover
- Decorative motion; ignoring reduced-motion
- Nested severity rails or card-in-card outlines
- Stoplight / multi-colour mark rail (mark is one cyan bar)
- Stuck icon + label, tighter than gap-3 (12px)
- Packed panels, content flush to edges or stacked cages instead of breathing room
- Agency wordmark or product names prefixed with the agency brand
- Naming agent tooling or IDE brands in shipped UI