Files
Project-SDE-WP-Suite/docs/reference/tokens.md
n.siegfried 12c0e5ca74 Wave 3 exit - record what the wave actually changed on screen
wave-3.md asks for "no unintended visual change on any page versus wave 2".
Measured with tests/token_check.py, b105135 against the end of the wave, all 7
pages: 7 elements changed and all 7 are the fixes the wave was for - two
.user-pick selects going white (T3.4) and five green action buttons going blue
(T3.5). The other 3,493 elements compute exactly what they computed at the end of
wave 2, and all 178 wave-2 token names resolve to the same literals.

Three fixes are invisible to a computed-style diff and are recorded with how they
were verified instead: two green buttons that need application state the fixture
does not reach, and the sentence-case pass, which changes text rather than style.

The one exit criterion not met is carried forward explicitly rather than ticked:
T3.2's "no page stylesheet declares a raw spacing or type value" is complete for
colour and open for spacing and type (BL-010).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 19:13:42 -05:00

60 KiB
Raw Blame History

Token inventory — every declared and raw value in html/

Task: T3.1, updated by T3.2 · Items: C3, S5 · Produced: August 15, 2026 Branch: feat/wp-suite-r2-implementation · Read by: T3.2, T3.5, T9.3

T3.1 produced this as the mapping table T3.2 builds from; T3.2 has since been built and §2 and §11 record what actually shipped rather than what was proposed. Sections 3 to 10 are the pre-T3.2 inventory and are deliberately left as they were — they are the evidence for the refactor, and rewriting them into the past tense would destroy the before-picture that T9.3 needs. Everything in them was read from the working tree at b105135 (end of wave 2).

Read §8 Near-duplicates before writing a single line of T3.2. That section is the whole risk in this wave.


0. The rule this wave is enforcing

From CLAUDE.md:

After wave 3 there is exactly one place a color, spacing or type value is defined. Page stylesheets alias that source and declare nothing new.

And the constraint T3.2 adds on top:

Do not change any rendered value in this task.

Those two together mean one thing that has to be said out loud, because it is the difference between a clean wave 3 and a broken one:

Consolidation is not unification.

Where two sheets declare the same value under different names, T3.2 collapses them to one declaration and aliases the names. That is free — nothing renders differently.

Where two sheets declare different values for the same role — --shadow-lg, the two banner-border greens, the three mono stacks — T3.2 gives each value its own canonical name and records the pair in §8. Collapsing those is a visual change, which T3.2 forbids. It belongs to T3.3/T3.4/T3.5 or to backlog.md.

A "consolidation" that quietly picks a winner between two near-identical greys is exactly the failure this wave exists to prevent.


1. Scope: what "four token systems" actually means

wave-3.md:22 and IMPLEMENTATION.md both say "the four stylesheets". Per D1 in docs/reference/file-map.md there are six, and after T2.2 a seventh surface matters: the launcher's inline <style>.

# Surface Lines Custom props declared In T3.2 scope?
1 theme-light.css 250 120 (114 --cds-*, 6 --wp-appbar-*) yes — becomes the source
2 console.css 192 28 unprefixed at :root yes — aliases
3 work-package-suite-styles.css 663 16 unprefixed at :root yes — aliases
4 wp-creation-styles.css 897 24 (21 at :root, 3 --nav-w on body) yes — aliases
5 wp-chrome.css 249 14 --wpc-*, scoped to .wp-chrome, none at :root yes — aliases
6 wp-sidenav.css 109 0 — every colour is a hardcoded hex yes — 31 raw values
7 index.html inline <style> (17313) 296 0 — already consumes --cds-* yes — 12 raw values, 7 of them dead

202 custom-property declarations in total. Plus the raw literals in §5.

Corrected during T3.2: row 7 is not the only inline <style>. login.html (3 literals) and field.html (26 literals, including the ninth amber at §8-K) carry one too, and work-package-suite.html:407 has a colour in a style= attribute. All four were converted. Counting only the launcher's was an inventory miss, not a scoping decision.

Out of T3.2 scope, stated so nobody goes looking

