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

Aa

text-1

#ffffff

Primary on surface, sharp white ink (AAA, 15.2:1 on surface)

Aa

text-2

#b3cde4

Secondary body on surface, cool light blue (AAA, 9.3:1 on surface)

Aa

text-3

#93aec6

Labels, muted legends, verified AA (6.6:1 on surface, 5.3:1 on healthy tile)

Text on field (`bg`)

Aa

on-bg

#ffffff

Titles / primary on page bg, sharp white ink

Aa

on-bg-muted

#b3cde4

Leads, loading, secondary on bg

Aa

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)

no bg token

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

Space Mono throughout (400 / 700), monospace-first brand. Bold for titles, chrome, and lit values; regular for body and meta. Via 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

8pt grid + breathing room, prefer multiples of 8 (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.
gap-14px

Half-step · accent bar, dense gaps

gap-28px

Compact stacks · dense list rhythm

gap-312px

Icon + label · secondary gaps

gap-416px

Default card / tile grid · section insets

gap-520px

Tile / panel padding (corner-tick air)

gap-624px

Card padding · page / section padding

gap-832px

Large section gaps · expand column air

gap-1040px

Overview band separation

gap-1248px

Page-level stacks

Icon + label · gap-3 (never stuck)

RefreshCopyWrong: gap-1 / gap-2 between glyph and text

Stack rhythm · gap-2 / gap-4 / gap-6

gap-2 · 8px

gap-4 · 16px

gap-6 · 24px

Layout tokens

Recurring chrome measurements.

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

Primary controls: 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

Use only when communicating scan/status, expand, a completed action, or segmented chrome preference feedback. Language copy and € amounts update instantly. Honor 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

Sharp 16px stroke set in 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.
IconRefresh
IconSearch
IconCopy
IconCheck
IconExternal
IconHide
IconShow
IconShare
IconLogout
ExpandChevron

Buttons

Quiet bordered actions via lib/button-classes.ts : border + surface, hover brand wash. Never invert to ink black. Icons beside labels use at least gap-3, never stuck. Toolbar Refresh stays text + icon, no box.

BTN_ACTION

min-h-11 · bordered · gap-3

Primary card / form actions

BTN_ACTION_COMPACT

px-3 py-2 · bordered · gap-3

Inline / specimen denser actions

FIELD_CONTROL

h-11 · bordered surface

Login / form / specimen inputs

BTN_TOOLBAR_TEXT

h-10 · text only · gap-3

Refresh, never a second box

Toolbar Refresh (BTN_TOOLBAR_TEXT)

Search…

Fields

Bordered flat inputs on the cobalt field via FIELD_CONTROL. Text, select, and disabled — all h-11 (44px), inset brand focus ring, no heavy outer cage. Used on login and settings forms.

Status tags

Card row status from runtimeStatusTag and vulnerabilityStatusTag: a caps dimension label + coloured status text. Colour and label together, never colour alone.
RuntimeEOL · 2y
RuntimeEOL soon
RuntimeSupported
AlertsCritical
AlertsClear
AlertsChecking

Marks

Wide HealthLogo lockup for chrome (cobalt + cyan rail + white HEALTH inside); square cobalt tile + healthy cyan rail + geometric H for favicon / app mark.
Favicon · app/icon.svg
Chrome · HealthLogo

Small compositions

Molecules

Atoms combined into a single working control — navigation, filters, the report-language segment, scan phases, and runtime rows.

Filters

Active = quiet 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

Chrome prefs are one bordered unit each : 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)

ENNL

Phase & counts

Composites built from atoms: 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

ReposRuntimeAlerts

Scan phase strip · failed step

ReposRuntime failedAlerts

Alert count grid

2Crit
1High
0Mod
3Low

EOL runtime rows

EolRuntimeRow, status badge uses eol→EOL, unsupported→Unsupported, eol-soon→EOL soon, ok→Supported, unknown→Unknown.
PHP7.43y 8mo past EOL

EOL 2022-11-28 · PHP 7.4 (7.4.33) · via composer.json

EOL
Node18

EOL 2025-04-30 · Node 18 (18.20.4) · via package.json

EOL soon
Laravel10

Support 2025-02-04 · Laravel 10 (10.48) · via composer.json

Supported

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

Every data surface (Overview, Dashboard, Opportunities, Settings) shares three first-class states from 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)

Project cards

Shared shell: border + 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

RuntimeEOL · 1y
SecurityCritical

example-healthy

client, production

RuntimeSupported
SecurityClear

ancient-stack

Long past EOL

RuntimeEOL · 4y
SecurityHigh

Catalogue & guardrails

Reference

The full component catalogue (including page-level organisms) and the anti-pattern list that keeps the system honest.

Components

Catalogue of 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

If it looks like default AI SaaS, agency moodboards, or a security product ad, it is wrong. Same list on /brand § 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