Repository navigation
A11Y-001: the kitchen display's text is readable on its ambient wash - #1006
Conversation
The kitchen display (/display) paints a time-of-day gradient behind
every tile, on by default. Its brightest stops (pink-300 at dawn,
sky-300 in the morning, orange-400 in the evening, amber and cyan in
others) put the display's text at 1.7 to 3.7:1, measured from the
rendered pixels with the text hidden, on a seeded local household at
390 and 1280 px; most of it was white/50-60. axe could not see it: the
wash is a fixed -z-10 layer, so axe measured against the page's body
colour instead ("passes" in the dark theme, "1.04" in the light one).
- The display's own ink (#0b1020) lies at 75% over the wash and its
glows, so each theme keeps its hue.
- Body text on the display, and on its reconnect and error screens, is
white/70 or more (50 classes, from /25-/60), and so is the timer
input's placeholder.
- The floating hints pill ("20 chores due today — who's on it?") is
bg-black/70, not /45: over a pale photo at 390 px it read 1.4:1.
tests/the-kitchen-display-text-is-readable-on-its-ambient-wash.test.ts
reads the stops and glows (lib/display/ambient.ts), the scrim
(display-grid.tsx) and every bare text-white/NN on the display, then
composes the faintest text on a white/10 tile, and white on the
white/15 setup button, over each theme's brightest colour: 4.5:1 at
worst. The hints pill clears 4.5:1 over a white pixel. Without the
scrim the same text reads under 3:1 (the control).
On main, 3 of its 4 cases fail.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Audit follow-up on 06754d5 — bounded repair/test work for the existing owner. P2: the ambient contrast test omits nested surfaces and hover states. The 10px timer duration text (white/70) is on a white/10 button, white/20 hovered, inside the display-grid white/6 tile. Using the same morning #7dd3fc stop and 75% black scrim, composing all layers predicts about 4.27:1 idle and 3.43:1 hovered. The 14px semibold setup-guide label is white on white/15, white/25 hovered, inside its own white/7 card; it is outside the grid. That predicts about 5.72:1 idle and 4.41:1 hovered. These are source-derived bounds, not observed pixel failures. Please extend the existing test to derive and compose the actual parent/child layers and interactive states, adjust both controls to clear 4.5:1, and verify the timer picker and setup-guide interactions in the bright ambient themes. Preserve keyboard/focus behavior and existing handlers. The loading text's animate-pulse also changes effective opacity, so do not treat the bare white/70 class as a full-animation guarantee. Scrim ordering and pointer exclusion look coherent. Photo/recipe contrast remains the separately acknowledged open scope. Return the repair commit and exact-head tests; distinguish arithmetic checks from rendered measurements. No deployment or production changes. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KP9rt5rVQ9jDMpZp2xBy3K
…r them The audit of #1006 found the ambient-wash test composed each text over one surface. The timer picker's duration was white/70 on a white/10 button (white/20 hovered) inside the grid's white/6 tile: 4.27:1 idle and 3.43:1 hovered on the morning stop. The setup guide was white on white/15 (white/25 hovered) in its own white/7 card: 4.41:1 hovered. And a white/70 under animate-pulse is white/35 at the pulse's faintest frame. tests/helpers/display-surfaces.ts now reads each text's parents from the JSX with the TypeScript parser, follows a component up to where it is mounted (KitchenTimers in WidgetBody in a grid tile; the setup card above the grid), and composes every surface idle, hovered, focused and at a looping animation's faintest frame (from Tailwind's own keyframes), keeping cn() conditions consistent across one rendering. Over every text on the wash, it found, and this fixes: - timer preset duration white/70 -> white: 6.73 idle, 5.13 hovered - "Add timer" white/80 -> white: hovered 3.95 -> 5.13 - setup guide hovered white/25 -> white/20: 4.41 -> 5.01 - a finished timer pulsed the whole card, so its label read 2.89 and "Done!" 2.44 at the faintest frame; the pulse is now a layer under the words (6.34 and 4.95 at its brightest) - loading text under animate-pulse white/70 -> white: 3.19 -> 5.31 at the faintest frame - the schedule's "now" row: time emerald-300 -> emerald-200 (4.41 -> 5.25) and its Now badge emerald-300 on emerald-400/20 -> emerald-100 on emerald-500/20 (3.37 -> 4.90) Those are arithmetic (alpha compositing, WCAG luminance). The new spec, tests/e2e/the-kitchen-display-text-is-readable-on-its-ambient-wash.spec.ts, measures the same controls in Chromium: real DisplayShell, setup card and timers with the app's compiled stylesheet, the wash painted flat in each of the nine themes' brightest colour, two screenshots per text (with and without it). On the morning stop it reads 5.14 for the hovered duration, 5.05 for the hovered guide, 4.97 for "Done!"; on the code before this commit it read 3.41, 4.44 and 2.43 at the faintest frame. Keyboard and pointer behaviour (Tab, Enter, Space, the picker's close, Got it's write) is exercised there unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KP9rt5rVQ9jDMpZp2xBy3K
|
Re 01:21Z (nested surfaces and hover states), at What changed
Arithmetic, worst stop sky-300 under the 0.75 scrim (before → after)
The finished label goes from 2.89 to 6.34, loading text from 3.19 to 5.31, the now-row time from 4.41 to 5.25, and the Now badge from 3.37 to 4.90. Rendered. A new spec drives the real DisplayShell, setup card and KitchenTimers on the compiled CSS, in Chromium at 1280×900, DPR 2, dark. It measures glyph pixels against the same pixels without text, in all nine wash themes. Morning: duration 6.75 idle and 5.14 hovered, guide 5.79 and 5.05 hovered, and every theme clears 4.5. Run on the pre-fix code, it measures 4.25 and 3.41 for the duration and 4.44 for the guide hovered. A behaviour case checks that Tab, Enter and Space start and reopen a timer and that Got it dismisses the card. Checks: Not covered: the page's own radial tint is in the rendered spec only. Rendering is dark theme at one viewport. Text over photos stays out of scope as before. Generated by Claude Code |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KP9rt5rVQ9jDMpZp2xBy3K
A11Y-001: the kitchen display (
/display) is the open "/display at narrow widths" item. The defect is not about width or theme.The defect
The display paints a time-of-day gradient behind every tile. It is on by default (
ambient: trueinlib/display/ambient.ts). Each theme has bright stops:Most of the display's text is
text-white/50–/60(50 of its 66text-white/NNclasses were below /70).axe could not see it. The wash is a
fixed -z-10layer, so axe measured against the page's body colour instead:#f6f7fc", which is not what is on screen either.Measured from the pixels instead: a seeded local household, Chromium, the evening theme. Every visible text leaf is recorded with its computed colour, all text is then made transparent, and the background is sampled under each text box. The darkest-to-lightest pixel is the worst case. The text colour is composited over it as the browser would.
mainThe fix
#0b1020) lies at 75% over the wash and its glows (a newfixed -z-10layer after them, only when ambient is on). Each theme keeps its hue, at a quarter of the strength.text-white/70or more: 50 classes went from /25–/60, plus the timer input's placeholder. Hover states that were already /70–/100 are unchanged.bg-black/70, not/45, so it holds 4.5:1 even over a white photo pixel.The non-ambient display (a solid
#0b1020) and the photo backdrop mode are otherwise unchanged.Tests
tests/the-kitchen-display-text-is-readable-on-its-ambient-wash.test.tsreads, from the source:lib/display/ambient.ts);display-grid.tsx);text-white/NNon the display (components/display/*.tsx, the page and its error screen).It then composes, the way the browser does:
bg-white/10tile (the strongest tint behind body text), over each theme's brightest colour: 4.5:1 at worst required;bg-white/15button, over the same colours;Non-vacuity: dawn's and morning's bright stops are found, ambient is on by default, there is a scrim, and there are more than 40 text classes. The control is the same text without the scrim, under 3:1.
mainNot covered: text over photographs
The featured recipe tile puts its label ("Featured Recipe",
white/70), its name and its category over the recipe photo. The photo's overlay (from-black/80 via-black/25 to-transparent) leaves bright plate pixels behind the label: 1.00 and 2.34 at the worst pixel at 390 and 1280 px. At 390 px the text block covers most of the short tile, so holding 4.5:1 against every pixel would take about 70% black over nearly the whole photo. That is a design decision, so I have left it for you. The options are:The photo backdrop mode (
background: 'photos') has the same question.The hints pill's new value is verified by the arithmetic in the test. A pixel reading of it was inconclusive, because the pill fades between hints.
No migration, configuration or dependency change.
🤖 Generated with Claude Code
https://claude.ai/code/session_01TRY21ZKsFrfB3qtoP972A4
Generated by Claude Code