Repository navigation
fix(a11y): /dashboard/cli-code failed the project's own bar, unaudited - #71
Merged
Merged
Conversation
The product audit drove the shipped production build and found this page
carrying four blocking axe nodes, in BOTH themes and at every width it
measured — against a project bar of zero critical/serious:
select-name critical x2 both filter selects, 1280 and 375
color-contrast serious x2 dark 4.14 / 3.25, light 3.74 / 2.92
The selects had no accessible name at all: no id, no aria-label, and the
two VISIBLE <label>s had no htmlFor. Each one's first option is "All", so
a screen reader announced two unnamed combo boxes both saying "All".
Bound each label to its select — the labels are visible here, so this is
the htmlFor/id pair rather than the visually-hidden FilterSelect the logs
page uses.
The contrast nodes are two separate defects:
- the "no active providers" banner at 2.92:1 in light — the one line a
first-run user with nothing configured most needs to read, and the
least readable text on the page. amber-600 -> amber-800 (6.3:1).
- brand coral used as TEXT on a tinted surface, the same failure mode
#55 fixed. globals.css already ships --color-primary-on-tint for
exactly this, with the reasoning written down in Sidebar.tsx; these
two call sites used plain text-primary.
The gate's coverage was the real finding. tests/e2e/a11y.spec.ts audited
seven paths, NEITHER of this release's two new surfaces among them, and
A11Y_VIEWPORTS started at 768 so 375 was never swept. Both pages and the
phone width are in it now, and the test title no longer claims a range it
does not cover.
tests/unit/cli-code-page-a11y.test.tsx is the fast half, so a regression
fails in the unit suite instead of waiting for the nightly axe job.
Proven failable: removing the two htmlFor attributes takes it from
2 pass to 2 fail.
cli-code + workspaces a11y 3 pass / 0 fail
typecheck:core exit 0, 0 errors; eslint exit 0; prettier clean
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
The product audit drove the shipped production build with axe-core 4.13.0 and found this page carrying four blocking nodes, in both themes and at every width it measured — against a project bar of zero critical/serious:
select-namecritical ×2,color-contrastserious ×2select-namecritical ×2,color-contrastserious ×2select-namecritical ×2passes 25, violations 2, incomplete 0— real failures, not gradient "incomplete" noise.select-name(critical)Both filter selects had no accessible name at all: no
id, noaria-label, noaria-labelledby, not wrapped — and the two visible<label>s had nohtmlFor. Each select's first option is "All", so a screen reader announced two unnamed combo boxes both saying "All".Bound each label to its select. The labels are visible here, so this is the
htmlFor/idpair rather than the visually-hiddenFilterSelectthe logs page uses — that component exists to supply a name the UI does not show, which is not this case.color-contrast(serious) — two different defectsThe 2.92:1 node is the worst thing here. It is the amber banner that tells a brand-new user with nothing configured what to do next — the most load-bearing text on the page for a first run, and the least readable.
amber-600→amber-800takes it to 6.3:1.The other three are brand coral used as text on a tinted surface — the same failure mode #55 fixed.
globals.cssalready ships--color-primary-on-tintfor exactly this, with the reasoning written down inSidebar.tsx("measured 4.43:1 in dark theme… the theme-aware shade"). These call sites used plaintext-primary.The gate's coverage was the real finding
tests/e2e/a11y.spec.tsaudited seven paths — and neither of this release's two new surfaces was among them.A11Y_VIEWPORTSstarted attablet(768), so 375 was never swept, which is why a violation that reproduces at phone width could ship.Both pages and the phone width are in it now, and the test title no longer claims a range it does not cover (
768–1440px→375–1440px).Tests
tests/unit/cli-code-page-a11y.test.tsxis the fast half, so a regression fails in the unit suite instead of waiting for the nightly axe job. It renders the page in the no-provider state — the one that shows the amber banner — and asserts zero axe violations, plus a direct check that every<select>has a name by one of the four legitimate mechanisms.Proven failable rather than assumed: removing the two
htmlForattributes takes it from 2 pass to 2 fail.cli-code-page-a11y+workspaces-page-a11ytypecheck:coreeslint --max-warnings=0prettier --checkjsdom has no layout, so contrast is "incomplete" in the unit test — that half is covered by the e2e sweep this PR extends.
Not fixed here: the light-theme primary button falls to 3.96:1 over the violet end of
--grad-brand, which axe reports as incomplete because it is a gradient. That is a brand-token decision across every primary button in the product, not a cli-code defect, and it deserves its own change.🤖 Generated with Claude Code