Sam Mozaffari
Experience Designer
Design system · Three products · One token file

One design system, three products

The safety, workforce and hiring concepts are built on one token file and one component file, with gates that fail the build rather than warn. Here are the files as they are, the gate that failed for real, and one component photographed on all three products.

01

The token file

assets/product/tokens.dtcg.json is the source, written in the W3C Design Tokens Community Group format. tools/build_tokens.py compiles it to the stylesheet the screens load, and the lint fails if the two drift. The values below are read from the compiled file at build time, so they are the values the screens use. There is one mode: the DTCG resolver module is still a preview draft that says not to implement it, so this system does not pretend to have modes it cannot build.

Surfaces and ink

canvas#f6f6f7
panel#ffffff
panel-2#fafafa
sidebar#111113
line#e4e4e6
line-strong#83858c
ink#16171a
ink-2#4c4e55
ink-3#6a6d75
ink-inverse#ffffff

Status pairs

danger#9b2a21
danger-bg#fbf0ef
warning#7d5300
warning-bg#fdf4e0
success#1a6042
success-bg#ecf4ef
info#1f4fd8
info-bg#eef1fd
neutral#4c4e55
neutral-bg#f1f1f2

Roster states, the workforce product only

w-shift#24405e
w-open#5b5e66
w-leave#1a6042
w-training#54407e
w-break-risk#7d5300
w-overtime#9b2a21
w-forecast#83858c
w-actual#16171a

The type scale

Eight steps from a 14px base, the smallest 11px for badges and labels. A pixel font-size anywhere in a screen's style block or in the component file fails the lint, so nineteen sizes cannot creep back in.

TokenSizeSample
fs-011pxThe manager classifies from the outcome
fs-112pxThe manager classifies from the outcome
fs-213pxThe manager classifies from the outcome
fs-314pxThe manager classifies from the outcome
fs-415.5pxThe manager classifies from the outcome
fs-518pxThe manager classifies from the outcome
fs-626pxThe manager classifies from the outcome
fs-734pxThe manager classifies from the outcome
02

The manifest an agent must search

assets/product/components.manifest.json lists every class the component file defines, 233 of them, generated from components.css so it cannot drift. AGENTS.md tells an agent to search it before writing a component; the lint fails on any p- class used in a screen that is neither in the manifest nor defined in that screen's own style block. Components were lifted into the shared file once a second screen needed them, which is how the drawer, the numbered step list, the comparison pair and the oversized search field arrived.