Surface Why it is out Where it goes
help.js:79 injected sheet — 52 colour literals, zero var() It is a whole parallel palette in a different design language (slate #27313f/#334155, violet #7c3aed/#f3e8ff, its own blue rgba(37,99,214,.15)). Converting it is a restyle, not a consolidation, and it would break T3.2's empty-diff done-when. BL-004
wp-creation-app.js:1257 print window w.document.write() builds a separate document that loads none of the suite's stylesheets. var() cannot resolve there. Genuine one-off. stays raw, documented in §7
wp-creation-app.js:1389 body{--nav-w:288px} Root cause of BL-001; T7.1 owns it. Touching it here changes the creator's layout at 390px, which is a visual change. T7.1
auth-guard.js:67-92 password modal + wp-format.js:120-150 prefs modal, inline styles 35 raw literals (16 + 19) in style= attributes, not a stylesheet. T2.2 already replaced the nav half of this markup; both modals are rebuilt under C1 at T9.5. BL-005
work-package-suite-app.js, admin.js, users.js inline style.* They consume page tokens (var(--muted), var(--red), var(--s2) …) rather than declaring them. See §9 — this is why names must survive as aliases. no change needed

2. The canonical set — as built (T3.2)

theme-light.css is already the source of truth in everything but name: it is linked first on all 7 pages, it holds 114 of the 202 declarations, and its values are real Carbon g10. T3.2 should make that official rather than invent a new file.

Two namespaces, and the split is not cosmetic — it records provenance:

  • --cds-* — values that are genuine IBM Carbon g10. Unchanged, plus the two additions below which are also real Carbon token names that this sheet simply never declared.
  • --wp-* — roles Carbon does not define, which the suite invented. Naming these separately is what stops the next person "correcting" #8e6a00 to a Carbon yellow.

2a. Additions to --cds-* (Carbon values, currently redeclared per sheet)

New canonical token Value Carbon name Replaces
--cds-highlight #edf5ff $highlight / blue-10 --accent-soft, --primary-light, --accent-dim (3 sheets)
--cds-hover-support-success #0e6027 green-70 5 raw literals, declared nowhere today

2b. Additions under --wp-* (suite inventions, no Carbon equivalent)

New canonical token Value Why it is not Carbon Replaces
--wp-status-success-bg #defbe6 green-10 is Carbon; the role is not ($notification-background-success is a different construct) --green-bg, --accent-green-dim
--wp-status-error-bg #fff1f1 as above (red-10) --red-bg, --red-dim
--wp-status-warning-bg #fdf6dd Carbon yellow-10 is #fcf4d6this is not it --amber-bg, --warning-bg, wp-chrome.css:50, index.html:298
--wp-status-warning-text #8e6a00 no Carbon amber text token exists; this is the suite's accessible-on-#fdf6dd amber --amber, --warning, --accent-amber
--wp-font-sans see §6 6 sites, 2 variants
--wp-font-mono see §6 4 sites, 3 variants
--wp-s1--wp-s6 4 8 12 16 20 28 px console.css --s1..--s6; the only real spacing scale in the repo
--wp-ctl, --wp-ctl-sm 32px, 26px console.css:19-20
--wp-radius-0 0 --radius, and 34 literal border-radius: 0

2c. Kept, and kept separate, because their values differ

These are not duplicates. Each gets a canonical name; nobody collapses them in T3.2. See §8 for the decision each one is waiting on.

Canonical token Value Used by Paired with
--wp-shadow-lg-neutral 0 4px 16px rgba(0,0,0,.16) wizard --shadow-lg --wp-shadow-lg-cool
--wp-shadow-lg-cool 0 4px 16px rgba(20,30,50,.12) creator --shadow-lg --wp-shadow-lg-neutral
--wp-status-success-border-a #a7f0ba (green-20, Carbon) console.css:82 -b
--wp-status-success-border-b #b6e3c6 (not Carbon) wp-creation-styles.css:494,532 -a

2d. What T3.2 actually built, where it differs from the above

theme-light.css now holds 191 declarations: the 114 Carbon g10 values, --cds-highlight, the 7 app-shell tokens, and 69 --wp-* suite tokens. It is the only sheet in html/ that contains a colour literal. Two changes from §2a's proposal, both for the same reason — a token whose value is Carbon but whose role Carbon does not define belongs in --wp-*, or the namespace split stops meaning anything:

Proposed in T3.1 Built as Why
--cds-hover-support-success --wp-hover-success #0e6027 is Carbon green-70, but "hover for a green fill" is not a Carbon role — green is not one of its action colours
--cds-layer-inverse-01 --wp-appbar-layer #262626 is Carbon gray-90, but the name was invented; it sits in the app-shell group where both its consumers live

Tokens added beyond §2a/§2b, all of them one-offs that still needed a single home — "one place a value is defined" applies to a value used once just as much as to one used forty times:

Group Tokens
status borders --wp-status-{success,error,warning}-border-{a,b,c} — 7
surfaces --wp-table-zebra, --wp-table-row-hover, --wp-nav-bg, --wp-nav-hover, --wp-nav-active, --wp-pop-divider, --wp-accent-soft-hover
elevation --wp-shadow-{pop,menu,menu-lg,modal,modal-lg,gate,rail,drawer,sticky,navbar,tooltip,toast} — 12
scrims --wp-scrim-{drawer,cool,cool-modal,cool-strong,neutral,loading,frosted}, --wp-on-accent-divider
type --wp-font-sans, --wp-font-sans-2, --wp-font-mono, --wp-font-mono-2, --wp-font-mono-3
spacing / shape --wp-s1--wp-s6, --wp-ctl, --wp-ctl-sm, --wp-radius-0, --wp-shadow-none
one-offs --wp-tag-super-{bg,text}, --wp-term-{bg,fg,pass,fail}, --wp-dev-{bg,fg,rule}, --wp-sop-inherited-bg, --wp-status-warning-text-alt

Twelve distinct shadows is the number that surprised. They were invisible while spread across four sheets as inline box-shadow values; collected in one block they are obviously eleven more than a flat Carbon UI wants. Collapsing them is a rendered change, so not here — but they are now countable, which they were not before.


3. Declaration inventory — theme-light.css (the source)

All 120 stay exactly as they are. Grouped by role here so the shape is readable; every one of the 120 is listed individually with its line number in Appendix A. The file is a flat :root block at lines 2117 and 169176.

Lines Group Count Distinct values Action in T3.2
36 --cds-interactive-01..04 4 #0f62fe ×3, #393939 keep
712 --cds-ui-background, --cds-ui-01..05 6 #f4f4f4 ×2, #ffffff, #e0e0e0, #8d8d8d, #161616 keep
1318 --cds-text-01..05, --cds-text-error 6 #161616 #525252 #a8a8a8 #ffffff #6f6f6f #da1e28 keep
1921 --cds-icon-01..03 3 #161616 #525252 #ffffff keep
2224 --cds-link-01, -02, --cds-inverse-link 3 #0f62fe #0043ce #78a9ff keep
2528 --cds-field-01/02, --cds-inverse-01/02 4 #ffffff #f4f4f4 #ffffff #393939 keep
2936 --cds-support-01..04 + inverse 8 #da1e28 #198038 #f1c21b #0043ce #fa4d56 #42be65 #f1c21b #4589ff keep
3746 overlay, danger, focus, hover/active primary·secondary·tertiary 10 incl. #0353e9 ×2, #002d9c ×2 keep
4761 hover/active/selected UI, disabled 15 #e5e5e5 ×4, #cacaca ×2, #c6c6c6, #353535, #f4f4f4 ×2, #bdbdbd, #8d8d8d, #ba121b, #750e13, #0043ce keep
6266 --cds-button-* 5 #0f62fe ×2, #393939, #da1e28 ×2 keep
6789 v11 background / layer / field / border set 23 mostly aliases of the above literals keep
90115 v11 text / link / icon / support / interactive set 26 mostly aliases of the above literals keep
116 --cds-shadow 1 rgba(0,0,0,.16) keep — see §8-F
170175 --wp-appbar-bg/fg/fg-dim/border/hover/height 6 #161616 #ffffff #c6c6c6 #6f6f6f #353535 48px keep

Note for T9.3: theme-light.css re-declares the same literal under many names by design — that is Carbon's v10→v11 alias layer (--cds-ui-01 and --cds-layer are both #ffffff, deliberately). Those are not the S5 defect and must not be "deduplicated". The S5 defect is the same literal declared in different sheets, which is §4.

One raw literal in this file: theme-light.css:203 background: #fff on .wp-logo-chip. → --cds-ui-01. (The chip is a deliberate white plate under a white-background wordmark; the value is white for a reason, but it should still read from the token.)


4. Declaration inventory — the five page sheets

Every one of these is a duplicate declaration of a value theme-light.css already holds, unless flagged. Action is what T3.2 writes.

4a. console.css — 28 declarations, lines 1220

Line Token Value Canonical Action
12 --bg #f4f4f4 --cds-background alias
12 --surface #fff --cds-layer alias — notation-only dupe, see §8-G
12 --border #e0e0e0 --cds-border-subtle alias
12 --border-strong #8d8d8d --cds-border-strong alias
12 --text #161616 --cds-text-primary alias
13 --muted #525252 --cds-text-secondary alias
13 --dim #8d8d8d --cds-ui-04 alias — contrast issue is T4.6, not here
13 --accent #0f62fe --cds-interactive-01 alias — missed by the wave-0 count, see §10
13 --accent-hover #0353e9 --cds-hover-primary alias
13 --accent-soft #edf5ff --cds-highlight (new) alias
14 --green #198038 --cds-support-success alias
14 --green-bg #defbe6 --wp-status-success-bg (new) alias
15 --red #da1e28 --cds-support-error alias
15 --red-bg #fff1f1 --wp-status-error-bg (new) alias
15 --amber #8e6a00 --wp-status-warning-text (new) alias
15 --amber-bg #fdf6dd --wp-status-warning-bg (new) alias
16 --head-bg #f4f4f4 --cds-layer-accent alias
16 --zebra #fafafa one-off, §8-A. Not a Carbon grey; a 4-step-lighter zebra stripe
16 --row-hover #eef0f2 one-off, §8-B. Deliberately not --accent-soft; see the comment at :115-116
17 --mono 'IBM Plex Mono','Cascadia Mono',Consolas,monospace --wp-font-mono value differs, §8-H
18 --s1--s6 4 8 12 16 20 28px --wp-s1..6 (new) promote — this is the canonical spacing scale
1920 --ctl, --ctl-sm 32px, 26px --wp-ctl, --wp-ctl-sm (new) promote

Lines 85 and 115 contain --dim / --accent-soft inside comments — a naïve grep '--x:' counts them. They are prose, not declarations.

4b. work-package-suite-styles.css — 16 declarations, lines 217

Line Token Value Canonical Action
2 --primary #0f62fe --cds-interactive-01 alias
3 --primary-light #edf5ff --cds-highlight (new) alias
4 --success #198038 --cds-support-success alias
5 --warning #8e6a00 --wp-status-warning-text (new) alias
6 --warning-bg #fdf6dd --wp-status-warning-bg (new) alias
7 --danger #da1e28 --cds-support-error alias
8 --text #161616 --cds-text-primary alias
9 --text-light #525252 --cds-text-secondary alias
10 --text-dim #8d8d8d --cds-ui-04 alias — T4.6 re-measures this, do not change the value in wave 3
11 --border #e0e0e0 --cds-border-subtle alias
12 --border-strong #8d8d8d --cds-border-strong alias
13 --bg #f4f4f4 --cds-background alias
14 --bg-card #ffffff --cds-layer alias
15 --appbar #161616 --wp-appbar-bg alias — file-map called this out; it is the same value under a second name
16 --shadow none --wp-shadow-none alias — and it is a no-op, see §7
17 --shadow-lg 0 4px 16px rgba(0,0,0,0.16) --wp-shadow-lg-neutral value differs from the creator's, §8-F

Line 340 mentions --cds-field: #ffffff inside the T1.5 comment. Prose, not a declaration.

4c. wp-creation-styles.css — 24 declarations (21 at :root lines 1030, 3 on body)

Line Token Value Canonical Action
10 --bg #f4f4f4 --cds-background alias
11 --surface #ffffff --cds-layer alias
12 --surface2 #f4f4f4 --cds-layer-accent alias
13 --border #e0e0e0 --cds-border-subtle alias
14 --border-strong #8d8d8d --cds-border-strong alias
15 --text #161616 --cds-text-primary alias
16 --text-muted #525252 --cds-text-secondary alias
17 --text-dim #8d8d8d --cds-ui-04 alias — applied at 10px on :374; T4.6 owns the contrast
18 --accent #0f62fe --cds-interactive-01 alias
19 --accent-dim #edf5ff --cds-highlight (new) alias
20 --accent-green #198038 --cds-support-success alias
21 --accent-green-dim #defbe6 --wp-status-success-bg (new) alias
22 --accent-amber #8e6a00 --wp-status-warning-text (new) alias
23 --accent-amber-dim #fdf6dd --wp-status-warning-bg (new) alias
24 --red #da1e28 --cds-support-error alias
25 --red-dim #fff1f1 --wp-status-error-bg (new) alias
26 --radius 0 --wp-radius-0 (new) alias — and contradicted 45×, see §7
27 --shadow none --wp-shadow-none alias — no-op, §7
28 --shadow-lg 0 4px 16px rgba(20,30,50,.12) --wp-shadow-lg-cool value differs, §8-F
29 --mono 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', 'Segoe UI Mono', Consolas, monospace --wp-font-mono the fullest stack — adopt this one, §8-H
30 --sans 'IBM Plex Sans', …, 'Helvetica Neue', sans-serif --wp-font-sans alias, §8-H
685 --nav-w (on body) 288px leave alone — T7.1/BL-001
686 --nav-w (body.wp-nav-collapsed) 56px leave alone
817 --nav-w (@media ≤860px) 56px leave alone

4d. wp-chrome.css — 14 declarations, scoped to .wp-chrome (lines 1925, 2935)

These are not :root declarations. They are a two-value theme switch: the same seven role names resolve light or dark depending on [data-bar="dark"]. That pattern is correct and should survive; only the literals move behind tokens.

Line Token Light value → canonical Line Dark value → canonical
19 --wpc-fg #161616--cds-text-primary 29 #ffffff--wp-appbar-fg
20 --wpc-fg-dim #525252--cds-text-secondary 30 #c6c6c6--wp-appbar-fg-dim
21 --wpc-bg #ffffff--cds-layer 31 #262626one-off, §8-J
22 --wpc-bg-soft #f4f4f4--cds-layer-accent 32 #393939--cds-interactive-02
23 --wpc-border #c6c6c6--cds-border-subtle-selected 33 #6f6f6f--wp-appbar-border
24 --wpc-hover #e8e8e8--cds-layer-hover 34 #353535--wp-appbar-hover
25 --wpc-accent #0f62fe--cds-interactive-01 35 #78a9ff--cds-link-inverse

4e. wp-sidenav.css — 0 declarations, 31 raw literals

Every colour is hardcoded. The header comment says why — "Colours come from the dark app bar it hangs off … not from theme-light.css" — but the values are all Carbon greys that theme-light.css already holds, so the reasoning ("match the bar") and the mechanism ("hardcode it") disagree. Aliasing them to the appbar tokens makes the stated intent true.

Value Sites Canonical
#161616 :35 and the :8 comment --wp-appbar-bg
#f4f4f4 :15, :35, :68, :89, :108 --cds-text-inverse
#353535 :17, :77, :92 --wp-appbar-hover
#ffffff :18, :93 (focus rings), :77 --wp-appbar-fg
#e8e8e8 :21 (light-bar hover) --cds-layer-hover
rgba(22,22,22,.55) :25 scrim near-dupe of --cds-overlay rgba(22,22,22,.5) — §8-F
rgba(0,0,0,.4) :38 drawer shadow one-off, §7
#393939 :49, :59, :103 (rules) --cds-inverse-02
#a8a8a8 :53, :65, :100, :106 --cds-text-placeholder
#c6c6c6 :71, :74, :96 --wp-appbar-fg-dim
#8d8d8d :82 --cds-ui-04
#262626 :94 current-row fill one-off, §8-J
#0f62fe :94 current-row rule --cds-interactive-01
#78a9ff :98 current-row icon --cds-link-inverse

4f. index.html inline <style> — 0 declarations, 6 raw literals

The block already consumes --cds-* throughout; these six are the stragglers.

12 literals: 5 live, 7 dead.

Line Literal Context Canonical
126 #0e6027 .card.complete .card-button:hover --cds-hover-support-success (new)
291 #fff .proj-row select background --cds-field
298 #fdf6dd / #f1c21b / #8e6a00 .proj-archived-note — the amber triple --wp-status-warning-bg / --cds-support-warning / --wp-status-warning-text
291, 292 ×2, 300 ×2, 303, 305 #8d8d8d ×3, #fff ×2, #e0e0e0, #0f62fe var() fallbacks only — 7 of them delete the fallbacks — see §7

index.html:13 <meta name="theme-color" content="#161616"> is markup, not CSS. It cannot take a var(). Leave it; note it in §7.


5. Raw literals used in rules, by sheet

Counts exclude declarations and comments — measured by stripping /* … */ first, then subtracting literals that sit inside a --x: declaration. Full site lists are in §4e/§4f for the two sheets that are all-raw; the rest are itemised here.

Sheet Raw literals in rules The ones that are not already a token
theme-light.css 1 :203 #fff
console.css 19 #a7f0ba :82 · #ffd7d9 :83 · #fddc69 :84 · #0f1525 #d7e0f5 :90 · #56d364 #ff7b72 :92 · #e8daff #6929c4 :145 · #e8e8e8 :146 · rgba(20,30,50,.5) :158 · rgba(20,30,50,.3) :161 · rgba(20,30,50,.12) :174 · #fff ×6 (:49 :52 :53 :54 :67 :138)
work-package-suite-styles.css 15 #fff :43 :78 :105 :133 :562 · #c6c6c6 :111 :150 · #6f6f6f :134 :144 :149 · #353535 :143 · #0353e9 :476 (a dead var() fallback) · #0e6027 ×2 :498 · rgba(0,0,0,0.5) :606
wp-chrome.css 22 the whole .wpc-pop block :125-180 is raw (#fff #161616 #e0e0e0 #6f6f6f #f0f0f0 #f4f4f4 #edf5ff #0f62fe #c6c6c6) plus the archived banner :50-52
wp-creation-styles.css 68 see below
wp-sidenav.css 30 (a 31st is in the header comment) §4e
index.html inline 12 (5 live, 7 dead) §4f

The creator's raw literals — the largest group

Grouped by what they are, because they need different treatment:

Already-tokenised values written raw — straight substitution, zero risk: #fff ×19 (:248 :262 :264 :372 :385 :389 :446 :471 :498 :504 :505 :506 :517 :536 :618 :706 :715 :784 :823) → --cds-text-on-color / --cds-layer; #0353e9 ×3 (:263 :711 :718) → --cds-hover-primary; #0e6027 ×2 (:265 :447) → --cds-hover-support-success; #161616 ×2 (:618 :623, the .sop-chip tooltip) → --cds-ui-05; #0043ce (:231) → --cds-link-secondary.

Accent tints with no token — §8-C, do not merge: #b9d2fb ×4 (:230 :449 :614 :636) · #cdd9f2 ×2 (:515 :581) · #e2ecfc (:582).

var() fallbacks that can never fire — the token is declared at :root in the same sheet, so the fallback is dead code. §7. var(--accent-dim,#eef3fd) :581 :839 · var(--accent-dim,#eaf0fd) :515 · var(--accent,#0f62fe) :854.

Planner-style neutral greys, no token — §8-B, do not merge: #fbfbfc (:680) · #eef0f3 ×3 (:696 :740 :773) · #e8eaed ×2 (:741 :774).

Status borders, no token — §8-D: #b6e3c6 ×2 (:494 :532) · #f0d9ad ×3 (:451 :461 :495) · #f3c4c4 (:496) · #ffc4c4 (:663).

Deliberate one-offs — §7: dev-mode banner #3a2a00 #ffd479 (:573), #ffb000 (:574) · rgba(37,99,214,0.07) (:565) — §8-E · rgba(255,255,255,.94) (:598), rgba(255,255,255,.28) (:715) · rgba(244,245,247,.82) (:302) — §8-A.

Cool-tinted shadows and scrims — one family, seven alphas: rgba(20,30,50, .06 .08 .16 .18 ×2 .22 .28) (:599 :824 :818 :647 :722 :621 :391) plus rgba(20,28,40,.55) (:521) and rgba(0,0,0,.25) (:518), rgba(0,0,0,.3) (:523). §8-F.


6. Type, spacing, radius and font scales

6a. Type — 27 distinct font-size values, three notations

rem on the two Carbon-native surfaces, px everywhere else, and half-pixels in the creator. There is no scale; there is a histogram.

Value Total Where
9px 9.5px 10px 10.5px 34 creator 28, chrome 4, wizard 1, sidenav 1
11px 11.5px 46 creator 34, console 3, sidenav 4, wizard 2, index 2, chrome 1
12px 12.5px 53 creator 32, console 7, index 6, wizard 5, chrome 2, sidenav 1
13px 13.5px 66 creator 31, console 10, index 9, chrome 7, wizard 4, sidenav 3, theme 2
14px 21 wizard 7, creator 7, theme 2, chrome 2, index 2, sidenav 1
15px 16px 17px 18px 20px 22px 26px 27 creator 13, wizard 9, console 2, theme 1, sidenav 1
.75rem2.25rem (10 values) 16 theme 8, index 8
8px, .92em, inherit 3 creator 2, console 1

Recommendation for T3.2: publish --wp-type-* as a documented ladder that page sheets may adopt, and convert only the values that already sit on it. Rewriting 27 sizes onto 8 steps changes rendering on every page and is forbidden here. The half-pixel sizes (9.5 10.5 11.5 12.5 13.5, 17 sites, creator + chrome) are the ones to retire — log as BL-006, do it during the T7.1 rebuild where the creator is being re-laid-out anyway.

6b. Spacing — three systems

System Where Values
Token scale console.css --s1..--s6 4 8 12 16 20 28 px — 62 var() consumers, 2 of them in users.js
rem ladder wizard + index.html inline 0.25 0.3 0.5 0.55 0.6 0.65 0.7 0.75 0.85 0.9 1 1.25 1.4 1.5 1.75 2 2.5 3 rem — 18 steps, 8 of them used once
Raw px, no scale creator, chrome, sidenav every integer 114, then 16 18 20 22 24 28 32 36 52 56 72 — 289 sites in the creator alone

--s1..--s6 is the only one of the three that is a scale. T3.2 promotes it to --wp-s* and aliases console.css. The other two get converted as their pages are rebuilt (T5.x, T7.x), not now.

6c. Radius — the token says square, the sheet says otherwise

--radius: 0 is declared once and honoured 23 times in wp-creation-styles.css — and contradicted by raw values 45 times in the same file: 2px 3px 4px 5px 6px 8px 9px 10px 12px 14px 20px 50%, plus the two asymmetric CTA radii at :707 and :716.

Across all sheets: 0 ×34, 3px ×13, 4px ×10, 6px ×9, 50% ×7, 9px ×6, 20px ×5.

The square-corner idiom is Carbon and it is the intent everywhere except the creator, which drifted. Do not fix in T3.2 — changing 45 radii is the most visible possible diff. Log as BL-007 against T7.1.

6d. Font stacks — 2 sans variants, 3 mono variants

Stack Sites Difference
Sans with 'Helvetica Neue' theme-light:121, wizard:22, creator --sans:30
Sans without 'Helvetica Neue' console:22, wp-chrome:54, wp-sidenav:36 drops one fallback
Mono A creator --mono:29 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', 'Segoe UI Mono', Consolas, monospace
Mono B console:17 drops ui-monospace and 'Segoe UI Mono'
Mono C wp-chrome:159, :221 drops 'Cascadia Mono' and 'Segoe UI Mono'

The file map recorded two mono stacks; there are three. Adopt Mono A (the superset) as --wp-font-mono and the Helvetica-Neue sans as --wp-font-sans.

This is a rendering change on machines that have 'Segoe UI Mono' or ui-monospace but not 'IBM Plex Mono' — i.e. most Windows machines without Plex installed, which is the target environment. Adopting the superset stack in T3.2 would change the admin console's monospace face. → Alias to the superset but keep console.css's narrower stack as --wp-font-mono-console until T3.5, or accept the change and re-shoot admin/users. Flagged, not decided — see §8-H.


7. One-offs, kept raw, with reasons

Values that should not become tokens, and why. T3.2 leaves each of these alone.

Value(s) Where Reason
#1a2230 #2563d6 #cdd9f2 #f0f2f5 #ccc wp-creation-app.js:1257 The print window is a separate document written with document.write(). It loads no stylesheet, so var() cannot resolve. Hardcoding is the only correct option here.
#0f1525 #d7e0f5 #56d364 #ff7b72 console.css:90-92 pre.out is a terminal emulator. Green-on-dark pass / red-on-dark fail is the convention being quoted; it is not the product palette and should not track it.
#3a2a00 #ffd479 #ffb000 wp-creation-styles.css:573-574 The dev-mode banner is meant to look wrong. Making it consume the product's amber would make it look intentional, which defeats it.
#e8daff #6929c4 console.css:145 Carbon purple-10/purple-60, used once, for the Project Super User tag. Real Carbon values with no --cds-* slot in this sheet's subset. Promote only if a second consumer appears.
#161616 index.html:13 <meta name="theme-color"> Markup attribute. Cannot take a var().
rgba(0,0,0,.4) wp-sidenav.css:38 Drawer drop-shadow. Neutral by intent — it falls on both light and dark hosts. §8-F
--shadow: none wizard :16, creator :27 A no-op token with 8 consumers. box-shadow: var(--shadow) renders nothing on .step-navigation, .seq-step, .card, .stepper, .btn-primary, .btn-generate. It is not broken — flat is the Carbon idiom — but it is 8 dead declarations. Keep the alias; note for T3.3, which is hunting exactly this class of silent no-op.
var(--accent-dim,#eef3fd) ×2, var(--accent-dim,#eaf0fd), var(--accent,#0f62fe), var(--cds-hover-primary,#0353e9), var(--cds-border-strong,#8d8d8d) ×3, var(--cds-ui-01,#fff) ×2, var(--cds-ui-03,#e0e0e0), var(--cds-link-01,#0f62fe) 12 sites: creator :515 :581 :839 :854, wizard :476, index.html:291 :292(×2) :300(×2) :303 :305 Dead fallbacks. Every one of these tokens is declared at :root on a sheet the page loads, so the fallback can never render. They are a meaningful share of the raw-hex count and cost nothing to delete. Deleting them is T3.2 work and is provably invisible.

8. Near-duplicates — where unintended visual change will come from

Computed, not eyeballed: every pair of distinct colours in the six sheets plus the launcher's inline block, ranked by summed channel distance. Anything a careless refactor would collapse.

A. Off-whites around #f4f4f4 — 5 values

Value Site Role
#f4f4f4 canonical, 22 sites page background / layer-accent
#fafafa console.css:16 --zebra table zebra stripe
#fbfbfc wp-creation-styles.css:680 .wp-nav panel background
rgba(244,245,247,.82) wp-creation-styles.css:302 loading overlay
#f0f0f0 wp-chrome.css:139,163 popover divider hairlines

The zebra is the trap. #fafafa sits 6 points lighter than #f4f4f4; collapsing them erases the striping on the user table entirely, on the one page where nine columns make striping load-bearing. T3.2 keeps all five.

B. Hover greys — 5 values, max spread 26

#e8e8e8 (canonical --cds-layer-hover, 7 sites) · #e5e5e5 (--cds-selected-ui, 8) · #eef0f2 (console --row-hover) · #eef0f3 (creator .wp-nav-*:hover, 3) · #e8eaed (creator .wp-nav-item.active, 2).

#eef0f2 and #eef0f3 are one point apart and in different sheets — almost certainly the same colour typed twice from memory. They are still not interchangeable in T3.2: console has a comment at :115-116 explaining that its row hover is deliberately not the accent tint. Two canonical tokens, one decision logged.

C. Accent tints — 4 values, and 2 of them are dead

Value Site Live?
#edf5ff canonical (blue-10), 4 sheets live
#eef3fd wp-creation-styles.css:581,839 deadvar() fallback, token declared
#eaf0fd wp-creation-styles.css:515 dead — same
#e2ecfc wp-creation-styles.css:582 .ref-link:hover live, one-off

Deleting the two dead fallbacks removes two near-duplicates at zero rendering risk. That is the cheapest win in this wave.

D. Status borders — the pairs that must not merge

Role Value A Value B Note
success border #a7f0ba console:82 #b6e3c6 creator:494,532 A is Carbon green-20; B is not Carbon at all
error border #ffd7d9 console:83 #f3c4c4 creator:496, #ffc4c4 creator:663 three values for one role; #f3c4c4/#ffc4c4 differ by 12
warning border #fddc69 console:84 #f0d9ad creator:451,461,495 A is Carbon yellow-30; B is not
accent border #b9d2fb ×4 #cdd9f2 ×2 neither is Carbon blue-20 (#d0e2ff)

Eight values doing four jobs. Every one renders somewhere today. T3.2 names all eight; picking four winners is a T3.5 conversation.

E. There is a second blue in the app

rgba(37,99,214,0.07) at wp-creation-styles.css:565 — that is #2563d6, not #0f62fe. It fills .sop-inherited, the highlight on every field a work package inherited from its SOP. At 7% alpha nobody has noticed, but it is a second brand blue, and help.js carries the same one at rgba(37,99,214,.15) and wp-creation-app.js:1257 uses it solid.

Not a T3.2 fix — swapping it to #0f62fe shifts a real fill. Log as BL-008, resolve at T3.5 alongside the button roles, since "one accent" is the same conversation.

F. Shadows and scrims — one blur, four tints

Family Values Sites
neutral rgba(0,0,0, .16 .25 .3 .4 .5) theme --cds-shadow, wizard --shadow-lg + .modal, creator #toast + .modal, sidenav
cool rgba(20,30,50, .06 .08 .12 .16 .18 .22 .28 .3 .5) creator ×10, console ×3, chrome ×1
cool-darker rgba(20,28,40,.55) creator .modal-overlay:521
near-black rgba(22,22,22, .5 .55) --cds-overlay, sidenav scrim :25

Correcting the file map: it records --shadow-lg as differing by "a different blur". The blur is identical — 0 4px 16px in both. The difference is the colour: rgba(0,0,0,0.16) (wizard) vs rgba(20,30,50,.12) (creator). Same geometry, warmer/cooler and 4% apart in alpha. That matters because it means the two can be unified later by choosing a tint, with no layout consequence at all.

rgba(22,22,22,.5) vs rgba(22,22,22,.55) is the same colour at two alphas — the sidenav scrim is 5% darker than --cds-overlay for no recorded reason. Cheapest merge in the set, but still a rendered change: not in T3.2.

G. Notation-only duplicates — safe to collapse

#fff vs #ffffff: 69 sites between them, identical rendering. Same for the whitespace variants rgba(0, 0, 0, .16) / rgba(0,0,0,.16) and rgba(22, 22, 22, .5). --surface: #fff (console) and --surface: #ffffff (creator) are the same token name, the same colour, two notations, two sheets. This is the one class of duplicate T3.2 can collapse with no thought required.

H. Font stacks — see §6d

Three mono stacks, two sans stacks. Adopting the superset is a real rendering change on machines missing IBM Plex Mono. Decide in T3.2; if adopted, admin and users need re-shot baselines — and remember trap 2: those two pages are not byte-stable (live timestamps), so capture twice before believing any diff.

K. A ninth amber, four points from the eighth

field.html:35 fills .pill.warn with #8a6d00, where every other warning text in the app is #8e6a00. Four points apart, same job, and field.html's inline block was not in the T3.1 inventory — §1 listed only the launcher's, and login.html and field.html also carry one. Almost certainly a typo rather than a decision.

T3.2 named it --wp-status-warning-text-alt rather than merging it, on the same rule as everything else in §8: merging moves a rendered colour. BL-009, resolve at T3.5.

J. #262626 — Carbon gray-90, used twice, no token

wp-chrome.css:31 (dark search field) and wp-sidenav.css:94 (current row). It is a real Carbon value that theme-light.css simply never declared. Add --cds-layer-01-inverse: #262626? No — cleanest is --wp-appbar-layer: #262626, in the appbar group where its two consumers live.


9. Tokens read from JavaScript — names that cannot be deleted

T3.2 says to keep existing names as aliases "where deleting them would touch large amounts of markup". This is the concrete list. These var() references live in .js files, so a rename fails silently — no build error, no console warning, just an unstyled element.

Consumer Tokens it reads Count
work-package-suite-app.js --border 15, --bg 7, --danger 6, --text-dim 5, --primary 2, --primary-light 1, --text-light 2, --text 1, --warning 1, --mono 1 41
wp-creation-app.js --text-dim 8, --accent 5, --accent-amber 4, --accent-green 3, --red 3, --nav-w 2, --surface2 1, --rail-top 1 27
admin.js --muted 14, --green 8, --red 5, --amber 2 29
users.js --muted 4, --red 4, --s2 2, --green 1 11
field.js --cds-text-helper 2, --cds-text-secondary 1 3

111 JS-side references across 23 distinct token names (21 once the two runtime-set properties below are set aside). Every one of those names must still resolve after T3.2. Grep before deleting any alias:

grep -n 'var(--NAME' *.js

Two runtime-set properties are declared in no stylesheet and must not be shadowed: --wp-chrome-h (work-package-suite-app.js:528) and --rail-top (wp-creation-app.js:1328).


10. The #0f62fe count — correcting the wave 0 baseline

file-map.md §4 metric 5 records 14 declarations in 4 sheets and defines the metric as "the number of stylesheets that declare their own accent token", with a wave 9 target of 1.

The count is 15, in 5 sheets. The baseline command misses one:

grep -nE '^\s*--[a-zA-Z0-9-]+\s*:\s*#0f62fe' *.css      # 14 — anchored to ^

console.css:13 packs five declarations onto one line, and --accent:#0f62fe is the third of them. ^\s*-- only ever matches the first declaration on a line, so console's accent was never counted. Corrected command and result:

grep -ohE '\-\-[a-zA-Z0-9-]+\s*:\s*#0f62fe' *.css | wc -l    # 15 declarations
grep -lE  '\-\-[a-zA-Z0-9-]+\s*:\s*#0f62fe' *.css | wc -l    # 5 sheets
Sheet Token Line
theme-light.css --cds-interactive-01 + 10 Carbon aliases 3, 5, 6, 22, 39, 62, 64, 69, 88, 97, 115
console.css --accent 13 — missed by the baseline
work-package-suite-styles.css --primary 2
wp-chrome.css --wpc-accent 25
wp-creation-styles.css --accent 18

The wave 9 target is unchanged: 1 sheet. The extra sheet does not change the goal, it changes the count of what has to be removed — 4 page-sheet declarations, not 3.

Two secondary corrections while this is open:

  • Raw occurrences: 30, not 31. file-map.md recorded 31 at wave 0. T2.2 deleted the flat strip and took one with it. This is the metric the file map explicitly says not to track ("track the sheet count, not the raw occurrence count") — recorded here only so the discrepancy is not rediscovered as a defect.
  • Current spread of the 30: theme-light 11, wp-chrome 4, help.js 4, wp-creation-styles 2, and one each in console.css, work-package-suite-styles, wp-sidenav, index.html, auth-guard.js, work-package-suite-app.js, wp-creation-app.js, wp-format.js.

11. T3.2 — what was done, and how it was proved

Done in this order, because the ordering is what kept the diff empty:

  1. Deleted the 12 dead var() fallbacks (§7). Provably invisible.
  2. Added the canonical tokens to theme-light.css (§2a, §2b, §2d) without touching a consumer. Additive, zero diff.
  3. Converted the :root blocks of console.css, work-package-suite-styles.css and wp-creation-styles.css to aliases. Names stayed; literals went.
  4. Converted wp-chrome.css's two .wp-chrome blocks, preserving the light/dark switch.
  5. Converted wp-sidenav.css's 30 raw literals to the app-bar tokens, which is the sheet whose stated intent ("colours come from the bar") and mechanism (hardcoded hex) disagreed. Flipping --wp-appbar-bg now takes the drawer with it, which is what the comment claimed.
  6. Converted the raw literals in rules across all six sheets and all four inline <style> blocks, skipping everything §7 lists.
  7. Left every §8 near-duplicate as its own canonical token. No winners picked.

Result against the done-when list

Check Result
exactly one declaration per canonical value; #0f62fe → 1 met. One sheet declares it (theme-light.css); the 11 occurrences inside it are Carbon's own v10→v11 alias layer, which §3 records as deliberate and not the S5 defect
no page stylesheet declares a raw colour met. 0 literals across all 5 page sheets and all 4 inline <style> blocks; theme-light.css is the only file in html/ holding one
no page stylesheet declares a raw spacing or type value not met, and not achievable under this task's own "change no rendered value" — see below
screenshot diff empty on all pages at both widths superseded by a stronger check — see below
docs/reference/tokens.md updated to what was built met — §2d

The spacing and type half of check 2

Token declarations are aliased: --s1--s6, --ctl, --ctl-sm, --radius, --mono, --sans all resolve from theme-light.css now. But 483 raw spacing values, 281 raw font-sizes and 65 raw radii remain inside rules, 492 of them in the creator.

They cannot be tokenised here, and the reason is not effort. §6b measured that the creator's spacing is every integer from 1px to 14px — it is not a scale, it is a histogram, and there is no token that padding: 9px 11px maps to without changing one of the two numbers. T3.2 forbids changing a rendered value, so tokenising them and honouring that constraint are mutually exclusive.

Stated plainly rather than quietly skipped: this check is not met. The colour half, which is what S5 and C3 are actually about — four sheets each declaring #0f62fe — is met in full. The spacing and type half is logged as BL-010 and belongs with the page rebuilds at T5.x and T7.1, where the values are being chosen again anyway.

Why the screenshot check was replaced

The done-when asks for an empty screenshot diff. Captured against the wave 2 build, 11 of 14 shots were pixel-identical and 3 were not: the creator at 1440px and both admin widths. Capturing wave 2 against itself produced the same 3 differences, at the same bounding box (creator@1440: 344,272px, bbox 288,14→1439,4924). Those shots re-render live content, so a pixel diff on them cannot distinguish a regression from the clock.

Trap 2 in the task brief is half right and half wrong. It names admin and users as the unstable pair. users is stable at both widths; the actually-unstable third is the creator at 1440px, and admin's instability is worse than "live timestamps" implies — its captured page height varies by ~600px between runs.

So tests/token_check.py was written, and it checks the thing wave 3 actually claims:

  • every custom property resolves to the same literal it did before, on every page;
  • every element computes the same color, background-color, four border colours, outline-color, box-shadow, text-decoration-color, caret-color, column-rule-color, font-family, font-size, font-weight and border-radius.

That is strictly stronger than a screenshot: it covers the hover, focus and disabled rules a screenshot never exercises, and it is deterministic.

Result, wave 2 versus T3.2, all 7 pages: every one of the 178 wave-2 token names resolves identically, and all 3,500 elements compute identically. 213 tokens are new. 16 tokens differ in notation only (#fff#ffffff, rgba(0,0,0,0.16)rgba(0, 0, 0, .16)), which is §8-G — the one class of duplicate this task was supposed to collapse.

Two things that cost time and are recorded so they do not cost it again:

  • The element walk was first keyed by sibling index, which reported 55 phantom differences on the SOP page. The cause is three JS-injected overlays — #wp-sync-badge, .wp-navscrim and #wp-sidenav — appending in whichever order their async work finishes. All three are position: fixed with their own z-index, so the order changes nothing painted. The probe now keys by identity. BL-011.
  • The comparator normalises notation before reporting, because otherwise it fails on its own success. That is safe only because the element comparison is the real evidence: a normalisation that hid a genuine change would show up on every element consuming the token.

Backlog entries this work raised

ID What Suggested
BL-004 help.js's 52-colour parallel palette in a different design language wave 9 (C4)
BL-005 auth-guard.js + wp-format.js modal inline styles, 35 raw literals T9.5 with C1
BL-006 17 half-pixel font sizes in the creator and chrome T7.1
BL-007 --radius: 0 contradicted by 45 raw radii in the creator T7.1
BL-008 #2563d6, a second brand blue, in .sop-inherited and help.js T3.5
BL-009 #8a6d00, a ninth amber four points from the eighth, on the field view T3.5
BL-010 483 raw spacing, 281 raw font-size and 65 raw radius values in rules T5.x / T7.1
BL-011 Three JS-injected overlays race to append on the SOP page wave 9
BL-012 admin and creator@1440 are not stable enough to screenshot-diff wave 9 with C2

12. Button roles — A5, built at T3.5

251 buttons across the 7 pages, counted in the browser with every wizard step, creator section and tool panel forced visible — two thirds of them are display: none at load and a static grep misses them entirely.

The four roles

Defined once, in theme-light.css, as --wp-btn-*. No fifth role exists.

Role Looks like For Tokens
primary filled accent, white text the one action the screen exists for; at most one per view --wp-btn-primary-bg/-fg/-hover/-active
secondary white fill, --border-strong hairline, accent on hover every other real action — the app's workhorse --wp-btn-secondary-bg/-fg/-border/-hover-fg
tertiary no fill, no border, accent text navigating or undoing --wp-btn-tertiary-fg/-hover-fg
danger outlined red; filled red only where a control is too small for an outline to read destructive --wp-btn-danger-fg/-border/-soft-bg/-fill-bg/-fill-fg

Green is a status colour

It says cleared, ready, complete — on a dot, a pill, a banner, a badge — and it never fills a button. That was the second button language A5 recorded: Save & View and SOP Complete shouted louder than every other action in the suite while doing nothing more dangerous than saving a form.

The green did not go anywhere. .cstatus button.on-cleared, .toggle-btn.enabled, .wp-nav-dot.ok, .rb-ready, .badge-R, .auto-tag and the launcher card's left border and .card-status all still carry it — every one of them a state rather than an action.

Every button class, mapped

Class Role Where Changed at T3.5?
button (bare) secondary console tokens only
button.primary primary console tokens only
button.danger, button.mini.danger danger, outlined console tokens only
button.mini secondary console
.add-btn primary wizard tokens only
.nav-btn secondary wizard tokens only
.nav-btn.primary primary wizard — SOP complete was green
.seq-del danger, filled wizard — a 28px ✕ tokens only
.comments-toggle secondary wizard
.header-button, .wp-appbar-btn secondary on a dark bar theme
.btn.btn-primary primary creator tokens only
.btn.btn-generate primary creator — Save & view was green
.use-btn primary creator was green
.btn.btn-ghost secondary creator tokens only
.add-btn (creator) secondary, dashed creator uppercase removed
.add-btn-gate secondary, amber creator — adds a QC hold
.row-del danger, outlined on hover creator tokens only
.wp-nav-cta, .wp-nav-cta-more primary creator navigator tokens only
.link-btn, .lock-edit, .cmt-jump tertiary creator tokens only
.mode-btn.active primary creator
.cstatus button.on-* status, not a role creator — Open / Cleared / N/A
.card-button primary launcher
.card.complete .card-button primary launcher was green
.submit-btn, .comments-toggle primary launcher
.close-btn secondary launcher
.wpc-foot-btn secondary shared chrome tokens only
.wpc-clear, .comments-dropdown-close, .modal-close, .cmt-x, .wp-sidenav-close tertiary everywhere
.wp-navbtn, .wp-nav-toggle, .wp-sidenav-link, .wp-nav-link, .wp-nav-item tertiary drawers and rails
.ui-help-fab primary help centre out of scope — BL-004
.dev-toggle deliberately invisible creator

Four green action sites, not the two A5 names. .use-btn and the launcher's completed-SOP card button never render green in the default fixture, so the review could not have seen them.

Sentence case

Applied to buttons and field labels only, which is what A5 scopes it to.

The rule: first word capitalised, everything after it lowercased, except acronyms (SOP, QC, WP, UPN, PM, APM, CM, QM, PDF, JSON, CSV, BIM, MIMO) and external proper nouns (Excel, Acumatica). Page names get sentence case too when they appear inside a sentence-shaped label — "Open the user directory", not "Open the User Directory" — because the label is a description, not a title.

  • ~30 button labels rewritten across the launcher, wizard, creator, admin and two scripts.
  • 46 field labels rewritten. Labels carrying markup — a .req asterisk, a .help-tip chip — had only their text nodes transformed, so the markup survives and "first word" means the first word of the label rather than of each fragment.
  • text-transform: uppercase removed from four rules: .btn and .add-btn (creator buttons) and label and .cmt-namebar label (creator field labels). The mono face, the 10px size and the tracking are the creator's idiom and are untouched — only the forced uppercase goes.
  • help.js was updated too. It names Load Sample and SOP Complete in prose, so renaming the buttons without it would have left the help centre describing controls that no longer exist.

Not touched, deliberately

What Why
console.css .card h2 uppercase T3.5 says explicitly not to. Confirmed by diff: the only changes to console.css in this task are token substitutions, none of them on .card h2.
table.grid th, .wp-types-header, .dash-table th table headers, not buttons or field labels
.section-label, .sub-heading, .wpc-proj-kicker, .wpc-pop-head, .wp-sidenav-sect section eyebrows and headings
.step-tab uppercase a stepper tab is neither a button nor a field label, and the stepper is rebuilt at T7.x. Logged as BL-015.
.nav-tab labels (SOP Configuration, Work Package Creation) these name the two tools rather than describing an action; they read as titles and are the same strings the launcher cards use.

13. Wave 3 exit — everything wave 3 changed on screen

wave-3.md asks for "no unintended visual change on any page versus wave 2". Measured with tests/token_check.py, wave 2 (b105135) against the end of wave 3, all 7 pages:

7 elements changed. All 7 are the fixes the wave was for.

Page Element Change Task
sop #role_super_name.user-pick #f4f4f4#ffffff, border #e0e0e0#8d8d8d T3.4 (A3/F5)
sop #role_foreman_name.user-pick same T3.4
sop #sop-complete-btn.nav-btn.primary #198038#0f62fe T3.5 (A5)
sop #wp-gate button.nav-btn.primary #198038#0f62fe T3.5
creator #sticky-save .btn-generate #198038#0f62fe T3.5
creator .nav-row .btn-generate #198038#0f62fe T3.5
creator #hold-modal .btn-generate #198038#0f62fe T3.5

Every other element on every page — 3,493 of them — computes exactly what it computed at the end of wave 2, and all 178 wave-2 token names resolve to the same literals. T3.2's consolidation of five token systems into one changed nothing on screen, which was its whole contract.

Three fixes are not visible in that table and are verified another way, because a computed -style diff cannot see them:

Fix Why it is invisible here How it was verified
.card.complete .card-button green → primary needs a completed SOP; the fixture has none source diff
.use-btn green → primary needs a suggested value to be offered source diff
Sentence case, ~30 buttons + 46 labels token_check fingerprints computed style, not text or text-transform the button inventory: 0 uppercase labels, 251 buttons still present
Focus rings :focus is not in the fingerprint measured directly with CDP focus emulation — see T3.4

Exit criteria

  • one token source of truth; page sheets alias only — theme-light.css is the only file in html/ containing a colour literal
  • #0f62fe declared once — one sheet, down from five
  • no unintended visual change versus wave 2 — 7 changes, 7 intended
  • fields look editable, buttons speak one language
  • not met: T3.2's "no page stylesheet declares a raw spacing or type value" — colour is complete, spacing and type are BL-010, and §11 explains why the two halves of that check cannot both be satisfied in the same task

Appendix A — every theme-light.css declaration, line by line

T3.1's first done-when is "lists every declaration with file and line". §4 itemises the 82 page-sheet declarations; this is the other 120, so the list is complete rather than grouped. Column # is the line number in html/theme-light.css. Read down each column pair in turn.

# Token Value # Token Value # Token Value
3 --cds-interactive-01 #0f62fe 43 --cds-hover-secondary #4c4c4c 83 --cds-field #ffffff
4 --cds-interactive-02 #393939 44 --cds-active-secondary #6f6f6f 84 --cds-field-hover #e8e8e8
5 --cds-interactive-03 #0f62fe 45 --cds-hover-tertiary #0353e9 85 --cds-border-subtle #e0e0e0
6 --cds-interactive-04 #0f62fe 46 --cds-active-tertiary #002d9c 86 --cds-border-strong #8d8d8d
7 --cds-ui-background #f4f4f4 47 --cds-hover-ui #e5e5e5 87 --cds-border-inverse #393939
8 --cds-ui-01 #ffffff 48 --cds-hover-light-ui #e5e5e5 88 --cds-border-interactive #0f62fe
9 --cds-ui-02 #f4f4f4 49 --cds-hover-selected-ui #cacaca 89 --cds-border-subtle-selected #c6c6c6
10 --cds-ui-03 #e0e0e0 50 --cds-active-ui #c6c6c6 90 --cds-text-primary #161616
11 --cds-ui-04 #8d8d8d 51 --cds-active-light-ui #cacaca 91 --cds-text-secondary #525252
12 --cds-ui-05 #161616 52 --cds-selected-ui #e5e5e5 92 --cds-text-placeholder #a8a8a8
13 --cds-text-01 #161616 53 --cds-selected-light-ui #e5e5e5 93 --cds-text-helper #6f6f6f
14 --cds-text-02 #525252 54 --cds-inverse-hover-ui #353535 94 --cds-text-on-color #ffffff
15 --cds-text-03 #a8a8a8 55 --cds-hover-danger #ba121b 95 --cds-text-inverse #f4f4f4
16 --cds-text-04 #ffffff 56 --cds-active-danger #750e13 96 --cds-text-disabled #bdbdbd
17 --cds-text-05 #6f6f6f 57 --cds-hover-row #f4f4f4 97 --cds-link-primary #0f62fe
18 --cds-text-error #da1e28 58 --cds-visited-link #0043ce 98 --cds-link-secondary #0043ce
19 --cds-icon-01 #161616 59 --cds-disabled-01 #f4f4f4 99 --cds-link-visited #0043ce
20 --cds-icon-02 #525252 60 --cds-disabled-02 #bdbdbd 100 --cds-link-inverse #78a9ff
21 --cds-icon-03 #ffffff 61 --cds-disabled-03 #8d8d8d 101 --cds-link-primary-hover #0353e9
22 --cds-link-01 #0f62fe 62 --cds-button-primary #0f62fe 102 --cds-icon-primary #161616
23 --cds-link-02 #0043ce 63 --cds-button-secondary #393939 103 --cds-icon-secondary #525252
24 --cds-inverse-link #78a9ff 64 --cds-button-tertiary #0f62fe 104 --cds-icon-on-color #ffffff
25 --cds-field-01 #ffffff 65 --cds-button-danger-primary #da1e28 105 --cds-icon-inverse #f4f4f4
26 --cds-field-02 #f4f4f4 66 --cds-button-danger-secondary #da1e28 106 --cds-icon-disabled #bdbdbd
27 --cds-inverse-01 #ffffff 67 --cds-background #f4f4f4 107 --cds-support-error #da1e28
28 --cds-inverse-02 #393939 68 --cds-background-inverse #161616 108 --cds-support-success #198038
29 --cds-support-01 #da1e28 69 --cds-background-brand #0f62fe 109 --cds-support-warning #f1c21b
30 --cds-support-02 #198038 70 --cds-background-active #e5e5e5 110 --cds-support-info #0043ce
31 --cds-support-03 #f1c21b 71 --cds-background-hover #e8e8e8 111 --cds-support-error-inverse #fa4d56
32 --cds-support-04 #0043ce 72 --cds-background-selected #e5e5e5 112 --cds-support-success-inverse #42be65
33 --cds-inverse-support-01 #fa4d56 73 --cds-background-selected-hover #cacaca 113 --cds-support-warning-inverse #f1c21b
34 --cds-inverse-support-02 #42be65 74 --cds-layer #ffffff 114 --cds-support-info-inverse #4589ff
35 --cds-inverse-support-03 #f1c21b 75 --cds-layer-accent #f4f4f4 115 --cds-interactive #0f62fe
36 --cds-inverse-support-04 #4589ff 76 --cds-layer-accent-hover #e8e8e8 116 --cds-shadow rgba(0, 0, 0, .16)
37 --cds-overlay rgba(22, 22, 22, .5) 77 --cds-layer-accent-active #e0e0e0 170 --wp-appbar-bg #161616
38 --cds-danger #da1e28 78 --cds-layer-hover #e8e8e8 171 --wp-appbar-fg #ffffff
39 --cds-focus #0f62fe 79 --cds-layer-active #e5e5e5 172 --wp-appbar-fg-dim #c6c6c6
40 --cds-hover-primary #0353e9 80 --cds-layer-selected #e5e5e5 173 --wp-appbar-border #6f6f6f
41 --cds-active-primary #002d9c 81 --cds-layer-selected-hover #cacaca 174 --wp-appbar-hover #353535
42 --cds-hover-primary-text #0043ce 82 --cds-layer-selected-inverse #393939 175 --wp-appbar-height 48px

120 declarations. Lines 3116 are the Carbon g10 set; lines 170175 are the app-shell group added by the suite. Many share a literal by design — that is Carbon's v10→v11 alias layer, not the S5 defect. See the note at the end of §3.