Skip to content

a11y(4.1.2): add label prop to Button primitive and enforce accessible names#3556

Open
rosanusi wants to merge 3 commits into
mainfrom
wcag/4.1.2-button-anchor-empty
Open

a11y(4.1.2): add label prop to Button primitive and enforce accessible names#3556
rosanusi wants to merge 3 commits into
mainfrom
wcag/4.1.2-button-anchor-empty

Conversation

@rosanusi

Copy link
Copy Markdown
Contributor

Summary

  • Adds a label?: string prop to BaseProps in the Button primitive (src/lib/holocene/button.svelte) rendered as aria-label on both the <a> anchor branch and <button> branch
  • Adds a dev-time onMount check that throws (dev) or console.errors (prod) when a button is rendered without any accessible name (no label, aria-label, aria-labelledby, title, slot text, leading/trailing icon, or count badge)
  • Updates 7 consumer call-sites that were triggering the defect: Workflows List (per-row Start button + 3 toolbar buttons), Activities List (per-row Start button), Schedules List (toolbar configure-columns button), and Schedule Detail page (refresh button)
  • Adds configure-columns i18n key to src/lib/i18n/locales/en/common.ts

Affected files

  • src/lib/holocene/button.svelte
  • src/lib/i18n/locales/en/common.ts
  • src/lib/components/workflow/start-workflow-button.svelte
  • src/lib/components/standalone-activities/start-activity-button.svelte
  • src/lib/components/workflow/workflows-summary-configurable-table.svelte
  • src/lib/pages/schedule-view.svelte
  • src/lib/pages/schedules.svelte

Test plan

  • Visual regression: all 7 consumer buttons render and function identically; no visible change
  • Screen reader (NVDA/VoiceOver): each button announces its label before activation
  • Dev-mode enforcement: render a <Button> with no name source — confirm it throws; add label="…" — confirm no throw
  • Anchor branch: render <Button href="…"> with no name source — confirm same enforcement fires
  • Count badge: render <Button count={3}> — confirm enforcement does not fire (count provides a numeric name)
  • pnpm check passes (no new type errors beyond pre-existing $$_$$ slot interop errors on main)

A11y-Audit-Ref: 4.1.2-button-anchor-empty

🤖 Generated with Claude Code

…cessible names

Adds a `label` prop to the Button/anchor primitive that renders as
`aria-label`, plus a dev-time `onMount` check that warns (prod) or throws
(dev) when a button is rendered without any accessible name. Updates
seven consumer call-sites and adds the `configure-columns` i18n key.

A11y-Audit-Ref: 4.1.2-button-anchor-empty

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
holocene Ready Ready Preview, Comment Jul 10, 2026 8:23pm

Request Review

@rosanusi rosanusi marked this pull request as ready for review June 12, 2026 14:26
@rosanusi rosanusi requested a review from a team as a code owner June 12, 2026 14:26
@ardiewen ardiewen changed the title [a11y] WCAG 4.1.2 — add label prop to Button primitive and enforce accessible names a11y(4.1.2): add label prop to Button primitive and enforce accessible names Jul 6, 2026
@github-actions github-actions Bot added a11y Accessibility audit PR a11y:bucket-3 Bucket 3: engineer required a11y:sc-4.1.2 labels Jul 6, 2026
…orcement

Reworks the approach based on review:

- button.svelte: remove the `label` prop and the onMount accessible-name
  throw. `aria-label` is already a typed attribute (Button's props intersect
  HTMLButtonAttributes/HTMLAnchorAttributes), so call sites set it directly —
  no redundant, ambiguously-named prop. Dropping the throw avoids crashing dev
  on any unlabeled Button (the check also wrongly treated a decorative
  leadingIcon as a name, so it both missed real violations and threw on
  equivalent ones).
- Convert the 7 call sites from label={…} to aria-label={…}.
- Add aria-label to two icon-only buttons the original PR missed (same defect):
  activities-summary configure-columns and workflow-current-details refresh.

Automated enforcement will move to the existing axe accessibility suite once it
is repaired and gated in CI (tracked as a follow-up).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ardiewen

ardiewen commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Pushed a revision reworking the approach based on review:

  • Dropped the label prop. aria-label is already a typed attribute on Button (its props intersect HTMLButtonAttributes/HTMLAnchorAttributes), so label was redundant — and it reads like it should render visible text. Call sites now pass aria-label directly.
  • Dropped the onMount enforcement. It throws in dev for any unlabeled Button (including Storybook/tests), and its hasName check treated a decorative leadingIcon as a name (icon/svg.svelte renders <svg aria-hidden={!title}>, and no title is passed) — so it simultaneously missed real violations (~22 leadingIcon-only buttons) and would have thrown on the equivalent icon-in-slot pattern.
  • Fixed 2 icon-only buttons this PR missed (same defect, would have tripped the removed check): activities-summary-configurable-table configure-columns and workflow-current-details refresh.

On enforcement: the repo already has an axe suite (tests/accessibility/) that computes the real accessible name and would catch this correctly — but it isn't gated in CI and has rotted (the page fails to load before axe runs). Repairing + CI-gating it is the right systematic guard, tracked as a follow-up in DT-4255 rather than a bespoke runtime check.

…hor-empty-review

# Conflicts:
#	src/lib/i18n/locales/en/common.ts
#	src/lib/pages/schedule-view.svelte
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y:bucket-3 Bucket 3: engineer required a11y:sc-4.1.2 a11y Accessibility audit PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants