Skip to content

Fix the accessibility findings and enforce the rules - #14

Merged
blakebauman merged 1 commit into
mainfrom
fix/accessibility-findings
Sep 7, 2026
Merged

blakebauman merged 1 commit into
mainfrom
fix/accessibility-findings

Conversation

@blakebauman

Copy link
Copy Markdown
Owner

Clears the accessibility debt left by #13 and puts the rules back at error severity in biome.json. They were set to warn there only so the findings stayed visible rather than being switched off; this makes them enforced, so regressions fail CI.

Nine real defects

ListItem Carried role="button" and tabIndex={0} unconditionally, so rows rendered without an onClick — K8sResourcesPage renders them that way — sat in the tab order announcing themselves as controls that did nothing. Now a real <button> when interactive and a plain <div> when not, which also drops the hand-rolled Enter/Space handler for native behaviour.
RowMenu onClick was attached unconditionally while role, tabIndex and onKeyDown were all gated on selectable — a click target with no keyboard path whenever a row was not selectable.
State filter A div with role="group" wrapping checkboxes. It is a genuine form-control group, so it is now a <fieldset> with an sr-only <legend>.
LabelChips aria-label on a bare div, where it is never exposed. The chips are a list, so <ul>/<li>.
Status dots ConsolePanel and ExecTerminal labelled a dot that duplicates the adjacent status text, so the state was announced twice. Decorative, now aria-hidden.
Four chart SVGs No accessible name. AreaChart and RingGauge are decorative — the value sits beside them as text — so they are aria-hidden. ChartsPanel and LiveSparkline are the only carriers of their data, so they get role="img" and a <title> stating the series, latest value and peak.

Seven false positives

Suppressed individually with the reason named, rather than switching the rules off:

  • The xterm host onClick only forwards stray clicks on the padding; keyboard users reach the terminal through xterm's own textarea, already in the tab order.
  • RowMenu's two inner wrappers exist solely to stop the row click reaching their children — the checkbox and menu trigger are the real controls — and the outer row's four selectable ternaries cannot be correlated by the rule.
  • SegmentedControl and the sidebar mode switcher are button groups, which is ARIA role="group". useSemanticElements suggests <fieldset>, which is for grouping form controls and would be wrong here.

RowMenu and SegmentedControl use file-level biome-ignore-all because the flagged element is the root of a return, where a JSX comment cannot precede it.

Verification

biome ci passes with the a11y rules at error, plus tsc --noEmit and the production build.

Checked in the browser against a live engine, since three of these change DOM structure and could shift layout: the <fieldset> renders with no default border or padding, the label chips wrap with no list bullets or indentation, row selection and the detail panes still work, and the sparklines render.

Not verified: the ListItem change on K8sResourcesPage, as no cluster was reachable. It compiles and typechecks, and that consumer passes no onClick, so it takes the plain-div branch.

🤖 Generated with Claude Code

https://claude.ai/code/session_015dD6k7ZHtNg7d44xnKLMjH

All sixteen a11y findings from the Biome adoption are resolved, and the rules
are back at their recommended error severity in biome.json — they were set to
warn only so that PR could land without hiding them.

Nine were real defects:

- ListItem carried role="button" and tabIndex={0} unconditionally, so rows
  rendered without an onClick — K8sResourcesPage renders them that way — sat in
  the tab order announcing themselves as controls that did nothing. It now
  renders a real <button> when interactive and a plain <div> when not, which
  also drops the hand-rolled Enter/Space handler in favour of native behaviour.
- RowMenu attached onClick unconditionally while role, tabIndex and onKeyDown
  were all gated on `selectable`, leaving a click target with no keyboard path
  whenever a row was not selectable.
- The container state filter was a div with role="group" around checkboxes; it
  is a genuine form-control group, so it is now a <fieldset> with an sr-only
  <legend>.
- LabelChips put aria-label on a bare div, where it is never exposed. The chips
  are a list, so they are now <ul>/<li>.
- The ConsolePanel and ExecTerminal status dots carried an aria-label
  duplicating the adjacent status text, so the state was announced twice. They
  are decorative and now aria-hidden.
- Four chart SVGs had no accessible name. AreaChart and RingGauge are
  decorative — the value sits beside them as text — so they are aria-hidden.
  ChartsPanel and LiveSparkline are the only carriers of their data, so they
  get role="img" and a <title> stating the series, latest value and peak.

Seven were false positives and carry suppressions naming the reason rather than
being switched off globally:

- The xterm host div's onClick only forwards stray clicks on the padding;
  keyboard users reach the terminal through xterm's own textarea, which is
  already in the tab order.
- RowMenu's two inner wrappers exist solely to stop the row click reaching
  their children — the checkbox and menu trigger are the real controls — and
  the outer row's four `selectable` ternaries cannot be correlated by the rule.
- SegmentedControl and the sidebar mode switcher are button groups, which is
  ARIA role="group"; useSemanticElements suggests <fieldset>, which is for
  grouping form controls and would be wrong.

RowMenu and SegmentedControl use file-level biome-ignore-all because the
flagged element is the root of a return, where a JSX comment cannot precede it.

Verified in the browser against a live engine: the fieldset renders with no
default border or padding, the label chips wrap with no list bullets or
indentation, row selection and the detail panes still work, and the sparklines
render.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015dD6k7ZHtNg7d44xnKLMjH
@blakebauman
blakebauman merged commit 787560f into main Sep 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant