Per RFC-WS-PRIMEVUE-PLAN-2-5 Track B (§6). Inventories and classifies the design tokens live in the codebase (Brand-essential / Bespoke / Generic per RFC §6.2) and records the Typography decision register (AD-2.5-T1) end-to-end — including the historical Public Sans removal across both the CSS path (P2, commit41af1801) and the webfontloader JS path (P2-followup, commit641ca513). Inventory covers: - Tailwind v4 @theme + :root (font tokens + dark variant selector) - PrimeVue Aura preset (full 11-step Crewli teal primary palette + light/dark colorScheme bindings; everything else inherits Aura) - PrimeVue runtime config (darkModeSelector='.dark', cssLayer=false, empty pt defaults scaffold) - Iconify (Tabler set, dash-naming convention) - useShellUiStore runtime writers (.dark class, data-density) - Workspace gradient palette (8 pairs, deterministic per org id) - Brand-square recipe (32px / rounded-lg / px-4 / centring equation) - Density axis (comfortable | compact, axis present but no component-level reaction yet — backlog DENSITY-AWARE-SPACING) Drift items flagged for Plan 4 (no fix in P7 — read-only audit): - Workspace gradient palette uses Tailwind palette anchors, not derivations of Crewli's #0D9394 - User-avatar gradient hardcodes #f472b6 (Tailwind pink) + a fallback #0d9488 that's NOT Crewli's #0D9394 - --topbar-h referenced with fallback only, never declared in :root - 'density' axis attribute set but no component spacing reacts to it Remaining token decisions (surface tones, focus-ring, radius scale, font-size scale, spacing rhythm, density-aware spacing, shadow scale, secondary palette) explicitly deferred to Plan 4 per RFC §6.4. Read-only audit: zero code files touched (verified via git status). Foundation document for Plan 4's template-layer token work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
33 KiB
Crewli Design Tokens — Audit & Decision Register
| Field | Value |
|---|---|
| Source design system | crewli-starter / PrimeVue Aura |
| Aura preset version | @primeuix/themes 4.5.x |
| Tailwind version | v4 (CSS-first @theme, no tailwind.config.ts) |
| Audit date | 2026-05-21 |
| Phase-1 decided tokens | Typography (AD-2.5-T1) |
| Plan reference | RFC-WS-PRIMEVUE-PLAN-2-5 §6 (Track B) |
| Related docs | PRIMEVUE_COMPONENTS.md, RFC-WS-PRIMEVUE-PLAN-2-5.md |
1. Purpose & scope
This document is the inventory and decision register for the design tokens currently live in the Crewli SPA. It is generated as Track B of RFC-WS-PRIMEVUE-PLAN-2-5 and serves as the foundation for Plan 4's template-layer token work.
What this doc does:
- Inventories every token source (Tailwind v4
@theme, PrimeVue Aura preset, component-level CSS variables, store-managed runtime attributes). - Classifies each token as Brand-essential, Bespoke, or Generic per the RFC §6.2 framework.
- Records the Typography Decision Register (AD-2.5-T1) end-to-end, including the historical Public Sans removal across both the CSS path and the webfontloader JS path.
- Flags drift, inconsistencies, and follow-up items for Plan 4.
What this doc does NOT do:
- Make new token decisions beyond Typography (per RFC §6.4, the rest is deferred to Plan 2.5b or absorbed into Plan 4).
- Refactor, consolidate, or move any token value. This is a read-only audit; the doc records state without changing it.
- Decide on density-aware component spacing, focus-ring tuning, surface-tone overrides, or radius-scale customization — all explicitly deferred.
If a token value changes after this audit date, update the relevant row + the Audit date field; do not silently let the inventory drift.
2. Classification scheme
Each token is one of (verbatim from RFC §6.2):
- Brand-essential — token carries Crewli brand identity. Keep current value. Examples:
--p-primary-color(teal), gradient definitions, Crewli wordmark colors. - Bespoke — token has a non-default value with deliberate design intent. Keep current value with rationale. Examples: the brand-square recipe (32px /
rounded-lg/px-4), the density axis attribute, custom topbar height. - Generic — token has a non-default value with no documented intent. Default decision: revert to framework default. Override requires explicit rationale.
3. Token sources
| Source | Type | Notes |
|---|---|---|
apps/app/src/assets/styles/tailwind.css |
Tailwind v4 @theme + :root |
Single source for Tailwind tokens. Houses --font-sans (@theme), --crewli-font-family (:root), and the @custom-variant dark selector binding. |
apps/app/src/plugins/primevue/theme.ts |
PrimeVue Aura preset (programmatic) | definePreset(Aura, {...}) — overrides ONLY the semantic.primary palette + the light/dark colorScheme bindings. Everything else inherits Aura defaults. |
apps/app/src/plugins/primevue/index.ts |
PrimeVue runtime config | darkModeSelector: '.dark', cssLayer: false, Dutch locale, empty pt defaults. |
apps/app/src/plugins/iconify.ts |
Iconify runtime | Tabler set eager-loaded; icon name convention tabler-<slug> (dash, NOT Iconify standard colon). |
apps/app/src/stores/useShellUiStore.ts |
Pinia store (runtime DOM attribute writer) | Writes <html class="dark"> (theme) and <html data-density="..."> (density) via applyDomAttributes(). |
apps/app/src/utils/v2/gradient.ts |
Component data | The 8-pair gradient palette consumed by the WorkspaceSwitcher avatar (per-org gradient via deterministic hash). |
Component scoped CSS (<style scoped> blocks) |
Per-component | Inset box-shadow recipe on the brand-square (RFC §7.4 last-resort — Tailwind has no inset directional utility). |
apps/app/src/@core/scss/template/libs/vuetify/_variables.scss |
Vuetify SCSS | Mirrors Inter via $font-family-custom so legacy Vuexy surfaces match the v2 shell during the F3–F6 window. |
4. Color tokens
4.1 Primary palette (Brand-essential)
Set by CrewliPreset in plugins/primevue/theme.ts. The full 11-step ramp is derived from Crewli teal #0D9394.
| Token | Value | Aura default | Classification | Decision | Note |
|---|---|---|---|---|---|
primary.50 |
#E6F4F4 |
emerald-50 | Brand-essential | Keep | Tinted teal scale; replaces Aura emerald. |
primary.100 |
#CCE9EA |
emerald-100 | Brand-essential | Keep | |
primary.200 |
#99D3D4 |
emerald-200 | Brand-essential | Keep | |
primary.300 |
#66BDBE |
emerald-300 | Brand-essential | Keep | |
primary.400 |
#33A7A8 |
emerald-400 | Brand-essential | Keep | |
primary.500 |
#0D9394 |
emerald-500 | Brand-essential | Keep | The Crewli teal. Single source of brand identity in the color system. |
primary.600 |
#0B7F80 |
emerald-600 | Brand-essential | Keep | Mirrors Vuetify's staticPrimaryDarkenColor (lock-stepped during F3–F6). |
primary.700 |
#086B6C |
emerald-700 | Brand-essential | Keep | |
primary.800 |
#055758 |
emerald-800 | Brand-essential | Keep | |
primary.900 |
#034344 |
emerald-900 | Brand-essential | Keep | |
primary.950 |
#012F30 |
emerald-950 | Brand-essential | Keep |
4.2 Color-scheme bindings (Brand-essential)
| Token | Value | Classification | Note |
|---|---|---|---|
light.primary.color |
{primary.500} |
Brand-essential | Crewli teal as light-mode primary. |
light.primary.contrastColor |
#ffffff |
Brand-essential | White text on teal. |
light.primary.hoverColor |
{primary.600} |
Brand-essential | One step darker on hover. |
light.primary.activeColor |
{primary.700} |
Brand-essential | |
dark.primary.color |
{primary.400} |
Brand-essential | Lighter teal on dark surfaces. |
dark.primary.contrastColor |
{surface.900} |
Brand-essential | Inherits Aura surface scale. |
dark.primary.hoverColor |
{primary.300} |
Brand-essential | |
dark.primary.activeColor |
{primary.200} |
Brand-essential |
4.3 Dark-mode mechanism (Bespoke)
| Token | Value | Classification | Note |
|---|---|---|---|
PrimeVue darkModeSelector |
.dark |
Bespoke | Class-based, NOT prefers-color-scheme. AD-2.5-D1. |
Tailwind @custom-variant dark |
(&:where(.dark, .dark *)) |
Bespoke | Pins Tailwind dark: variants to the same class. Without this, PrimeVue and Tailwind would divergent (PrimeVue listens to class, Tailwind default listens to OS). |
useShellUiStore.applyDomAttributes() write |
documentElement.classList.toggle('dark', theme==='dark') |
Bespoke | Runtime write — single source of truth for the class. |
4.4 Workspace gradient palette (Brand-essential pattern; values are data)
utils/v2/gradient.ts exports GRADIENT_PALETTE, an 8-pair list keyed by deterministic hash of org.id. Each entry is a [from, to] tuple for a 135° linear gradient.
| Index | From | To | Tailwind palette anchor |
|---|---|---|---|
| 0 | #0d9488 |
#0f766e |
teal-600 → teal-700 |
| 1 | #0891b2 |
#0e7490 |
cyan-600 → cyan-700 |
| 2 | #059669 |
#047857 |
emerald-600 → emerald-700 |
| 3 | #10b981 |
#059669 |
emerald-500 → emerald-600 |
| 4 | #0284c7 |
#0369a1 |
sky-600 → sky-700 |
| 5 | #14b8a6 |
#0d9488 |
teal-500 → teal-600 |
| 6 | #06b6d4 |
#0891b2 |
cyan-500 → cyan-600 |
| 7 | #34d399 |
#10b981 |
emerald-400 → emerald-500 |
Classification: the pattern (per-org deterministic gradient) is Brand-essential. The specific palette values are data, not core design tokens — they sit in component code. ⚠ Drift: the palette uses Tailwind palette anchors (teal/cyan/emerald/sky from Tailwind defaults) rather than derivations of Crewli's actual #0D9394. crewli-starter's reference markup uses pairs like #0D9394, #075F60 (Crewli teal-500/-700 in the new ramp). Flagged for Plan 4 — see §10.
4.5 Avatar gradient leftover (drift)
AppTopbar.vue user avatar pt-style:
background: linear-gradient(135deg, #f472b6, var(--p-primary-500, #0d9488));
| Aspect | Value | Note |
|---|---|---|
Hardcoded #f472b6 |
Tailwind pink-400 | Not a Crewli brand color. Vuexy-era leftover; mixed with brand teal in the gradient. |
Fallback #0d9488 |
Tailwind teal-600 | NOT the Crewli teal #0D9394. Inert today (the var() resolves), but masks the brand color if the CSS var ever fails. |
| Classification | Generic (drift) | Flagged for Plan 4 cleanup; not in P7 scope to change. |
5. Typography tokens + Decision Register
5.1 Live state
| Token | Value | Source | Classification | Decision |
|---|---|---|---|---|
Tailwind --font-sans (@theme) |
"Inter", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif |
apps/app/src/assets/styles/tailwind.css |
Brand-essential | Decided — AD-2.5-T1 |
:root --crewli-font-family |
same stack as above | apps/app/src/assets/styles/tailwind.css |
Brand-essential | Decided — AD-2.5-T1 |
html, body { font-family } |
var(--crewli-font-family) |
apps/app/src/assets/styles/tailwind.css |
Brand-essential | Decided — AD-2.5-T1 |
Vuetify $font-family-custom |
"Inter", ... |
apps/app/src/@core/scss/template/libs/vuetify/_variables.scss |
Brand-essential | Decided — AD-2.5-T1 |
| Inter weights loaded | 400 / 500 / 600 / 700 (via @fontsource/inter) |
apps/app/src/main.ts |
Brand-essential | Decided — AD-2.5-T1 |
| Inter load mechanism | Local NPM package (@fontsource/inter) — no CDN |
apps/app/src/main.ts |
Brand-essential | Decided — AD-2.5-T1 |
5.2 Decision Register — AD-2.5-T1 (Inter via @fontsource/inter)
Decision: Inter is Crewli's sans-serif typography across PrimeVue, Tailwind, and Vuetify surfaces. Loaded locally via @fontsource/inter (weights 400 / 500 / 600 / 700). No Google Fonts CDN, no CDN of any kind for the body font.
Status: complete (decided + implemented + regression-locked).
Rationale:
- Inter is PrimeVue's de-facto reference font — using it produces the cleanest visual default across the component set.
- Local loading avoids external Google Fonts requests, which carry GDPR/privacy implications and add a third-party network dependency to first paint.
- Single source — one font, three frameworks all reading from the same
--crewli-font-familystack — prevents the dual-path drift that the initial Plan 2.5 P2 audit missed (see §5.3).
History:
| Date / commit | Event |
|---|---|
| Pre-Plan 2.5 | Codebase loaded Public Sans via two independent paths: (a) CSS font-family declarations in tailwind.css and Vuexy _variables.scss; (b) a JS webfontloader plugin (apps/app/src/plugins/webfontloader.ts) that fetched Public+Sans:...&display=swap from the Google Fonts CDN at runtime. The two paths were independent; neither knew about the other. |
Plan 2.5 P2 (commit 41af1801) |
CSS path reverted to Inter. tailwind.css @theme --font-sans + :root --crewli-font-family + the html, body cascade all changed to the Inter stack. Vuetify $font-family-custom matched. Regression-lock spec tests/unit/styles/typography.spec.ts added — but it only inspected CSS file contents. |
| P5 manual smoke (2026-05-20) | The live DOM still carried wf-publicsans-n4-active wf-active classes on <html>. webfontloader was still fetching Public Sans from Google Fonts. The CSS-only regression-lock had missed the JS path. |
P2-followup (commit 641ca513) |
The whole webfontloader.ts plugin removed (it loaded only Public Sans — no other families). webfontloader@1.6.28 + @types/webfontloader@1.6.38 removed from package.json. Regression-lock strengthened: typography.spec.ts now also scans every file in src/plugins/ for any reference to webfontloader, WebFont.load, Public Sans (any spelling), or fonts.googleapis.com. The plugin's auto-registration via Vuexy's registerPlugins() glob is severed by the file deletion alone — no manual unregister site to clean up. |
Regression lock (current state):
| Spec | Asserts |
|---|---|
tests/unit/styles/typography.spec.ts — Typography regression lock (AD-2.5-T1) describe |
tailwind.css declares Inter first in every direct font stack; Vuexy $font-family-custom declares Inter first; no Public Sans (any case/separator) remains in either CSS file. |
tests/unit/styles/typography.spec.ts — JS font-loading path (AD-2.5-T1 completion) describe |
No file under src/plugins/ references webfontloader, WebFont.load, Public Sans (any case/separator), or fonts.googleapis.com; the webfontloader.ts plugin file does NOT exist. |
If a future PR re-introduces any of these (a Vuexy template port that loads a Google Font, a Storybook addon that lazy-loads a webfontloader path, etc.) the spec catches it before merge.
5.3 Why Typography is the only fully-decided Phase-1 token
Per RFC §6.4: font choice has the largest visual surface impact (every text element in the app), the regression-lock pattern needed an anchor reference, and reverting from Public Sans to Inter was already a clear correctness call. Other tokens (focus-ring width, surface tones, density-aware spacing) are localized and benefit from waiting until more pages exist so visual impact is observable.
6. Spacing / sizing / radius tokens
6.1 The brand-square recipe (Bespoke)
The most consequential bespoke layout invariant in the shell. Encoded across SidebarHeader + WorkspaceSwitcher in P6-styling commits.
| Property | Value | Note |
|---|---|---|
| Square size | w-8 h-8 |
32px × 32px. Applies to the SidebarHeader brand mark AND the WorkspaceSwitcher avatar. |
| Square radius | rounded-lg |
8px. Set after manual smoke rejected rounded-xl (12px) as too soft. |
| Wrapper height | h-[56px] |
Matches the topbar height; applied to the SidebarHeader brand row AND the WorkspaceSwitcher wrapper in collapsed state. |
| Wrapper horizontal padding | px-4 |
16px each side. Constant across both states for both wrappers (collapse no longer toggles justify-center/px-0). |
| Inset shadow | inset 0 -2px 0 rgb(0 0 0 / 8%) |
Scoped CSS in both components — no Tailwind utility for inset directional shadow at this granularity (RFC §7.4 last-resort). |
Centring equation: collapsed rail (64px) = square (32px) + 2 × px-4 (2 × 16px). A left-aligned 32px square inside a px-4 row is also centred when the row is 64px wide. This is what keeps the brand logo stationary through the rail's width animation — no justify-content re-centring during the transition.
6.2 Sidebar rail widths (Bespoke)
| Token | Value | Classification | Note |
|---|---|---|---|
| Expanded rail width | w-64 (256px) |
Bespoke | Matches crewli-starter's --sidebar-w. |
| Collapsed rail width | w-16 (64px) |
Bespoke | Matches crewli-starter's --sidebar-w-collapsed. Locked to the brand-square recipe (see §6.1). |
| Width transition | transition-[width] duration-200 |
Bespoke | 200ms ease on the aside. |
| Aside overflow | overflow-hidden |
Bespoke | Clips content during the width animation — prevents whitespace-nowrap wordmark overflow. |
6.3 Topbar height (Bespoke CSS var with fallback)
| Token | Value | Note |
|---|---|---|
--topbar-h |
var(--topbar-h, 56px) |
Consumed in AppTopbar's pt.root class. ⚠ Not declared in :root anywhere — only used with fallback. Effectively a hard-coded 56px with the option of being overridden. Worth either declaring properly or removing the var indirection in Plan 4. |
6.4 Other component sizes (Generic — Tailwind defaults)
The shell uses Tailwind defaults for the rest: h-[38px] for icon buttons (search, density, dark, notifications) is a one-off literal; w-7 h-7 for the inline collapse chevron; gap-[10px] between brand row children; etc. None of these carry brand intent — they're sized to match crewli-starter visual reference and otherwise inherit Tailwind's default scale. Classified Generic; Plan 4 may consolidate via a --icon-btn-size token if patterns recur.
6.5 PrimeVue surface tones, focus-ring, border-radius scale (DEFERRED)
Inherited from Aura defaults — no overrides in CrewliPreset. Surface scales ({surface.0} … {surface.950}), focus-ring width/style, default radius (var(--p-border-radius)) all come from @primeuix/themes/aura as-shipped. Plan 2.5 made no decisions here; deferred to Plan 4 (see §9).
7. Density tokens (Bespoke)
| Token | Value | Source | Note |
|---|---|---|---|
density enum |
'comfortable' | 'compact' (NOT comfy) |
useShellUiStore.ts |
Binary. The earlier RFC draft referenced a comfy value — incorrect; the runtime type is comfortable. |
<html data-density> |
written by applyDomAttributes() |
useShellUiStore.ts |
Single DOM read for any consumer that wants to react to density. |
toggleDensity() action |
binary flip + sync applyDomAttributes |
useShellUiStore.ts (P6 Fix 10) |
Topbar button delegates here. |
| Component-level reaction | none yet | — | No component CSS currently keys off [data-density=compact]. The attribute is set; the component-level spacing-aware styles are deferred (backlog DENSITY-AWARE-SPACING). |
Classification: the axis itself is Bespoke (Crewli design intent — toggle between two interface densities). The component-level spacing recipes that respond to it are deferred and become Plan 4 work.
8. Icon tokens (Bespoke convention)
| Token | Value | Note |
|---|---|---|
| Icon naming | tabler-<slug> |
Dash-separated. Deviates from Iconify standard tabler:<slug> (colon) because Crewli's Icon.vue bridge expects dashes — a Vuexy-era convention preserved for the v1→v2 bridge. |
| Iconify collection | @iconify-json/tabler |
Full set eager-loaded at app boot (addCollection in plugins/iconify.ts). |
| Default size | per-call (:size="N") |
No global icon-size token. Each call sets its own size. |
Classification: dash convention is Bespoke (a documented Crewli deviation); the set choice (Tabler) and the eager-load strategy are Bespoke decisions (size trade-off accepted for CSP compatibility — Iconify's runtime CDN is blocked).
9. Classification summary
| Bucket | Approximate count | What this means for Plan 4 |
|---|---|---|
| Brand-essential | ~20 (full primary palette + scheme bindings + Typography stack) | Sacred. Plan 4 should not change these without explicit brand-team sign-off. Inter and #0D9394 ARE Crewli. |
| Bespoke | ~10 (dark-mode mechanism, brand-square recipe, rail widths, density axis, icon-dash convention, topbar height) | Intentional Crewli deviations from defaults. Plan 4 may refactor / consolidate, but the rationale stays. Each Bespoke row in this doc carries a one-line "why". |
| Generic | ~everything else (full Aura surface scale, default border radius, focus ring, font-size scale, spacing scale, default shadows, default transitions) | These are framework defaults used as-is. Plan 4 decides which of these to override with Crewli-specific values. Default decision per RFC §6.2 is "revert to default" unless rationale exists. |
10. Drift + follow-up items for Plan 4
These are not P7 decisions; they're flagged here so they don't get lost.
| Item | Severity | Source | Recommended action |
|---|---|---|---|
Workspace gradient palette uses Tailwind palette anchors (teal-500/600, cyan-500/600, emerald, sky) rather than derivations of Crewli's primary #0D9394. |
Low (visual; data tier) | utils/v2/gradient.ts line 14 |
Re-derive the 8 pairs from the Crewli primary ramp (primary.500/primary.700 plus complementary tints from a Crewli secondary palette, when Plan 4 defines one). |
User-avatar gradient hardcodes #f472b6 (Tailwind pink-400) mixed with var(--p-primary-500). |
Medium (off-brand color baked into chrome) | AppTopbar.vue line 385 + 400 |
Replace pink with a Crewli secondary/accent (Plan 4 decision). At minimum, drop the var fallback #0d9488 which is NOT the Crewli teal (#0D9394). |
--topbar-h referenced with fallback only, never declared in :root. |
Low (correctness) | AppTopbar.vue line 225 |
Either declare :root { --topbar-h: 56px; } in tailwind.css, or replace the var(--topbar-h, 56px) with the literal h-14. The current form is a half-finished abstraction. |
density axis is set on <html> but no component spacing reacts to it. |
Medium (feature incomplete) | useShellUiStore.ts + every shell component |
Plan 4 picks the spacing surfaces that should compress in compact mode (typically row heights in tables, sidebar nav item padding, topbar height). Tracked as DENSITY-AWARE-SPACING. |
PrimeVue pt defaults file is empty. |
None (scaffold, by design) | apps/app/src/plugins/primevue/defaults.ts |
F4 sub-packages populate as each Vuetify surface migrates. Not an immediate Plan 4 concern, but worth knowing this central override site exists. |
The --crewli-font-family CSS variable duplicates the Tailwind @theme --font-sans stack. |
Low (redundancy) | tailwind.css |
Intentional twin — @theme feeds Tailwind utilities; --crewli-font-family is the cascade-level named alias for non-Tailwind consumers (PrimeVue, Vuetify body inheritance). Plan 4 could collapse to a single source if the two-step is no longer needed after Vuetify is removed. |
11. Deferred to Plan 4
Per RFC §6.4, Plan 2.5 deliberately stops Phase-1 at Typography. The following are NOT decided in this audit:
| Deferred area | Why deferred | Where it'll land |
|---|---|---|
Surface tone scale (--p-surface-*) |
Localized impact; needs page surfaces beyond the dashboard to evaluate. | Plan 4 (or Plan 2.5b if priority bumps). |
| Focus-ring width / color | Aura default works visually; needs accessibility audit before customization. | Plan 4 + accessibility pass. |
Border-radius scale (--p-border-radius family) |
Single-value change cascades through every rounded surface. Wait until more components are migrated. | Plan 4. |
| Font-size scale (root rem base) | Touches the typographic system; needs Plan 4's full type-scale design. | Plan 4. |
Spacing rhythm (--p-spacing-*) |
Plan 2.5 used Tailwind defaults; Plan 4 may introduce Crewli-specific spacing tokens. | Plan 4. |
| Density-aware component spacing | The axis exists (<html data-density>) but nothing reads it. Plan 4 picks the surfaces. |
Plan 4 (DENSITY-AWARE-SPACING). |
| Shadow scale | Aura defaults; one bespoke inset-shadow exception on the brand square (RFC §7.4 last-resort). | Plan 4 if shadow recipes recur. |
| Secondary / accent color palette | Crewli's brand currently has only the teal primary defined. A secondary is needed for callouts (e.g., the off-brand pink in §10). | Plan 4 + brand-team input. |
| Workspace gradient palette re-derivation | See §10. | Plan 4. |
12. Maintenance
When a token decision lands in a future PR:
- Find the row in this doc; update the Decision column and the Note column.
- Add a new row to §5.2-style "Decision Register" if the token gets its own register entry.
- Update the Audit date field at the top.
- Add the corresponding regression-lock spec if the decision is enforceable in code (CSS file contents, computed style, store action). Cross-link the spec path here.
When a new token source appears (e.g., a pt default block populates):
- Add the source to §3.
- Inventory its tokens in the relevant section.
- Classify each one.
Drift catches: if a follow-up audit finds a token in the codebase not listed here, that's the doc to fix, not the codebase.