coinbase / coinbase/onchainkit

Bug: TransactionButton has no accessible name while a transaction is approving or pending

Open Beginner friendly
#2,675 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
1k
Forks
520
Avg merge
32m
Merged PRs (30d)
2

Description

### Describe the bug and the steps to reproduce it

`TransactionButton` swaps its entire label for a bare spinner while a
transaction is being submitted or is pending, with no accessible name left
on the button. A screen reader user who has already started a transaction
gets no indication that anything is happening, or that the button is even
still a button with a purpose, until it either succeeds or fails.

1. Run this repo's own playground app (`packages/playground`), which already
demonstrates `Transaction` with `TransactionButton` in
`packages/playground/components/demo/Transaction.tsx`, with a screen
reader running or the accessibility tree open in devtools.
2. Connect a wallet and click the demo's `TransactionButton` to submit a
transaction.
3. While the transaction is being signed/submitted and while it is pending
on-chain, inspect the button (`data-testid="ockTransactionButton_Button"`).

### What's the expected behavior?

The button should keep an accessible name throughout, for example
"Transact, pending" or similar, so a screen reader announces both that the
control is still a button and what state it is in. Marking it `aria-busy`
would also be reasonable in addition to a name.

### Actual behavior

`TransactionButton`'s content is computed in `buttonContent`
(`src/transaction/components/TransactionButton.tsx`, lines 63-75): when
`isLoading` is true, it returns `` and nothing else. The `Spinner`
component (`src/internal/components/Spinner.tsx`) renders two `

`
elements with no text, `aria-label`, or `role`. The `` element
itself (lines 108-127) sets no `aria-label` and has no visually-hidden text
fallback, so once `buttonContent` becomes the spinner, the button's
accessible name is empty. axe reports this as `button-name`.

```tsx
// TransactionButton.tsx
const buttonContent = useMemo(() => {
if (receipt) return 'View transaction';
if (errorMessage) return 'Try again';
if (isLoading) return ; // <- no text alternative
return idleText;
}, [isLoading, errorMessage, receipt, idleText]);
...

{buttonContent}

```

### What version of the libraries are you using?

@coinbase/onchainkit 1.1.2

### WCAG success criterion

4.1.2 Name, Role, Value.

### Rule

axe-core `button-name`.

### Suggested fix

Keep visually-hidden text on the button while `isLoading` is true (e.g. a
`sr-only`/`aria-label` reflecting the idle text plus "pending", or a fixed
string like "Transaction pending"), and consider `aria-busy="true"` on the
button for the duration. `Spinner` itself could also take an optional label
prop rather than always rendering as purely decorative markup.

Found while auditing wallet-connect kits for accessibility. A reproduction
script is available on request.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in src/transaction/components/TransactionButton.tsx, especially buttonContent and the button element, then inspect src/internal/components/Spinner.tsx. Reproduce the loading state in packages/playground/components/demo/Transaction.tsx and check the button with a screen reader, the accessibility tree, or axe. Done means the button retains an accessible name and communicates its pending state while signing, submitting, and awaiting confirmation.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
accessibility, frontend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
85/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.