Skip to content

Expose Kbd's accessible name with role="img" - #441

Merged
czarandy merged 1 commit into
mainfrom
fix/kbd-accessible-name
Aug 7, 2026
Merged

Expose Kbd's accessible name with role="img"#441
czarandy merged 1 commit into
mainfrom
fix/kbd-accessible-name

Conversation

@czarandy

@czarandy czarandy commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Fixes #362

Problem

Kbd does real work to build a spoken shortcut name — getKeyLabel maps mod to "Command"/"Control" and glyphs like // to "Alt"/"Backspace"/"Up Arrow", joined into e.g. Command+Shift+P — and then attaches it as aria-label on a bare <kbd>.

<kbd> has no implicit ARIA role; it maps to generic, and aria-label is prohibited on role="generic". Screen readers are liable to ignore the label and announce the raw glyph content of the nested <kbd> children instead ("⌘⇧P", or nothing intelligible). The feature largely wasn't reaching users.

Fix

Give the root <kbd> role="img", matching the existing pattern in StatusDot, Rating, and Avatar. img is a leaf role, so the nested glyph <kbd> elements become presentational and the shortcut is announced once, as its readable name.

The <kbd> element itself is unchanged, so document semantics and all styling stay as they were. aria-hidden still opts the whole thing out for callers whose control already carries aria-keyshortcuts.

Tests

The existing tests asserted the aria-label DOM attribute, which passed even while the name was unexposed. They now query by role and computed accessible name — getByRole('img', {name: 'Control+Shift+K'}) — plus new assertions that the root carries the role, that exactly one img is exposed (the glyphs aren't announced separately), and that aria-hidden removes it from the tree.

Verified as a regression test: reverting the role="img" line fails 8 of the 22 Kbd tests. Full suite green (3198 tests), lint/typecheck/format clean.

Kbd built a spoken shortcut name ("Command+Shift+P") and attached it as
aria-label on a bare <kbd>. That element has no implicit ARIA role — it
maps to generic, where aria-label is prohibited — so screen readers were
liable to drop the label and announce the raw glyphs instead.

Give the root role="img", matching the existing pattern in StatusDot,
Rating, and Avatar. As a leaf role it also makes the nested glyph <kbd>
children presentational, so the name is announced once.

The old tests asserted the aria-label DOM attribute, which passed even
though the name was unexposed. They now query by role and accessible
name via getByRole('img', {name}), which fails without the role.
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
silver-ui-site Ready Ready Preview Aug 6, 2026 4:19pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
silver-ui Ignored Ignored Preview Aug 6, 2026 4:19pm

@czarandy
czarandy enabled auto-merge (squash) August 7, 2026 01:45
@czarandy
czarandy disabled auto-merge August 7, 2026 01:45
@czarandy
czarandy merged commit 557b7a5 into main Aug 7, 2026
3 of 4 checks passed
@czarandy
czarandy deleted the fix/kbd-accessible-name branch August 7, 2026 01:45
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.

Kbd's computed accessible name is inert: aria-label on a role-less <kbd> is prohibited ARIA

1 participant