diff --git a/.planning/NOTES.md b/.planning/NOTES.md index 523a71e..e153800 100644 --- a/.planning/NOTES.md +++ b/.planning/NOTES.md @@ -1954,3 +1954,77 @@ Nine things worth keeping: and every one was a *different* mechanism. A finding at 110 nodes hides them; at 3 they are individually obvious. Cheap tail, only reachable from the other side of the main fix. + +## A role, not a value, decides whether a colour can be fixed + +Plan 008 phase 2, third landing: the two findings the contrast pass had +recorded as too big to fix in it, fixed — plus the check for the trap +that has now cost four sessions. + +The generalisation: **a token that is used for two different jobs will +be wrong at one of them, and no amount of choosing a better value +fixes it.** `--yj-error` was "the colour of error", which is two +questions. As the *background of a danger button* it wants to stay red +in every theme, and it does. As *the word `failed` on a surface* it +cannot be one value at all — a single colour cannot clear 4.5:1 against +both a near-black and a near-white background, which is why the fixed +set measured 2.31–4.28:1 on nearly every combination. Splitting the +token by the question it answers made both answerable; picking better +hexes never would have. + +The same shape twice more in the same landing: + +- **A fill's foreground cannot be written down**, because the accent is + a colour picker. `color: #000` is right for the current default + yellow and wrong for a navy one. `readableOn()` computes it — white + where white clears 4.5:1, black otherwise — which keeps a red danger + button conventional (4.51:1) and flips a green one (3.45:1). +- **`var(--yj-bg-base)` as a foreground is a token used for the wrong + meaning.** Two accent buttons did that. It reads as "the opposite of + the accent" and it is not: it inverts with the ramp, so the light + theme rendered white on yellow at 1.43:1. The bug is not the value, + it is that the *name* did not mean what the call site needed. + +Six more things worth keeping: + +- **The picture and the number disagreed, and the number was mine — + again.** I recorded "the header and player chrome stay dark while the + body goes light" as a finding, in the plan and in these notes. It is + false: `.top-bar` is `#e9ecef` and `.sidebar` `#f8f9fa` under the + light ramp, and a re-taken screenshot agrees with the DOM. The + original was captured before the theme had propagated. That is the + third time in two passes a screenshot read at the wrong *moment* + produced a confident wrong claim, after a spec reading the DOM before + a fetch and a sweep that had moved the app to another ramp. **A + screenshot has a timestamp and a state; check both before quoting + it.** +- **A `color:` regex matches `border-color:`.** Twice in one landing — + 3 borders while rewriting semantic text, then 30 more while rewriting + accent text. Both caught by grepping the *result*, not by any test, + because a border in the wrong shade fails nothing and looks fine. + When a mechanical rewrite is the right tool, the review is a grep of + what it did, not a run of the suite. +- **A fix at the ubiquitous case makes the rare ones findable.** The + greyscale fix took the dark ramp to 0 and the light ramp from 50 to a + list short enough to read individually — at which point every + remaining item was a *different* mechanism, and two of them were the + findings above. A queue of 110 hides its own structure. +- **Knowledge that has been ignored three times is not a knowledge + problem.** The backtick-in-a-`css`-comment trap is documented in + `CLAUDE.md`, in the skill and here; I read it twice in the session it + then cost a cycle in. It is `make css-check` now — a pre-commit hook + and a CI step. The detection is exact rather than heuristic: if a + backtick in a comment closed the literal early, the text the parser + took as the literal contains an unterminated `/*`, and nothing else + produces that. +- **The value of that check is the sentence, not the failure.** `tsc` + already failed on it — with `Class static side 'typeof NowPlaying' + incorrectly extends base class static side` and `Property 'scroll' + does not exist on type 'CSSResult'`, pointing at a line of prose. The + check was verified by breaking a file on purpose and reading both + reports side by side, which is also the only way to know it fires. +- **A change can be invisible to the tier that looks the most like it + covers it.** `make ui-visual` passed unchanged through a whole + palette rewrite, twice, because the component tier has no `:root` and + renders the fallbacks. The tier that *did* catch things was a unit + test over the palette table and a probe against the running app. diff --git a/.planning/plans/active/008-the-last-audit.md b/.planning/plans/active/008-the-last-audit.md index 89d6052..d20a2e7 100644 --- a/.planning/plans/active/008-the-last-audit.md +++ b/.planning/plans/active/008-the-last-audit.md @@ -325,23 +325,60 @@ access to any value by not being able to. **Stays dropped.** #### Two things the measurements found that are not in the audit -Both are bigger than what they were found under, and neither is fixed: +Both were bigger than what they were found under. Both are now fixed — +see the third landing below. -- **The semantic colours are fixed across ramps, and a fixed colour +- **The semantic colours were fixed across ramps, and a fixed colour cannot serve a near-black and a near-white background.** `--yj-error` - is 3.42:1 on dark's surface and 2.55:1 on its elevated; `--yj-info` - is 3.10:1 and 2.31:1; success and warning fail on dark and light - both. As *backgrounds* under white text, success (3.45) and warning - (3.58) fail too. The fix is a per-ramp semantic palette, which is a - decision about the app's colour identity rather than a value. -- **The light ramp is not a supported theme.** With the greyscale fixed - it still has **50 failing nodes**: the accent yellow under white text - (1.43:1), the autotag diff's pale greens and reds on white - (1.36–2.59:1), and the header and player chrome staying dark while - the body goes light. Read in a screenshot — the "Low confidence pick" - banner is invisible and the primary button is white-on-yellow. This - is a design job, and the honest question it raises is whether the - light theme should ship at all in its current state. + measured 3.42:1 on dark's surface and 2.55:1 on its elevated; + `--yj-info` 3.10:1 and 2.31:1; success and warning failed on dark and + light both. As *backgrounds* under white text, success (3.45) and + warning (3.58) failed too. +- **The light ramp was not a usable theme.** With the greyscale fixed + it still had **50 failing nodes**: the accent yellow under white text + (1.43:1) and the autotag diff's pale greens and reds on white + (1.36–2.59:1). + +### Phase 2, third landing — the ramp reaches the semantic colours + +**2237 nodes across three ramps and twelve views, 0 failing.** + +The split is by the question a colour answers. A **fill** is "what +colour is a danger button" — red in every theme, unchanged. A **text** +colour is "what colour is the word *failed* on this background" — per +ramp, because one value cannot clear 4.5:1 against both a near-black and +a near-white surface. `bgOverlay` keeps the exception it already had on +the dark ramp. + +And every fill now carries a **computed foreground**, because the accent +is a colour picker and no fixed answer survives one: white if white +clears 4.5:1, else black. That keeps a red danger button white and +flips a green or amber one to black. Accent-as-text goes through +`accentTextOn()`, which mixes along the hue until it clears the ramp's +surface and stops — returning the accent *unchanged* on both dark +ramps, so the dark themes are visually untouched by that half. + +`make ui-test` 608 → **649**. + +#### Where this pass was wrong + +- **"The chrome stays dark while the body goes light" was mine, and it + was false.** I read it off a screenshot; the DOM says `.top-bar` is + `#e9ecef` and `.sidebar` `#f8f9fa` under the light ramp, and a + re-taken screenshot agrees. The first one was captured before the + theme had propagated. Third time in two passes that a screenshot read + at the wrong moment produced a confident wrong claim — and the second + time this pass that **the picture and the number disagreed and the + number was mine**. +- **A `color:` regex matches `border-color:`.** Twice: once rewriting + semantic text colours (3 borders) and once rewriting accent text (30 + more). A border is a fill, not text. Caught by grepping the result + rather than by any test, because nothing renders differently enough + to fail. +- **Two accent buttons took their foreground from `--yj-bg-base`**, + which inverts with the ramp — white on yellow at 1.43:1. That is not + a colour that was chosen badly; it is a token used for the wrong + meaning, and it only shows up in the theme nobody looks at. --- @@ -358,10 +395,8 @@ Two of them are not one-liners and should be treated as such: minimum at 800×600. Reflow at high zoom is the same question one variable over. It may want its own landing. - **The unnamed `