T3.2 - C3/S5: one source of truth for colour; page sheets alias only

theme-light.css is now the only file in html/ that contains a colour literal.
The five page stylesheets and all four inline <style> blocks declare names and
nothing else.

  theme-light.css                 191 declarations, 175 with a literal value
  console.css                      28 declarations,   0
  work-package-suite-styles.css    16 declarations,   0
  wp-chrome.css                    14 declarations,   0
  wp-creation-styles.css           24 declarations,   0
  wp-sidenav.css                    0 declarations,   0

#0f62fe is declared in one sheet, down from five. The eleven occurrences left
inside theme-light.css are Carbon's own v10-to-v11 alias layer, which the
inventory records as deliberate and not the S5 defect.

Names were kept, because 111 var() references live in .js files across 23 token
names and a rename there fails silently - no build error, no console warning,
just an unstyled element.

The rule the refactor was built on: consolidation is not unification. Where two
sheets declared the same value, they collapse. Where they declared DIFFERENT
values for one role - the two shadows, the eight status borders doing four jobs,
the three mono stacks - each value got its own canonical name and the pair is
recorded for T3.5. Picking a winner between two near-identical greys is a
rendered change, which this task forbids. The console's zebra stripe is the one
that would have bitten: #fafafa is six points from #f4f4f4, and merging them
erases the striping on the nine-column user table.

