Skip to content

a11y(wallet): give icon-only buttons an accessible name - #12

Merged
Miracle656 merged 2 commits into
Divinity-Labs:mainfrom
dxfareed:a11y/icon-button-labels-4
Sep 6, 2026
Merged

Miracle656 merged 2 commits into
Divinity-Labs:mainfrom
dxfareed:a11y/icon-button-labels-4

Conversation

@dxfareed

@dxfareed dxfareed commented Sep 5, 2026

Copy link
Copy Markdown

Closes #4

What changed: Gave an accessible name to the 15 icon-only buttons in the wallet that had none. 18 added lines, all ARIA attributes, across 10 files. Nothing removed.

Why: These announced as a bare "button" to a screen reader. Seven had a title tooltip, which is unreliable as an accessible name and invisible on touch; eight had neither.

control label
dashboard copy address Copy wallet address
dashboard fee-payer notice dismiss Dismiss fee-payer warning
dashboard stranded-balance dismiss Dismiss stranded balance notice
NFT list refresh Refresh NFTs
NFT detail close Close NFT details
agent clear history Clear history
agent back Back
contacts delete Delete contact
multisig remove signer Remove signer
install banner dismiss Dismiss install prompt
contact picker close Close contact picker
settings/agent back, settings/profile back Back to settings
token/[code] back Back
settings/agent enable toggle Enable AI agent

Three notes on the choices:

  • Labels reuse vocabulary already in the tree. Back to settings appears 4× and Back once, so the back arrows follow that rather than a new phrasing.
  • The two dashboard dismiss buttons were both titled Dismiss. They sit on the same screen, so identical names are ambiguous out of context; their aria-labels distinguish them while the visible tooltips stay as they were.
  • The settings/agent toggle also gets role="switch" and aria-checked, matching the Toggle in components/ui/primitives.tsx, which is the existing house pattern for a switch. Without a role it announced as a button with no state. type="button" came along from that same pattern; it is a no-op here, there is no <form> in the file.

What I deliberately left alone: buttons whose accessible name is already rendered text. I scanned all 228 <button> elements under app/ and components/ and classified them; the ones that look icon-only but are not (nfts dev-mode toggles, the Copy/Copied button, NetworkSwitcher) all render a visible label. The settings gear in TabBar/AppShell is likewise already correct: visible text label with aria-hidden on the glyph.

How tested:

  • npm run typecheck in frontend/wallet clean.
  • npm run build succeeds, all routes compile.
  • npm test: 251 passed. One suite, feeBump.test.ts, fails to load on ReferenceError: TextEncoder is not defined. That is pre-existing on main and unrelated to this change; it is fixed separately in test(wallet): cover truncateAddress edge cases #11.
  • Re-ran my scan after the change: zero icon-only buttons without an accessible name remain, down from 15. No element ended up with a duplicate aria-label.
  • Diff is purely additive with no style, layout or copy touched, which is what satisfies "no visual change" structurally.

Not tested: I did not verify with a screen reader or the a11y devtools pane, so that acceptance box is unticked from my side. There is no eslint-plugin-jsx-a11y in the project and no component-render test setup (@testing-library/* is not installed), so I had no way to assert computed accessible names automatically without adding a dependency, which felt out of scope here. Worth a real screen-reader pass before merge, particularly on the role="switch" toggle.

Notes:

npm run lint in frontend/wallet is broken on main, unrelated to this change: the script is next lint, which Next 16 removed, so it fails with Invalid project directory provided, no such directory: .../lint. There is also no eslint config in that package. Happy to send a follow-up migrating it to eslint directly, and adding eslint-plugin-jsx-a11y would make regressions like this issue fail CI rather than needing a manual audit.

Note also that frontend/wallet's jest suite is not run by CI at all (only the sdk and mobile jobs invoke tests), so nothing here is currently enforced automatically.

Fifteen icon-only buttons announced as bare "button" to a screen reader.
Seven had a title tooltip but no accessible name; eight had neither.

Labelled: dashboard copy-address and its two dismiss buttons, the NFT
refresh and detail close, agent clear-history and back, contacts delete,
multisig remove-signer, the install banner dismiss, the contact picker
close, and the back arrows on settings/agent, settings/profile and
token/[code].

Labels follow the vocabulary already in the tree ("Back to settings",
"Back") rather than a new one. The two dashboard dismiss buttons, both
titled "Dismiss", get distinct labels so they are told apart out of
context. The agent toggle also picks up role="switch" and aria-checked to
match the Toggle in components/ui/primitives.tsx, which is the existing
house pattern for a switch.

Additive only: 18 lines, all ARIA attributes, no style, layout or copy
changed. Buttons whose label is rendered text were left alone.
@dxfareed
dxfareed requested a review from Miracle656 as a code owner September 5, 2026 17:58
Two of the labels added here replaced a visible label rather than
supplying a missing one, which is the case WCAG 2.5.3 (Label in Name)
covers: a speech-input user says what they can see, so the accessible
name has to contain it.

The dashboard copy button is not icon-only — it renders the truncated
address, and the dashboard is the only place that address appears. A
bare "Copy wallet address" said what the button does but took the
address away from screen reader users entirely. The label now carries
both.

The agent toggle sits beside a visible "Veil Agent" heading, so that is
its name; role="switch" and aria-checked already announce the on/off
state, which "Enable AI agent" was duplicating in words and, when the
switch was already on, contradicting.

Claude-Session: https://claude.ai/code/session_01USgemLt4Rnz4SGB1Srf3GB
@Miracle656
Miracle656 merged commit bf41b32 into Divinity-Labs:main Sep 6, 2026
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.

Accessibility: label all icon-only buttons

2 participants