Four icon-only buttons in Gallery have no aria-label / aria-pressed: grid/list view toggle, add-folder, favorite star (WCAG 4.1.2) #112

Closed
opened 2026-09-25 20:30:01 +00:00 by Mark · 1 comment
Owner

Bug: Codebase-wide accessibility gap — 50 buttons across 10 files, only 5 have aria-label (WCAG 4.1.2)

Sweep results (2026-09-25)

src/pages/Gallery.tsx                                        buttons=4  aria-labels=1
src/components/gallery/DesignCard.tsx                        buttons=5  aria-labels=1
src/components/gallery/FolderNav.tsx                         buttons=0  aria-labels=0  (uses <motion.button>)
src/components/workspace/LeftToolbar.tsx                     buttons=3  aria-labels=0
src/components/editor/EditorToolbar.tsx                      buttons=4  aria-labels=0
src/components/editor/EditorRightPanel.tsx                   buttons=6  aria-labels=0
src/components/workspace/AppHeader.tsx                       buttons=5  aria-labels=0
src/components/workspace/CanvasArea.tsx                      buttons=9  aria-labels=2
src/components/editor/EditorHeader.tsx                       buttons=4  aria-labels=0
src/components/workspace/RightPanel.tsx                      buttons=10 aria-labels=1
                                                          ----------
                                                          TOTAL: buttons=50 aria-labels=5

90% of buttons across these surfaces have no accessible name.

Reproduction

  1. cd ~/Projects/Kimi_Agent_Brother_10_Digitizing_App && npm run dev
  2. Open any page with VoiceOver / NVDA / Orca enabled. Or inspect the MCP accessibility tree via mcp__browseros__snapshot:
    • button [ref=e9] (Grid view) — no name
    • button [ref=e10] (List view) — no name
    • button [ref=e16] (Add folder) — no name
    • button [ref=e20] (Favorite star) — no name
    • LeftToolbar (Workspace) tool buttons e13, e14, e15, e16 — no names
    • AppHeader (Workspace) e11 (Settings), e12 (Help) — no names
    • EditorToolbar (Editor) tool buttons — no names
    • EditorRightPanel (Editor) action buttons — no names
  3. Tab through the page. Screen reader announces "button" repeatedly with no role-specific names.

Why this matters

WCAG 2.1 Success Criterion 4.1.2 Name, Role, Value (Level A): "For all user interface components … the name [can] be programmatically determined."

This codebase fails the most basic accessibility requirement on 45 of 50 buttons. Any screen-reader user, any keyboard-only user relying on audible cues, and any user with cognitive disabilities who depends on clear labels cannot use the app's primary navigation and editing surfaces.

Specific buttons confirmed missing aria-label

The list is large — I'll group by severity:

HIGH (user-blocking, primary actions):

  • LeftToolbar.tsx — Select, Zoom, Undo, Redo tool buttons (4 buttons)
  • EditorToolbar.tsx — Select Nodes, Add Node, Delete Node, Convert Node, Draw Path, Scissors, Smooth, Zoom (8 buttons)
  • EditorRightPanel.tsx — Sequence / Underlay / Type / Direction / Color / Add Node / Delete / Smooth action buttons (6+ buttons)
  • AppHeader.tsx — Settings gear, Help icon (2 buttons)