p-actions · p-alert · p-alert-danger · p-alert-info · p-alert-success · p-alert-warning · p-app · p-app-bar · p-appbar · p-avatar · p-avatar-open · p-avatar-sm · p-back · p-badge · p-badge-danger · p-badge-info · p-badge-neutral · p-badge-plain · p-badge-success · p-badge-type · p-badge-warning · p-bar · p-between · p-board · p-box · p-brand · p-brand-inline · p-brand-mark · p-breakdown · p-brk · p-btn · p-btn-block · p-btn-brand-quiet · p-btn-danger · p-btn-ghost · p-btn-lg · p-btn-primary · p-btn-secondary · p-btn-sm · p-caps · p-card · p-card-body · p-card-head · p-cell · p-chart · p-chat · p-check · p-chev · p-chip · p-choice · p-choices · p-clock · p-clock-body · p-clock-head · p-col · p-col-head · p-compare · p-consent · p-count · p-counter · p-counters · p-cover · p-crumbs · p-cta · p-datestrip · p-demand · p-diff · p-diff-cell · p-dot · p-drawer · p-drawer-body · p-drawer-foot · p-drawer-head · p-drawer-wrap · p-empty · p-error · p-field · p-fieldset · p-fig · p-fig-d · p-fig-l · p-fig-v · p-figs · p-filters · p-flag · p-floor · p-gap-2 · p-glyph · p-grid · p-grid-2 · p-grid-3 · p-grid-4 · p-grid-8-4 · p-grow · p-hint · p-home · p-ico · p-impact · p-input · p-kanban · p-kcard · p-kmeta · p-kv · p-lane · p-lane-head · p-ledge · p-legend · p-legendbar · p-link-ref · p-list · p-list-row · p-main · p-mb-3 · p-mb-4 · p-mb-5 · p-mdate · p-meta · p-mhead · p-modnav · p-mono · p-mrow · p-msg · p-mstat · p-mstat-l · p-mstat-v · p-mt-2 · p-mt-3 · p-mt-4 · p-mt-5 · p-muted · p-nav · p-nav-label · p-note-row · p-nowrap · p-num · p-overdue · p-page · p-page-fill · p-page-head · p-pager · p-pb · p-pd · p-person · p-ph · p-phone · p-pin · p-post · p-primary-cell · p-punch · p-quick · p-quick-add · p-rail · p-rail-group · p-rail-item · p-railed · p-rarea · p-rarea-sum · p-rc · p-record · p-record-head · p-record-id · p-rf · p-rgrid · p-rh · p-rhead · p-right · p-ring · p-rn · p-rname · p-roster · p-roster-grid · p-row · p-scope · p-screen · p-scrollx · p-search · p-search-lg · p-section · p-seg · p-select · p-sent · p-sevtabs · p-shell · p-shell-top · p-shift · p-shift-flag · p-sidebar · p-sidebar-foot · p-slot · p-slotday · p-slotrow · p-slots · p-spacer · p-stat · p-stat-accent · p-stat-delta · p-stat-label · p-stat-value · p-status · p-status-icons · p-stepper · p-steps · p-sub · p-swap · p-swap-body · p-switch · p-tabbar · p-table · p-table-foot · p-table-wrap · p-tabs · p-textarea · p-timeline · p-tl-item · p-tl-note · p-toast · p-togglerow · p-topbar · p-tri-head · p-triage · p-triage-panel · p-vcheck · p-w-100 · p-waited · p-weekpick · p-where · p-who · p-why · p-work · p-work-head · p-work-sub · p-wrap · p-zone

03

The gates, and the one that failed

GateCommandFails on
Tokens in steptools/build_tokens.py --checkCSS out of date with the DTCG source
Contrasttools/contrast_check.pyAny text pair under 4.5:1, any control border under 3:1
Component registrytools/lint_site.pyA `p-` class that is not declared anywhere
Site contenttools/lint_site.pyVendor names, wrong counts, broken local links, cropped artefacts, em dashes
Visual regressiontools/visual_check.pyA screen that has changed against its committed baseline
Side guttertools/check_layout.pyAny text under `main` within 16px of the window edge, or a page that scrolls sideways, at 390 and 1280 (pages are loaded in an iframe of that width, because headless Chrome will not lay out under about 500px)
Facts across surfacestools/check_facts.pyA hero number typed differently on the case, the home page, the Work page, the showcase or llms.txt; a Work headline that miscounts its cards; a stat-strip number missing from the case's facts.json
Safety factstools/check_safety_facts.pyA weekday that does not match its date in the safety screens, initials that do not match the name, a reference used for two records, counts that disagree between screens
The one that failed for real

It caught a real failure while this layer was being written: an open shift is drawn as an outline, so the outline is the object and has to clear 3:1, which the first grey did not.

The three fact checkers assert the screens against one data table each, and staleness is a content hash written at capture time, so they pass on a fresh clone as well as on the machine that captured them.

04

One component, three products

The same badge with a dot and a word, the same avatar and the same secondary button, photographed on the safety register, the workforce compliance list and the hiring pipeline. The row and the card are the same component family drawn for different jobs. Nothing here is a picture of a component; each crop is cut from the captured screen.

A table row with a status badge on the safety product, cut from its captured screen

Safety · from register.png

A table row with a status badge on the workforce product, cut from its captured screen

Workforce · from compliance-list.png

A table row with a status badge on the hiring product, cut from its captured screen

Hiring · from pipeline.png