Skip to content

fix(a11y): make artwork lightbox triggers and interactive rows keyboard-accessible#69

Draft
Copilot wants to merge 4 commits into
mainfrom
copilot/fix-accessibility-lightbox-triggers
Draft

fix(a11y): make artwork lightbox triggers and interactive rows keyboard-accessible#69
Copilot wants to merge 4 commits into
mainfrom
copilot/fix-accessibility-lightbox-triggers

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 19, 2026

Summary

  • Three artwork lightbox triggers (NowPlayingPanel, AlbumDetailView, ArtistDetailView) were bare <div onClick> / <img onClick> β€” not focusable, not announced by screen readers, and broken for useModalA11y focus restoration (captured activeElement was <body>, not the trigger).
  • Broader audit of cursor-pointer patterns found the same gap on lyric-seek rows, queue rows, WrappedView artist rows, and all six track-table views.

Artwork triggers (NowPlayingPanel, AlbumDetailView, ArtistDetailView):

  • Replaced with <button type="button"> + aria-label={t("common.viewArtwork")} + focus-visible:ring-2 focus-visible:ring-emerald-500
  • Button is omitted (not disabled) when no artwork path β€” no dead Tab stops
  • ArtistDetailView: pointer-events-none overlay + pointer-events-auto edit-pencil preserved; pencil is a sibling of the new button, not nested inside it, so clicks don't cross-fire
  • useModalA11y round-trip now works: document.activeElement is the <button> on open β†’ focus returns to it on close

Lyrics seek rows (LyricsPanel, FullscreenLyrics):

  • Added role="button", tabIndex={0}, onKeyDown (Enter/Space β†’ seek) to synced <li> elements

WrappedView top-artists:

  • <li onClick> β†’ <li><button>, matching the already-accessible top-albums pattern in the same component

QueuePanel:

  • QueueRow + SortableQueueItem: tabIndex, role="button", onKeyDown (Enter/Space β†’ jump to track)

Track rows (LibraryView, PlaylistView, LikedView, AlbumDetailView, ArtistDetailView, GenreDetailView):

  • tabIndex={0} + onKeyDown (Enter/Space β†’ play); focus ring uses focus-visible:ring-inset focus-visible:ring-emerald-500

i18n: common.viewArtwork added to all 17 locale files.

How I tested

  • bun run lint and bun run typecheck both pass cleanly
  • Manually verified the three artwork buttons are reachable via Tab and activate on Enter/Space
  • Confirmed the edit-pencil in ArtistDetailView still opens the image picker independently of the lightbox button

Screenshots / clips

Checklist

  • Title uses Conventional Commits (type(scope): subject, kebab-case scope)
  • bun run lint + bun run typecheck pass locally
  • cargo check --manifest-path src-tauri/Cargo.toml --all-targets passes locally
  • If UI strings changed: every locale in src/i18n/locales/ updated
  • If cross-cutting pattern changed: CLAUDE.md and docs/ updated
  • Breaking change? Called out in the summary above

Copilot AI changed the title [WIP] Fix accessibility for artwork lightbox triggers fix(a11y): make artwork lightbox triggers and interactive rows keyboard-accessible May 19, 2026
Copilot AI requested a review from InstaZDLL May 19, 2026 12:08
@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 19, 2026

Not up to standards β›”

πŸ”΄ Issues 5 high Β· 11 medium

Alerts:
⚠ 16 issues (≀ 0 issues of at least minor severity)

Results:
16 new issues

Category Results
BestPractice 11 medium
5 high

View in Codacy

🟒 Metrics 6 complexity · -2 duplication

Metric Results
Complexity 6
Duplication -2

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@InstaZDLL InstaZDLL force-pushed the copilot/fix-accessibility-lightbox-triggers branch from c341b8e to fdee4d5 Compare May 19, 2026 12:56
@github-actions github-actions Bot added scope: frontend React/Vite frontend (src/) scope: i18n Translations (src/i18n/) type: fix Bug fix size: l 200-500 lines labels May 19, 2026
InstaZDLL added 2 commits May 19, 2026 15:28
Follow-up to Copilot's a11y pass on this branch. Codacy raised two
classes of findings:

1. Ten MEDIUM "JSX attribute is specific to React" β€” PMD running on
   `.tsx`/`.jsx` and flagging `className` as if it were JSP. PMD
   targets Java / Apex / PLSQL / JSP β€” none of which exist in this
   repo. Drop PMD (and the Java runtime it pulled in) from
   `.codacy/codacy.yaml`; eslint already covers JS/TS, Opengrep +
   Trivy cover security, Lizard covers complexity.

2. Nine HIGH `jsx-a11y/*` findings β€” `<li>` / `<div>` non-interactive
   elements carrying `tabIndex` + `role="button"`. Split the fix two
   ways:

   - Lyrics rows (`LyricsPanel`, `FullscreenLyrics`) and the now-
     playing `QueueRow` (interactive variant was dead code anyway):
     refactor into real `<button>` elements. `onKeyDown` becomes
     unnecessary β€” `<button>` natively handles Enter and Space. Net
     code reduction.
   - `SortableQueueRow` and the six `TrackTable` rows: contain inner
     `<button>` elements (drag handle, like, more-options), so the
     outer can't be a `<button>` without producing nested-button
     invalid HTML. Keep the `<div tabIndex role="button">` pattern
     with bare `// eslint-disable-next-line` + comment explaining
     why the compound is intentional; keyboard activation still
     works end-to-end.

The bare disable directives target jsx-a11y rules that Codacy runs
server-side but that we don't load locally (no
`eslint-plugin-jsx-a11y` dep). Set linterOptions.reportUnusedDisableDirectives
to "off" in `eslint.config.js` so eslint v9 doesn't flag them as
unused locally.
Codacy runs its a11y checks via a cloud-side analyzer that doesn't read
the repo's eslint config and doesn't honor inline `eslint-disable`
directives. The seven bare `// eslint-disable-next-line` lines added in
the previous commit were therefore load-bearing for nothing and only
produced "Unused eslint-disable directive" warnings locally. Drop them
along with the matching `reportUnusedDisableDirectives: "off"` opt-out
in `eslint.config.js`. The explanatory comments above each compound
row stay β€” they still document why the `<div tabIndex role="button">`
pattern is intentional for rows that contain inner buttons.
@github-actions github-actions Bot added size: xl > 500 lines and removed size: l 200-500 lines labels May 19, 2026
The Codacy CLI install script leaked into the previous commit despite
being declared in `.codacy/.gitignore` β€” that file is itself untracked,
so its rules only apply on machines that already have it. Move the
exclusion rules to the tracked root `.gitignore` so a fresh checkout
doesn't accumulate the same machine-local artefacts.
@github-actions github-actions Bot added size: l 200-500 lines and removed size: xl > 500 lines labels May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: frontend React/Vite frontend (src/) scope: i18n Translations (src/i18n/) size: l 200-500 lines type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

a11y: clickable artwork (lightbox + interactive rows) is not keyboard-accessible

2 participants