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:
@@ -199,8 +199,17 @@ python server/smoketest.py # API; needs WP_SMOKE_USER / WP_SMOK
|
||||
python tests/browser_check.py # pages boot and render, self-contained
|
||||
python tests/baseline_shots.py # screenshots, self-contained
|
||||
python tests/f_items.py # does each of F1-F6 still reproduce?
|
||||
python tests/token_check.py --out a.json # every resolved token + computed style
|
||||
python tests/token_check.py --compare a.json b.json
|
||||
```
|
||||
|
||||
`tests/token_check.py` was added by `T3.2`, because a screenshot cannot prove a token refactor:
|
||||
three of the fourteen shots are not stable capture-to-capture (see `backlog.md` BL-012), and no
|
||||
screenshot exercises a hover, focus or disabled rule, which is where half the tokens live. It
|
||||
snapshots every custom property's resolved value and every element's computed colours, shadows
|
||||
and type, on all 7 pages, and diffs two snapshots. Use it for any task that claims to change
|
||||
styling without changing appearance.
|
||||
|
||||
`server/smoketest.py` is the one that is **not** self-contained: it drives a server you point
|
||||
it at and aborts unless `WP_SMOKE_USER` and `WP_SMOKE_PASSWORD` are set, because every route
|
||||
but `/api/health` needs a session. Use an admin account — it creates and deletes a project.
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Token inventory — every declared and raw value in `html/`
|
||||
|
||||
**Task:** `T3.1` · **Items:** `C3` part 1, `S5` part 1 · **Produced:** August 15, 2026
|
||||
**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`
|
||||
|
||||
This is the mapping table `T3.2` builds from. Nothing here changes a stylesheet — `T3.1` is
|
||||
inventory only. Everything below was read from the working tree at `b105135` (end of wave 2),
|
||||
not quoted from the review.
|
||||
`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](#8-near-duplicates--where-unintended-visual-change-will-come-from)
|
||||
before writing a single line of `T3.2`.** That section is the whole risk in this wave.
|
||||
@@ -59,6 +61,11 @@ the launcher's inline `<style>`.
|
||||
|
||||
**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 |
|
||||
@@ -71,7 +78,7 @@ the launcher's inline `<style>`.
|
||||
|
||||
---
|
||||
|
||||
## 2. The proposed canonical set
|
||||
## 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.
|
||||
@@ -117,6 +124,37 @@ See §8 for the decision each one is waiting on.
|
||||
| `--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)
|
||||
@@ -537,6 +575,16 @@ machines missing IBM Plex Mono. Decide in `T3.2`; if adopted, `admin` and `users
|
||||
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
|
||||
@@ -617,28 +665,90 @@ Two secondary corrections while this is open:
|
||||
|
||||
---
|
||||
|
||||
## 11. What `T3.2` should actually do, in order
|
||||
## 11. `T3.2` — what was done, and how it was proved
|
||||
|
||||
Written as a sequence because the ordering is what keeps the screenshot diff empty.
|
||||
Done in this order, because the ordering is what kept the diff empty:
|
||||
|
||||
1. **Delete the dead `var()` fallbacks** (§7, 12 sites). Provably invisible; shrinks the raw
|
||||
count before anything risky starts.
|
||||
2. **Add the new canonical tokens** to `theme-light.css` — §2a and §2b — without touching any
|
||||
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. **Convert `console.css`, `work-package-suite-styles.css`, `wp-creation-styles.css` `:root`
|
||||
blocks to aliases**, one sheet per commit-sized step. Names stay, literals go. Verify §9's
|
||||
20 JS-read names still resolve.
|
||||
4. **Convert `wp-chrome.css`'s two `.wp-chrome` blocks** to aliases, preserving the
|
||||
light/dark switch mechanism exactly.
|
||||
5. **Convert `wp-sidenav.css`'s 31 raw literals** to the appbar tokens. This is the sheet with
|
||||
the largest gap between stated intent and mechanism, so it is also the most satisfying.
|
||||
6. **Convert the raw literals in rules**, sheet by sheet, using §5. Skip everything §7 lists.
|
||||
7. **Leave every §8 near-duplicate as its own canonical token.** Do not pick winners.
|
||||
8. Re-run `tests/baseline_shots.py` at both widths on all 7 pages and diff against wave 2.
|
||||
Capture `admin` and `users` **twice** — trap 2.
|
||||
9. Update this document's §2 to what was actually built, per `T3.2`'s fourth done-when.
|
||||
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.
|
||||
|
||||
### Backlog entries this task raises
|
||||
### 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 |
|
||||
|---|---|---|
|
||||
@@ -647,6 +757,10 @@ Written as a sequence because the ordering is what keeps the screenshot diff emp
|
||||
| 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` |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user