fix(#715): the cod-skeleton HUD failed AAA at 4.51:1 — measured, then fixed - #722
Merged
Conversation
… fixed #721 excluded /game/cod-skeleton from the route-sweeping contrast spec because the sweep cannot settle on a GPU-less runner (#719), and wrote the cost into the exclusion itself: that page's contrast became unmeasured. This pays that debt, and the debt turned out to be real. MEASURED. The HUD is three chips — stance badge, quality select, controls hint — all `text-base-content` on `bg-base-300/70`, floating over an UNCONSTRAINED WebGL scene. Bracketing that scene between black and white, worst endpoint per theme: /70 light 5.00:1 dark 4.51:1 <- both FAIL the 7:1 gate /85 light 7.24:1 dark 7.43:1 <- passes by 1.03x /90 light 8.14:1 dark 8.78:1 <- shipped /85 was rejected for the reason globals.css:1716 already rejected it for the twin nav: a margin that thin is erased by one theme tweak. Each theme failed at the endpoint where the scene works against the text — dark over white, light over black — and the light case is not even adversarial, since ProceduralSky hour=16.5 puts a bright sky directly under the top-left and top-right chips. HOW IT IS MEASURED, and why not the obvious ways. jsdom + axe would be vacuous: this repo already ships three tests named for colour contrast that have never measured a ratio, because DaisyUI's stylesheet never applies there and the node lands in `incomplete`, which toHaveNoViolations() ignores. Loading the real route reintroduces #719. Hand-computing the blend measures a colour the browser never paints — Tailwind emits color-mix(in oklab, ...), and that trap once read a real 4.13:1 as 1.03:1. So: load the route for its STYLESHEET with WebGL denied, so isWebGLAvailable() returns false, FallbackPanel renders, and no WebGLRenderer is ever constructed. Inject the real utility classes, let the browser resolve them, and read back through a 1x1 canvas — the only place OKLCH and color-mix become sRGB correctly. THREE HONESTY GUARDS, EACH PROVEN TO FIRE. This repo keeps getting bitten by tests that cannot fail, so each was mutated on purpose: - alpha guard — surface must be translucent. Fired immediately and correctly on the first draft, which probed on /themes/ and got rgba(0,0,0,0); it caught me measuring a rule that page never loaded. Re-verified by pointing it at a nonexistent utility: 3 failed. - token guard — base-300 must resolve and differ from base-content. - source guard — the classes measured must be the classes shipped. FIRST VERSION WAS TOO WEAK: restyling one of three chips left it green, because the other two still matched. Now asserts the COUNT and rejects any chip drifting to another alpha. Re-mutated both ways: restyle one chip -> 1 failed; revert one chip to /70 -> 1 failed. The exclusion in color-contrast.spec.ts now names this spec, the way /chatt names twin-glass-contrast.spec.ts, so the coverage handoff is visible from both ends. Local harness note for the next person: measuring this needs a REAL root build (`NEXT_PUBLIC_BASE_PATH= DISABLE_BASE_PATH=true pnpm build`, then `serve out`). DISABLE_BASE_PATH alone is not enough — NEXT_PUBLIC_BASE_PATH from .env wins, absolute gotos then hit the 404 page, and every measurement reads transparent. 437 files / 4611 tests, type-check and lint clean. Closes #715 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 14, 2026
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pays the coverage debt #721 took on, and the debt was real: the HUD was failing AAA.
What it found
/game/cod-skeletonwas excluded from the route-sweeping contrast spec because the sweep cannot settle on a GPU-less runner (#719), and that exclusion said in its own body that the page's contrast was now unmeasured. Measured now — the three HUD chips (stance badge, quality select, controls hint) are alltext-base-contentonbg-base-300/70floating over an unconstrained WebGL scene. Bracketing that scene between black and white, worst endpoint per theme:/70/85/90/85was rejected for the reasonglobals.css:1716already rejected it for the twin nav — a 1.03× margin is erased by one theme tweak. Each theme fails at the endpoint where the scene works against the text, and the light case is not even adversarial:ProceduralSky hour={16.5}puts a bright sky directly under the top-left and top-right chips.How it measures, and why not the cheap ways
jsdom + axe would be vacuous — DaisyUI's stylesheet never applies there, the node lands in
incomplete, andtoHaveNoViolations()ignores it. Three tests in this repo named for colour contrast already ship green having never measured a ratio. Loading the real route reintroduces #719. Hand-computing the blend measures a colour the browser never paints, since Tailwind emitscolor-mix(in oklab, …)— that trap once read a real 4.13:1 as 1.03:1.So the spec loads the route for its stylesheet with WebGL denied via
addInitScript, soisWebGLAvailable()returns false,FallbackPanelrenders, and noWebGLRendereris ever constructed. Then it injects the real utility classes, lets the browser resolve them, and reads back through a 1×1 canvas — the only place OKLCH andcolor-mixbecome sRGB correctly. Plus a sign-consistency check, so the interior of the bracket cannot dip between two green endpoints.Three honesty guards, each proven to fire
Every one was mutated on purpose, because a guard never seen to fail is decoration:
/themes/and readrgba(0, 0, 0, 0), because Next.js splits CSS per route andbg-base-300/70ships only in the chunk for the route that uses it. It caught me measuring a rule the page never loaded. Re-verified against a nonexistent utility → 3 failed.base-300must resolve and differ frombase-content./70→ 1 failed.Verification
Harness note for the next person, added to the spec header: this needs a real root build (
NEXT_PUBLIC_BASE_PATH= DISABLE_BASE_PATH=true pnpm build).DISABLE_BASE_PATHalone is not enough —NEXT_PUBLIC_BASE_PATHfrom.envwins, absolute gotos then hit the 404 page, and every measurement reads transparent.Closes #715
🤖 Generated with Claude Code