Collecting the one-offs in one place made two things countable that were not
before: twelve distinct shadows, and a ninth amber (#8a6d00 on the field view,
four points from #8e6a00 and doing the same job - BL-009).

VERIFICATION - the screenshot done-when could not do the job, so it was replaced.

Captured against wave 2, 11 of 14 shots were pixel-identical and 3 were not.
Capturing wave 2 against ITSELF produced the same 3 differences at the same
bounding box, so those shots cannot distinguish a regression from the clock.
Trap 2 in the brief is half wrong: users.html is stable at both widths; the
unstable third is the creator at 1440px, and admin's captured page height varies
by ~600px between runs (BL-012).

So tests/token_check.py was added. It checks what wave 3 actually claims: that
every custom property resolves to the same literal, and every element computes
the same colours, shadows and type. That is stronger than a screenshot - it
covers the hover, focus and disabled rules a screenshot never exercises, and it
is deterministic.

  wave 2 vs T3.2, all 7 pages:
  178/178 wave-2 token names resolve identically, +213 new
  3,500 elements compute identically, zero added, zero removed
  16 tokens differ in notation only (#fff -> #ffffff), which is the duplicate
  class this task existed to collapse

Two detours worth not repeating: the element walk was first keyed by sibling
index and reported 55 phantom differences on the SOP page, where three
JS-injected overlays append in whichever order their async work finishes
(BL-011); and the comparator now normalises notation before reporting, because
otherwise it fails on its own success.

f_items 5 FIXED / F6 REPRODUCES as expected. browser_check 71/71.

ONE DONE-WHEN NOT MET, recorded rather than skipped: "no page stylesheet
declares a raw color, spacing or type value". The colour half is met in full.
483 raw spacing values, 281 font-sizes and 65 radii remain inside rules, 492 of
them in the creator. That is arithmetic, not effort: the creator's spacing is
every integer from 1px to 14px, so no token exists that padding:9px 11px maps to
without changing one of the numbers - and this task forbids changing a rendered
value. The two requirements are mutually exclusive. Logged as BL-010 for T5.x
and T7.1, where those pages are re-laid-out and the values get chosen again.

New backlog: BL-009 (ninth amber), BL-010 (raw spacing/type in rules),
BL-011 (overlay append race), BL-012 (unstable screenshot targets).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-15 18:50:30 -05:00
parent 0e40e967a0
commit 9ab7b48de2
14 changed files with 964 additions and 266 deletions

View File

@@ -114,11 +114,181 @@
--cds-support-info-inverse: #4589ff;
--cds-interactive: #0f62fe;
--cds-shadow: rgba(0, 0, 0, .16);
/* Carbon's own $highlight, which the page sheets each declared for themselves
as --accent-soft / --primary-light / --accent-dim. Real Carbon name and
real Carbon value; this sheet simply never carried it. (T3.2 / S5) */
--cds-highlight: #edf5ff; /* blue-10 */
}
/* ============================================================================
SUITE TOKENS (--wp-*) — roles IBM Carbon does not define
----------------------------------------------------------------------------
Everything above is Carbon g10 and should stay that way. Everything here is a
value the suite invented because Carbon has no slot for it. Keeping the two
namespaces apart is what stops the next person "correcting" #8e6a00 into a
Carbon yellow and breaking the warning banners.
Together with the --cds-* block above, this is the one place a colour,
spacing or type value is defined (CLAUDE.md, "The token rule"). Page
stylesheets alias these; they declare nothing of their own.
Full mapping, and the provenance of every value: docs/reference/tokens.md
============================================================================ */
:root {
/* -- status fills and their text ------------------------------------------
The greens and reds are Carbon 10-step values; the amber pair is not
Carbon at all (Carbon yellow-10 is #fcf4d6). --wp-status-warning-text is
the suite's own accessible amber for text sitting on --wp-status-warning-bg. */
--wp-status-success-bg: #defbe6;
--wp-status-error-bg: #fff1f1;
--wp-status-warning-bg: #fdf6dd;
--wp-status-warning-text: #8e6a00;
/* Four points from --wp-status-warning-text and doing the same job, on the
field view's warn pill. Almost certainly a typo rather than a decision, but
merging it moves a rendered colour, so T3.2 names it and T3.5 merges it.
BL-009 / docs/reference/tokens.md section 8-K. */
--wp-status-warning-text-alt: #8a6d00;
/* Carbon green-70. The value is Carbon, the role is not — Carbon has no
"hover for a green fill", because green is not one of its action colours.
Declared in no sheet today; written raw in five places. */
--wp-hover-success: #0e6027;
/* -- status borders -------------------------------------------------------
Eight values doing four jobs, because two sheets each picked their own.
Every one of them renders somewhere today, so T3.2 names all eight rather
than choosing four winners — choosing is a visual change. T3.5 decides.
See docs/reference/tokens.md section 8-D. */
--wp-status-success-border-a: #a7f0ba; /* green-20, Carbon — console */
--wp-status-success-border-b: #b6e3c6; /* not Carbon — creator */
--wp-status-error-border-a: #ffd7d9; /* red-20, Carbon — console */
--wp-status-error-border-b: #f3c4c4; /* not Carbon — creator, release banner */
--wp-status-error-border-c: #ffc4c4; /* not Carbon — creator, .crit-tag */
--wp-status-warning-border-a: #fddc69; /* yellow-30, Carbon — console */
--wp-status-warning-border-b: #f0d9ad; /* not Carbon — creator */
--wp-accent-border-a: #b9d2fb; /* neither is Carbon blue-20 (#d0e2ff) */
--wp-accent-border-b: #cdd9f2;
/* -- type ------------------------------------------------------------------
Three mono stacks and two sans stacks exist because five sheets each wrote
their own. They are NOT interchangeable: on a machine without IBM Plex Mono
installed — which is most of the target environment — each falls through to
a different face. Unifying them changes what admin and users render, so
T3.2 names all five and T3.5 picks. See docs/reference/tokens.md 6d, 8-H. */
--wp-font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
--wp-font-sans-2: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--wp-font-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', 'Segoe UI Mono', Consolas, monospace;
--wp-font-mono-2: 'IBM Plex Mono', 'Cascadia Mono', Consolas, monospace;
--wp-font-mono-3: 'IBM Plex Mono', ui-monospace, Consolas, monospace;
/* -- spacing ---------------------------------------------------------------
Promoted from console.css, which is the only sheet in the repo whose
spacing is a scale rather than a histogram. The wizard's rem ladder and the
creator's raw px are inventoried in docs/reference/tokens.md 6b and convert
as those pages are rebuilt (T5.x, T7.x) — not here, where any change to a
padding is a visual change. */
--wp-s1: 4px;
--wp-s2: 8px;
--wp-s3: 12px;
--wp-s4: 16px;
--wp-s5: 20px;
--wp-s6: 28px;
/* Control heights: --wp-ctl for anything in a form row, --wp-ctl-sm for
anything inside a table cell. This is what keeps a toolbar one clean band
and a table row ~34px instead of ~100px. */
--wp-ctl: 32px;
--wp-ctl-sm: 26px;
/* -- shape and elevation ---------------------------------------------------
Square corners are the Carbon idiom and the intent everywhere except the
creator, which drifted to 12 different radii (BL-007, owned by T7.1).
The two large shadows share their geometry exactly — 0 4px 16px — and
differ only in tint, so they can be unified later with no layout
consequence. Until then, both are named. */
--wp-radius-0: 0;
--wp-shadow-none: none;
--wp-shadow-lg-neutral: 0 4px 16px rgba(0, 0, 0, .16);
--wp-shadow-lg-cool: 0 4px 16px rgba(20, 30, 50, .12);
/* -- elevation -------------------------------------------------------------
Twelve distinct shadows, which is eleven more than a flat Carbon UI wants.
They are listed rather than merged for the same reason as the borders: each
one renders today and merging is a visual change. That the list is this long
is itself the finding — a later pass can collapse it to two or three now
that they are all visible in one place. */
--wp-shadow-pop: 0 8px 28px rgba(20, 30, 50, .22);
--wp-shadow-menu: 0 8px 24px rgba(20, 30, 50, .18);
--wp-shadow-menu-lg: 0 10px 26px rgba(20, 30, 50, .18);
--wp-shadow-modal: 0 12px 40px rgba(20, 30, 50, .3);
--wp-shadow-modal-lg: 0 20px 60px rgba(0, 0, 0, .3);
--wp-shadow-gate: 0 8px 30px rgba(20, 30, 50, .12);
--wp-shadow-rail: 6px 0 22px rgba(20, 30, 50, .16);
--wp-shadow-drawer: 2px 0 16px rgba(0, 0, 0, .4);
--wp-shadow-sticky: 0 -2px 10px rgba(20, 30, 50, .08);
--wp-shadow-navbar: 0 1px 4px rgba(20, 30, 50, .06);
--wp-shadow-tooltip: 0 4px 14px rgba(20, 30, 50, .22);
--wp-shadow-toast: 0 6px 24px rgba(0, 0, 0, .25);
/* -- scrims ----------------------------------------------------------------
Five overlay fills in three tints. --cds-overlay (rgba(22,22,22,.5)) is the
Carbon one; the drawer's is the same colour 5% darker for no recorded
reason, which makes it the cheapest merge in the set — but still a merge,
so still not here. See docs/reference/tokens.md section 8-F. */
--wp-scrim-drawer: rgba(22, 22, 22, .55);
--wp-scrim-cool: rgba(20, 30, 50, .28);
--wp-scrim-cool-modal: rgba(20, 30, 50, .5);
--wp-scrim-cool-strong: rgba(20, 28, 40, .55);
--wp-scrim-neutral: rgba(0, 0, 0, .5);
--wp-scrim-loading: rgba(244, 245, 247, .82);
--wp-scrim-frosted: rgba(255, 255, 255, .94); /* sticky section-nav backdrop */
--wp-on-accent-divider: rgba(255, 255, 255, .28);
/* -- surfaces that are not Carbon greys ------------------------------------
Four neutrals the suite invented, all within a few points of a Carbon step
and none of them equal to one. --wp-table-zebra is the dangerous one: it is
six points from --cds-layer-accent, and collapsing them erases the striping
on the nine-column user table. Section 8-A. */
--wp-table-zebra: #fafafa;
--wp-table-row-hover: #eef0f2;
--wp-nav-bg: #fbfbfc;
--wp-nav-hover: #eef0f3;
--wp-nav-active: #e8eaed;
--wp-pop-divider: #f0f0f0; /* popover hairlines in the shared chrome */
--wp-accent-soft-hover: #e2ecfc; /* hover on an --cds-highlight fill */
/* -- the Project Super User tag --------------------------------------------
Carbon purple-10 / purple-60. Real Carbon values with no --cds-* slot in
the subset this sheet carries. One consumer; promote into --cds-* if a
second ever appears. */
--wp-tag-super-bg: #e8daff;
--wp-tag-super-text: #6929c4;
/* -- console output --------------------------------------------------------
pre.out is a terminal emulator, not product surface. Green-on-dark pass and
red-on-dark fail is the convention being quoted; it should not track the
palette and must not be "corrected" to Carbon's support colours. */
--wp-term-bg: #0f1525;
--wp-term-fg: #d7e0f5;
--wp-term-pass: #56d364;
--wp-term-fail: #ff7b72;
/* -- dev mode --------------------------------------------------------------
Deliberately outside the palette. The banner is MEANT to look wrong; giving
it the product's amber would make it look intentional, which defeats it. */
--wp-dev-bg: #3a2a00;
--wp-dev-fg: #ffd479;
--wp-dev-rule: #ffb000;
/* -- the second blue -------------------------------------------------------
#2563d6, not #0f62fe. Fills .sop-inherited — every field a work package
inherited from its SOP — at 7% alpha, which is why nobody has noticed a
second brand blue. Named here so it is visible; swapped at T3.5 (BL-008). */
--wp-sop-inherited-bg: rgba(37, 99, 214, .07);
}
/* Typography */
body {
font-family: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
font-family: var(--wp-font-sans);
color: var(--cds-text-primary);
background: var(--cds-background);
font-size: 1rem;
@@ -173,6 +343,10 @@ input, textarea, select {
--wp-appbar-border: #6f6f6f; /* outline for ghost buttons on the bar */
--wp-appbar-hover: #353535;
--wp-appbar-height: 48px;
/* Carbon gray-90 — one step up from the bar itself. The raised surfaces that
sit ON the dark bar or the drawer: the chrome's search field, the drawer's
current row. Its two consumers both hardcoded it before T3.2. */
--wp-appbar-layer: #262626;
}
.wp-appbar {
@@ -200,7 +374,7 @@ input, textarea, select {
display: inline-flex;
align-items: center;
justify-content: center;
background: #fff;
background: var(--cds-ui-01);
border-radius: 4px;
padding: 4px 8px;
}