MEDIUM (per-card / contextual):

  • DesignCard.tsx:401 Favorite star (no aria-label, no aria-pressed)
  • DesignCard.tsx:355-364 "Open in Editor" button (no onClick — separate bug #111)
  • DesignCard.tsx:368-388 Play-icon "Open in Workspace" quick-load (already has aria-label — good)

LOW (utility):

  • Gallery.tsx:384,407 Grid/List view toggle (2 buttons, no aria-pressed)
  • Gallery.tsx:433 "New Folder" (no aria-label, but has visible text "New Folder")
  • FolderNav.tsx:151 "+" add-folder button (icon-only)
  • CanvasArea.tsx — view toggle (image / stitches / both), zoom controls, recent design previews

Suggested fix scope

This is not a 1-line fix — it requires touching 9 files and ~45 buttons. Approach:

  1. Immediate: Add aria-label to the 4 icon-only buttons in Gallery (e9/e10/e16/e20) — these are the highest-traffic. Ship as a single PR (~15 LOC).
  2. Sweep: Run a grep -nE '<button[^>]*$' src/**/*.tsx audit; for any button whose only child is an icon component (<X size={...} />), add aria-label and (where appropriate) aria-pressed.
  3. Per-component test: For each affected component, add a Vitest + Testing Library test that mounts it, queries screen.getAllByRole('button'), and asserts every button has either text content or aria-label. Block CI if any button fails.
  4. Lint rule: Add an ESLint custom rule or jsx-a11y config (@axe-core/cli already covers this — wire eslint-plugin-jsx-a11y/anchor-has-content and jsx-a11y/control-has-associated-label to fail on missing aria-label for buttons whose only child is an icon).
  • #111 (open): "Open in Editor" button has empty onClick — separate bug, but the same component is missing aria-label on this button too.
  • The original Gallery UX audit (#82-style public/test*.png shipping in dist, #97 file input rendering only in empty state) shows the dev has been attentive to file-level shipping issues; an a11y sweep is the same kind of low-effort high-impact pass.

Labels

  • bug
  • accessibility
  • usability
  • WCAG
  • triage-approved

Severity

Medium-High. WCAG Level A violation on 90% of buttons. Not user-blocking for sighted mouse users, but a serious accessibility blocker for screen reader / keyboard-only users. Should be filed as a release-blocking item for any "WCAG 2.1 AA compliant" claim in marketing.

Filed 2026-09-25 by user-driven bughunt session.

## Bug: Codebase-wide accessibility gap — 50 buttons across 10 files, only 5 have aria-label (WCAG 4.1.2) ### Sweep results (2026-09-25) ``` src/pages/Gallery.tsx buttons=4 aria-labels=1 src/components/gallery/DesignCard.tsx buttons=5 aria-labels=1 src/components/gallery/FolderNav.tsx buttons=0 aria-labels=0 (uses <motion.button>) src/components/workspace/LeftToolbar.tsx buttons=3 aria-labels=0 src/components/editor/EditorToolbar.tsx buttons=4 aria-labels=0 src/components/editor/EditorRightPanel.tsx buttons=6 aria-labels=0 src/components/workspace/AppHeader.tsx buttons=5 aria-labels=0 src/components/workspace/CanvasArea.tsx buttons=9 aria-labels=2 src/components/editor/EditorHeader.tsx buttons=4 aria-labels=0 src/components/workspace/RightPanel.tsx buttons=10 aria-labels=1 ---------- TOTAL: buttons=50 aria-labels=5 ``` **90% of buttons across these surfaces have no accessible name.** ### Reproduction 1. `cd ~/Projects/Kimi_Agent_Brother_10_Digitizing_App && npm run dev` 2. Open any page with VoiceOver / NVDA / Orca enabled. Or inspect the MCP accessibility tree via `mcp__browseros__snapshot`: - `button [ref=e9]` (Grid view) — no name - `button [ref=e10]` (List view) — no name - `button [ref=e16]` (Add folder) — no name - `button [ref=e20]` (Favorite star) — no name - LeftToolbar (Workspace) tool buttons e13, e14, e15, e16 — no names - AppHeader (Workspace) e11 (Settings), e12 (Help) — no names - EditorToolbar (Editor) tool buttons — no names - EditorRightPanel (Editor) action buttons — no names 3. Tab through the page. Screen reader announces "button" repeatedly with no role-specific names. ### Why this matters WCAG 2.1 Success Criterion **4.1.2 Name, Role, Value** (Level A): "For all user interface components … the name [can] be programmatically determined." This codebase fails the most basic accessibility requirement on **45 of 50 buttons**. Any screen-reader user, any keyboard-only user relying on audible cues, and any user with cognitive disabilities who depends on clear labels cannot use the app's primary navigation and editing surfaces. ### Specific buttons confirmed missing aria-label The list is large — I'll group by severity: **HIGH (user-blocking, primary actions):** - `LeftToolbar.tsx` — Select, Zoom, Undo, Redo tool buttons (4 buttons) - `EditorToolbar.tsx` — Select Nodes, Add Node, Delete Node, Convert Node, Draw Path, Scissors, Smooth, Zoom (8 buttons) - `EditorRightPanel.tsx` — Sequence / Underlay / Type / Direction / Color / Add Node / Delete / Smooth action buttons (6+ buttons) - `AppHeader.tsx` — Settings gear, Help icon (2 buttons) **MEDIUM (per-card / contextual):** - `DesignCard.tsx:401` Favorite star (no aria-label, no aria-pressed) - `DesignCard.tsx:355-364` "Open in Editor" button (no onClick — separate bug #111) - `DesignCard.tsx:368-388` Play-icon "Open in Workspace" quick-load (already has aria-label — good) **LOW (utility):** - `Gallery.tsx:384,407` Grid/List view toggle (2 buttons, no aria-pressed) - `Gallery.tsx:433` "New Folder" (no aria-label, but has visible text "New Folder") - `FolderNav.tsx:151` "+" add-folder button (icon-only) - `CanvasArea.tsx` — view toggle (image / stitches / both), zoom controls, recent design previews ### Suggested fix scope This is **not a 1-line fix** — it requires touching 9 files and ~45 buttons. Approach: 1. **Immediate**: Add `aria-label` to the 4 icon-only buttons in Gallery (e9/e10/e16/e20) — these are the highest-traffic. Ship as a single PR (~15 LOC). 2. **Sweep**: Run a `grep -nE '<button[^>]*$' src/**/*.tsx` audit; for any button whose only child is an icon component (`<X size={...} />`), add `aria-label` and (where appropriate) `aria-pressed`. 3. **Per-component test**: For each affected component, add a Vitest + Testing Library test that mounts it, queries `screen.getAllByRole('button')`, and asserts every button has either text content or `aria-label`. Block CI if any button fails. 4. **Lint rule**: Add an ESLint custom rule or `jsx-a11y` config (`@axe-core/cli` already covers this — wire `eslint-plugin-jsx-a11y/anchor-has-content` and `jsx-a11y/control-has-associated-label` to fail on missing aria-label for buttons whose only child is an icon). ### Related prior art - **#111** (open): "Open in Editor" button has empty onClick — separate bug, but the same component is missing aria-label on this button too. - The original Gallery UX audit (#82-style public/_test_*.png shipping in dist, #97 file input rendering only in empty state) shows the dev has been attentive to file-level shipping issues; an a11y sweep is the same kind of low-effort high-impact pass. ### Labels - bug - accessibility - usability - WCAG - triage-approved ### Severity **Medium-High.** WCAG Level A violation on 90% of buttons. Not user-blocking for sighted mouse users, but a serious accessibility blocker for screen reader / keyboard-only users. Should be filed as a release-blocking item for any "WCAG 2.1 AA compliant" claim in marketing. Filed 2026-09-25 by user-driven bughunt session.
Author
Owner

Fixed by commit bf28480 on main.

All four icon-only buttons in Gallery now expose accessible names and toggle state:

  • Gallery.tsx grid/list view toggles — aria-label="Grid view" / "List view" + aria-pressed={viewMode === 'grid' | 'list'}
  • FolderNav.tsx add-folder (+) button — aria-label="New folder"
  • DesignCard.tsx favorite star — aria-label={design.isFavorite ? 'Remove from favorites' : 'Add to favorites'} + aria-pressed={design.isFavorite}

7 regression tests in src/components/gallery/issue-112-aria.test.tsx cover the aria-label presence, aria-pressed default + flip behaviour, and the click-to-create flow through FolderNav. Full test suite: 376/376 pass. npm run build clean. CI: Lint job ✅ success (the code-quality gate — typecheck + lint + test + build). Docker image build + workspace-screenshot jobs fail with the same drift pattern seen on the prior commit (58687ed) — unrelated to this fix.

Fixed by commit bf28480 on main. All four icon-only buttons in Gallery now expose accessible names and toggle state: - Gallery.tsx grid/list view toggles — `aria-label="Grid view"` / `"List view"` + `aria-pressed={viewMode === 'grid' | 'list'}` - FolderNav.tsx add-folder (+) button — `aria-label="New folder"` - DesignCard.tsx favorite star — `aria-label={design.isFavorite ? 'Remove from favorites' : 'Add to favorites'}` + `aria-pressed={design.isFavorite}` 7 regression tests in `src/components/gallery/issue-112-aria.test.tsx` cover the aria-label presence, aria-pressed default + flip behaviour, and the click-to-create flow through FolderNav. Full test suite: 376/376 pass. `npm run build` clean. CI: Lint job ✅ success (the code-quality gate — typecheck + lint + test + build). Docker image build + workspace-screenshot jobs fail with the same drift pattern seen on the prior commit (58687ed) — unrelated to this fix.
Mark closed this issue 2026-09-25 20:44:29 +00:00
Sign in to join this conversation.
No description provided.