A comprehensive checklist for testing Web3 application accessibility against W3AG guidelines.
Run through these basic checks first:
- Tab through the entire page - can you reach all interactive elements?
- Is there a visible focus indicator on every focusable element?
- Can you connect a wallet using only keyboard?
- Are transaction amounts readable (not in wei)?
- Do addresses have ENS names or truncated formats?
- All addresses have text alternatives (ENS, labels, or truncation)
- Full address is available on hover/focus for verification
- Address checksum status is communicated non-visually
- Screen reader announces addresses in digestible format
- Every transaction has a human-readable type label
- Token amounts displayed in human-readable format (not wei)
- Token amounts include symbol/name
- Tokens show full name, not just ticker symbol
- NFT images have meaningful alt text
- Token contract addresses are accessible
- Profit/loss indicators don't rely on color alone
- Status indicators use icons or text, not just color
- Wallet modal opens with keyboard
- Can tab through wallet options
- Can select wallet with Enter/Space
- Modal closes with Escape
- Focus trapped within modal
- Focus returns to trigger on close
- Sign/Reject buttons are keyboard accessible
- Transaction details are screen reader accessible
- Buttons have visible focus indicators
- Users warned before time-sensitive actions
- Countdowns are accessible (not just visual)
- Skip links available for long pages
- Tab order matches visual order
- No positive tabindex values used
- Plain language descriptions accompany technical data
- Function names have human-readable explanations
- Wallet connect NEVER auto-triggers transactions
- Network switch requires explicit confirmation
- Address validation for correct network
- Invalid address error messages are clear
- Error messages accessible to screen readers
- Unverified contracts clearly flagged
- Warnings require acknowledgment
- Modals have
role="dialog"andaria-modal="true" - Dropdowns have proper
aria-expandedstates - Dynamic content uses
aria-liveregions - Transaction status changes announced
- Gas estimates include fiat currency values
- Gas fee text descriptions provided (not just visual bars)
- Transaction simulations have text summaries
- Token verification status accessible
- Contract address can be copied
- Charts have data tables or text summaries
- All text meets 4.5:1 contrast ratio
- Interactive elements meet 3:1 contrast ratio
- QR codes have copy-link alternative
- Connection URI can be copied to clipboard
- Time limits for signing can be extended
- Multi-step transactions maintain state
- Progress indicators for multi-step flows
- Deadline extensions available where possible
- Slippage warnings have adequate response time
- Quote expiry warned in advance
- Proper heading hierarchy (h1→h2→h3)
- Portfolio sections have clear headings
- Jargon terms have accessible definitions
- Tooltips/popovers for technical terms
- Transaction outcomes state concrete amounts
- Token approvals explain permissions granted
- Approval amounts clearly stated
- Unlimited approvals have warnings
- Large transactions require confirmation
- Simulation failures block submission
- "Max" button warns about gas reserves
- Impermanent loss explained in plain language
- Liquidation risks explained clearly
- Concrete examples provided for risks
- Token lists use proper table markup
- Tables have proper headers with scope
- Sort state announced for sortable columns
- Non-visual CAPTCHA alternative available
- Wallet signature can replace CAPTCHA
- Users can set preferred address display format
- Risk levels communicated via multiple modalities
- Structured accessibility metadata available
- High contrast mode supported
- Dark/light mode with system preference detection
- Voice-controlled wallet connection works
- Hardware wallet flows have screen reader guidance
- Accessibility mode with extended time limits
- Auto-refresh for quotes available
- Custom keyboard shortcuts don't conflict with AT
- Shortcuts can be customized or disabled
- Complexity levels adjustable (simple/advanced)
- User preference persists across sessions
- Position changes show before/after preview
- Pattern-based anomaly detection
- Unusual transaction warnings
- Risk assessments in multiple formats
- Audio risk announcements available
- Price feed announcement frequency configurable
- Manual announce button for prices
- CLI alternative for critical operations
- Voice transaction initiation supported
- Navigate page with arrow keys
- Use H key to jump between headings
- Use Tab to navigate interactive elements
- Verify live regions announce changes
- Test virtual cursor mode
- Use VO+Right/Left to navigate
- Use VO+Command+H for headings
- Verify rotor shows correct elements
- Test form controls with VO+Space
- Swipe to navigate elements
- Double-tap to activate
- Test with screen curtain on
- Verify all buttons are labeled
- Swipe to navigate
- Double-tap to activate
- Test reading controls
- Verify focus order
| Action | Expected Keys |
|---|---|
| Navigate forward | Tab |
| Navigate backward | Shift+Tab |
| Activate button | Enter or Space |
| Close modal | Escape |
| Navigate list | Arrow Up/Down |
| Select option | Enter |
| Open dropdown | Enter, Space, or Arrow Down |
-
Wallet Connection Flow
- Tab to Connect button
- Press Enter to open modal
- Navigate wallets with arrows
- Select with Enter
- Verify focus management
-
Swap Flow
- Tab through all inputs
- Open token selector with keyboard
- Search and select token
- Enter amount
- Review and confirm
-
Send Flow
- Enter address with keyboard
- Tab to amount input
- Use Max button
- Navigate confirmation dialog
- Confirm or reject
- axe DevTools - Run full page audit
- WAVE - Check errors and alerts
- Lighthouse - Accessibility score
# Install dependencies
npm install --save-dev jest-axe @testing-library/react
# Example test
import { axe } from 'jest-axe';
test('wallet modal has no violations', async () => {
const { container } = render(<WalletModal isOpen />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});# .github/workflows/accessibility.yml
name: Accessibility Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run test:a11y// ❌ Bad
<button><Icon /></button>
// ✅ Good
<button aria-label="Close modal"><Icon aria-hidden="true" /></button>// ❌ Bad
<span className={profit > 0 ? 'green' : 'red'}>{profit}%</span>
// ✅ Good
<span className={profit > 0 ? 'green' : 'red'}>
{profit > 0 ? '▲' : '▼'} {profit}%
</span>// ❌ Bad - status changes not announced
<span>{status}</span>
// ✅ Good
<span role="status" aria-live="polite">{status}</span>// Use a focus trap library or implement manually
import { FocusTrap } from 'focus-trap-react';
<FocusTrap>
<Modal />
</FocusTrap>## Accessibility Audit Report
**Application:** [Name]
**Date:** [Date]
**Tester:** [Name]
**Tools Used:** [List]
### Summary
- Level A: X/Y criteria pass
- Level AA: X/Y criteria pass
- Level AAA: X/Y criteria pass
### Critical Issues
1. [Description] - [Location] - [Impact]
### Major Issues
1. [Description] - [Location] - [Impact]
### Minor Issues
1. [Description] - [Location] - [Impact]
### Recommendations
1. [Recommendation]