fix(ui): answer forced colours and reduced motion, and declare color-scheme - #578
Merged
Conversation
…scheme Three platform settings the grid left to the consumer, only one of which a consumer could actually fix from outside. FORCED COLOURS. A range selection was invisible. The fill is a translucent background-image; forced colours drop the image and force the colour underneath to Canvas, so an eleven-cell selection measured identical to no selection — every cell rgb(255,255,255) on rgb(0,0,0), with only the single FOCUSED cell marked. The answer is the system pair Highlight/HighlightText, which is honoured inside the block rather than forced away. It needs `forced-color-adjust: none` to land, and that is the interesting part: without it Chromium paints a text BACKPLATE — an opaque Canvas rectangle behind text sitting over a background image, which a selected cell counts as — and every word inside the fill came out a solid white block. The computed styles were identical with and without it; only a screenshot told the two apart, and dropping the background-image explicitly does NOT lift the backplate (tried first). Opting out is safe only because the colours are the system's, so the pair travels together in one rule with the reason written above it. Descendants inherit the cell's colour so a red P&L cannot paint itself onto the fill, and disabled menu items take GrayText. Everything else in the grid survives forced colours unaided, which the audit established before any of this was written: the border cage carries the structure, the row-select glyph is drawn in currentColor, the focus ring is an outline the UA forces to CanvasText, and the frozen edge falls back to the pinned cell's own border when its gradient is dropped. REDUCED MOTION. Three animated declarations, no way out, and not something a consumer can patch without re-implementing rules they do not own. All three are decoration, so all three are switched off. The guard is structural rather than a spot check: every rule in the file that animates must be named in the reduce block, so the next transition added cannot ship without one. COLOR-SCHEME. Only pretable.css declared it, so an Excel- or Material-themed grid kept light scrollbars and a light <select> popup inside a dark app — the one part of the surface a theme cannot reach with a custom property. Both now declare it, Material in each mode it ships, and contract.test.ts grew a THEME_FILES constant so a theme added tomorrow inherits every theme guard rather than the ones whose inline copy was remembered. Verified in a browser at 20 selected cells across the hero's text, numeric and P&L columns: white on the Highlight fill, no backplate, the focused cell ringed in HighlightText. Five mutations fail the guards (block deleted, author colours instead of system ones, backplate opt-out removed, a new animation with no reduce entry, material dark dropping color-scheme), and the browser proof fails when the answer is taken out. format, lint, typecheck, api:check, test, the 29-spec bench suite and 64 website e2e specs all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
blove
enabled auto-merge (squash)
September 4, 2026 16:41
Contributor
Vercel preview readyPreview: https://pretable-9pnyy300w-cacheplane.vercel.app Updated automatically by the |
Merged
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.
Three platform accessibility settings the grid left to the consumer — only one of which a consumer could actually fix from outside.
1. Forced colours: a range selection was invisible
The selection fill is a translucent
background-image, and forced colours drop the image and force the colour underneath toCanvas. Measured on the live grid underforced-colors: activewith eleven cells selected:rgb(255,255,255)rgb(0,0,0)rgb(255,255,255)rgb(0,0,0)Only the single focused cell was marked. In High Contrast, "what is ⌘C about to copy?" had no answer on screen.
Selected cells now take
Highlight/HighlightText— the pair the platform guarantees against each other, and which are honoured inside aforced-colorsblock rather than forced away. Disabled menu items takeGrayText.The part worth reading:
forced-color-adjust: noneIt is load-bearing, not an escape hatch. Without it Chromium paints a text backplate — an opaque
Canvasrectangle behind text that sits over a background image, which a selected cell counts as — and every word inside the fill rendered as a solid white block:The computed styles were identical in both cases (
background-color: rgba(5,0,73,0.8),color: rgb(255,255,255)); only a screenshot told them apart. Settingbackground-image: noneexplicitly does not lift the backplate — that was the first thing tried. Opting out is safe only because the colours in the same rule are the system's, so the cell still paints in the user's palette; the two travel together with the reason written above them.Descendants inherit the cell's colour, so a red P&L or an amber badge inside a selection cannot paint itself onto the Highlight fill at whatever contrast it happened to have.
What needed nothing
The audit ran before any of this was written, and cleared most of the grid:
Canvas;currentColor, so they survive;outline, which the UA forces toCanvasText;border-right.2. Reduced motion
grid.cssanimated three things (a twisty's 120ms rotate, two 0.1s icon fades) and offered no way out — and unlike the other two items, this is not something a consumer can patch without re-implementing rules they do not own. All three are decoration, so all three are switched off.The guard is structural rather than a spot check: every rule in the file that declares a
transitionoranimationmust be named in the reduce block, so the next one added cannot ship without an entry.3.
color-schemeOnly
pretable.cssdeclared it. An Excel- or Material-themed grid in a dark app kept light scrollbars and a light<select>popup — the one part of the surface a theme cannot reach with a custom property. Both now declare it, Material in each mode it ships.contract.test.tsalso grew aTHEME_FILESconstant (the list was inlined in three places), so a theme added tomorrow inherits every theme guard rather than the ones whose inline copy someone remembered to update.Verification
Browser, production build, 20 cells selected across the hero's text, numeric and P&L columns: white text on the Highlight fill, no backplate, the focused cell ringed in
HighlightText.Mutations that fail the guards — each applied, run, and reverted:
That last one is a new bench spec that emulates forced colours and asserts a selected cell does not paint as an unselected one — the test that would have caught the original defect.
pnpm format,lint,typecheck,api:check,test, the 29-spec bench suite, and 64 website e2e specs all pass. The bench suite is in the list this time: leaving it out is what broke #577.🤖 Generated with Claude Code