Milton design system

This document is the single source of truth for Milton’s visual language. It replaces the previous amber-on-warm-white palette entirely. Read it fully before making any changes.


1. What changed and why

The previous palette used a warm off-white base with an amber accent. Two problems:

  1. Amber is the universal warning colour. In a compliance product, “needs review” is one of the most important states in the UI. Amber cannot be both the brand colour and the review state — every button competed with every warning.
  2. The warm base + warm accent + near-black ink structure was not distinctive. The fix is structural: a cool neutral base and an accent that carries no status meaning.

The new system is achromatic-dominant. Colour is spent on status, not decoration.

Amber is not deleted. It is demoted from brand colour to the review status colour. This distinction drives most of the migration decisions below.


2. Colour tokens

Core

TokenLightDarkUse
ink#14161A#E8E9ECPrimary text, logotype, headings
graphite#2E323B#9DA3AFSecondary text, body copy at lower emphasis
slate#6B7280#6B7280Muted text, metadata, timestamps, placeholders
rule#D8DBE0#2C3038Hairline borders, dividers, table rules
paper#F2F3F5#111318Page background
surface#FFFFFF#1A1D23Card, panel, modal background

Accent — interaction only

TokenLightDarkUse
signal#453C7A#8B7FD4Primary buttons, links, active nav, focus rings
signal-hover#5D52A0#A196E0Hover and active states
signal-tint#ECEBF4#24222FSelected rows, focus backgrounds, subtle highlights

signal means “this is interactive.” It never means “this is good” or “this is a problem.” Do not use it for status, emphasis, illustration, or decoration.

Status — reserved vocabulary

TokenLightDarkTint (light)Tint (dark)Meaning
compliant#1B6B47#4FB587#E6F0EB#16281FPassing check, verified evidence
review#96610B#E0A244#FAF0DE#2A2113Needs human review, expiring evidence
non-compliant#B03A2E#E07A6E#F7E9E7#2B1A18Failing check, missing control
pending#8E92A0#8E92A0#EDEEF1#1F2228Queued, not yet evaluated

Status colours appear only where they carry that meaning. They are never used as generic accent, chart series colour, or category colour.

Badges and pills use the tint as background and the solid as text. Never solid fill with white text — in a table of hundreds of rows, solid status fills turn the screen into a traffic light. Example: a compliant badge is #1B6B47 on #E6F0EB.


3. Typography

RoleFamilyNotes
UI and bodyIBM Plex SansEverything by default
Data and identifiersIBM Plex MonoDocument IDs, hashes, timestamps, version numbers, compliance codes, audit references, diffs

The mono face is functional, not decorative. Anything that represents a tracked, immutable, or machine-generated value gets mono. This is a deliberate part of the identity — it gives the product a visual language for “this is a record.”

Weights: 400 regular, 500 medium, 600 semibold. Do not use 700 or above.

Install via @fontsource/ibm-plex-sans and @fontsource/ibm-plex-mono rather than a Google Fonts CDN link — self-hosting avoids a third-party request, which matters for a product sold to compliance-conscious buyers.


4. Migration rules

These are the rules that determine whether the migration is correct rather than merely complete.

4.1 Do not blind-replace hex values

Every old amber occurrence must be classified before it is replaced. The same old hex maps to different new tokens depending on what it meant:

Old amber was used forNew token
Primary button, link, active nav item, focus ringsignal
Warning banner, “expiring soon”, “needs review” statereview
Logotype, wordmark, brand markink — remove colour entirely
Chart series, illustration, decorative fillslate or signal-tint — reassess case by case

If a usage is ambiguous, flag it rather than guessing.

4.2 The logotype stays achromatic

The Milton wordmark and the scattered-blocks icon are pure ink on paper, or pure paper on ink for inverse. No accent colour. If you find a coloured logo variant in the codebase, convert it to ink and note it.

4.3 Warm neutrals become cool neutrals

The old base was warm (cream, tan, warm grey). Every warm neutral maps to its cool equivalent in the core ramp. Do not leave a warm off-white anywhere — a single cream surface next to #F2F3F5 reads as a bug.

4.4 Dark mode values are not derived

Do not compute dark mode by inverting or darkening light values. Use the dark column as given. signal in particular shifts substantially (#453C7A#8B7FD4) because the light value is unreadable on a dark surface.

4.5 One accent per view

At most one signal-filled primary button per screen. Secondary actions use an outline style with rule border and ink text. If a screen has three filled purple buttons, two of them are wrong.

4.6 Contrast floor

All text meets WCAG AA: 4.5:1 for body, 3:1 for large text and UI boundaries. review at #96610B is the darkened variant specifically so small badge text passes — do not substitute the lighter #B4740E.

Milton is a compliance product. Shipping an interface that fails accessibility standards is a credibility problem, not just a quality one.


5. Definition of done

  • No hardcoded hex values remain in component files — all colour flows through tokens
  • No warm neutrals remain anywhere in the codebase
  • Both fonts load and render; no fallback flash
  • Mono face applied to IDs, timestamps, hashes, and version strings
  • Light and dark mode both verified on every changed screen
  • Logotype renders achromatic in both modes
  • Status badges use tint background with solid text
  • Focus states visible and use signal
  • No screen has more than one filled primary button

6. Additions from the Phase 1 audit

These extend the system to cover two real cases the original spec didn’t anticipate, plus one clarification and one exemption.

6.1 Category / type differentiation

Where the UI distinguishes several types of the same object (e.g. block types) rather than status, do not invent a new colour ramp. Chroma is reserved for status (§2) and signal (§2.1) — a seven-hue category ramp turns the system back into the “rainbow SaaS” look the palette exists to avoid, and it fails colourblind users regardless of how carefully the hues are chosen.

Differentiate type with icon + label, not hue. All type badges share one neutral fill. If a block needs to carry both a type and a status (e.g. “Policy” type, “needs review” status), the status shows as a separate small indicator — a dot or left border — not a recolour of the type badge. Keep the two dimensions visually independent so they aren’t read as the same signal.

6.2 Surface elevation

paper and surface cover pages and cards, but real UIs need more steps for nested wells, hover rows, and overlays. Add these by purpose, not by number:

TokenPurpose
surfaceCards, panels — existing
surface-sunkenNested wells, input backgrounds, code blocks — recedes from surface toward paper
surface-hoverRow/item hover and active list states
surface-overlayModals, popovers, dropdowns, tooltips — pairs with a shadow, not just a fill change

Derive exact values as consistent lightness steps per theme rather than eyeballing. In light mode this is mostly shadow-driven, since surface is already near-white. In dark mode expect four genuinely distinct fills. Map the current ramp’s actual call sites onto these four; anything that doesn’t fit cleanly, flag rather than force.

6.3 “Completed” is not “Compliant”

A workflow finishing is not the same fact as a control passing. Don’t badge “Completed” with a status colour by default — render it in ink / graphite as a neutral state. Only apply compliant / non-compliant / review where the badge reports the result of an evaluation, not that a process ran.

6.4 Exemptions

User-assigned colours (e.g. a colour picker for tagging the user’s own documents) are user content, not brand chrome, and sit outside this system entirely. Don’t constrain them to the palette.