Skip to content

Button: raw locale key 'Button.label' leaks as accessible name when token unregistered (overrides visible text) #330

@johnleider

Description

@johnleider

Observed

In an app with no Button.label locale token registered (the framework builder), screen readers announce "Button.label" — the raw locale key — as the accessible name on Button.Root usages across the app, per the Chromium accessibility tree. Affected examples include icon-only buttons (remove-token X) and, notably, buttons with visible text ("Start configuring", "Save & Next", "Open in Playground"), where the bogus aria-label overrides the visible text name.

Mechanism

  1. ButtonRoot.vue:217:
    'aria-label': ariaLabel || (isSolo.value ? locale.t('Button.label') : undefined),
  2. useLocale/adapters/v0.ts:46t() returns the key verbatim when unregistered, so the fallback is the literal string Button.label.
  3. No locale bundle in the repo defines Button.label, so every consumer that doesn't register it ships the raw key.

This is the same class of gap as #196 (components should coalesce to an English default when t() returns the key verbatim — PHILOSOPHY §5.5) but on the aria-label path. Additionally, isSolo appears to be true for renderless usages that clearly have text content, which makes the override land on text buttons too — that detection deserves its own look while in there.

Proposed fix

Two layers, consistent with §5.5:

  1. Never emit a raw key: coalesce locale.t('Button.label') to an English default (or emit no aria-label at all) when the lookup returns the key verbatim.
  2. Don't set a fallback aria-label when the button has visible text content — per WCAG, aria-label should not override a usable visible name. Restrict the fallback to genuinely icon-only buttons, and verify why isSolo is true for renderless text-bearing usages.

Regression tests next to the existing aria coverage at packages/0/src/components/Button/index.test.ts:602-627:

  • solo button without explicit ariaLabel must not expose aria-label="Button.label"
  • button with visible text must not get any default aria-label

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions