- Consolidated multi-line arguments into single lines where appropriate in `useAuth`. - Improved spacing and readability in data processing across components (`ProfileSettingsForm`, `PasswordChangeForm`, `SessionCard`). - Applied consistent table and markdown formatting in design system docs (e.g., `README.md`, `08-ai-guidelines.md`, `00-quick-start.md`). - Updated code snippets to ensure adherence to Prettier rules and streamlined JSX structures.
746 lines
17 KiB
Markdown
746 lines
17 KiB
Markdown
# Accessibility Guide
|
||
|
||
**Build inclusive, accessible interfaces** that work for everyone. Learn WCAG AA standards, keyboard navigation, screen reader support, and testing strategies.
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
1. [Accessibility Standards](#accessibility-standards)
|
||
2. [Color Contrast](#color-contrast)
|
||
3. [Keyboard Navigation](#keyboard-navigation)
|
||
4. [Screen Reader Support](#screen-reader-support)
|
||
5. [ARIA Attributes](#aria-attributes)
|
||
6. [Focus Management](#focus-management)
|
||
7. [Testing](#testing)
|
||
8. [Accessibility Checklist](#accessibility-checklist)
|
||
|
||
---
|
||
|
||
## Accessibility Standards
|
||
|
||
### WCAG 2.1 Level AA
|
||
|
||
We follow **WCAG 2.1 Level AA** as the **minimum** standard.
|
||
|
||
**Why Level AA?**
|
||
|
||
- ✅ Required for most legal compliance (ADA, Section 508)
|
||
- ✅ Covers 95%+ of accessibility needs
|
||
- ✅ Achievable without major UX compromises
|
||
- ✅ Industry standard for modern web apps
|
||
|
||
**WCAG Principles (POUR):**
|
||
|
||
1. **Perceivable** - Information can be perceived by users
|
||
2. **Operable** - Interface can be operated by users
|
||
3. **Understandable** - Information and operation are understandable
|
||
4. **Robust** - Content works with current and future technologies
|
||
|
||
---
|
||
|
||
### Accessibility Decision Tree
|
||
|
||
```
|
||
Creating a UI element?
|
||
│
|
||
├─ Is it interactive?
|
||
│ ├─YES─> Can it be focused with Tab?
|
||
│ │ ├─YES─> ✅ Good
|
||
│ │ └─NO──> ❌ Add tabIndex or use button/link
|
||
│ │
|
||
│ └─NO──> Is it important information?
|
||
│ ├─YES─> Does it have appropriate semantic markup?
|
||
│ │ ├─YES─> ✅ Good
|
||
│ │ └─NO──> ❌ Use h1-h6, p, ul, etc.
|
||
│ │
|
||
│ └─NO──> Is it purely decorative?
|
||
│ ├─YES─> Add aria-hidden="true"
|
||
│ └─NO──> Add alt text or ARIA label
|
||
```
|
||
|
||
---
|
||
|
||
## Color Contrast
|
||
|
||
### Minimum Contrast Ratios (WCAG AA)
|
||
|
||
| Content Type | Minimum Ratio | Example |
|
||
| -------------------------------------- | ------------- | ---------------------------- |
|
||
| **Normal text** (< 18px) | **4.5:1** | Body paragraphs, form labels |
|
||
| **Large text** (≥ 18px or ≥ 14px bold) | **3:1** | Headings, subheadings |
|
||
| **UI components** | **3:1** | Buttons, form borders, icons |
|
||
| **Graphical objects** | **3:1** | Chart elements, infographics |
|
||
|
||
**WCAG AAA (ideal, not required):**
|
||
|
||
- Normal text: 7:1
|
||
- Large text: 4.5:1
|
||
|
||
---
|
||
|
||
### Testing Color Contrast
|
||
|
||
**Tools:**
|
||
|
||
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
|
||
- Chrome DevTools: Inspect element → Accessibility panel
|
||
- [Contrast Ratio Tool](https://contrast-ratio.com)
|
||
- Browser extensions: axe DevTools, WAVE
|
||
|
||
**Example:**
|
||
|
||
```tsx
|
||
// ✅ GOOD - 4.7:1 contrast (WCAG AA pass)
|
||
<p className="text-foreground"> // oklch(0.1529 0 0) on white
|
||
Body text
|
||
</p>
|
||
|
||
// ❌ BAD - 2.1:1 contrast (WCAG AA fail)
|
||
<p className="text-gray-400"> // Too light
|
||
Body text
|
||
</p>
|
||
|
||
// ✅ GOOD - Using semantic tokens ensures contrast
|
||
<p className="text-muted-foreground">
|
||
Secondary text
|
||
</p>
|
||
```
|
||
|
||
**Our design system tokens are WCAG AA compliant:**
|
||
|
||
- `text-foreground` on `bg-background`: 12.6:1 ✅
|
||
- `text-primary-foreground` on `bg-primary`: 8.2:1 ✅
|
||
- `text-destructive` on `bg-background`: 5.1:1 ✅
|
||
- `text-muted-foreground` on `bg-background`: 4.6:1 ✅
|
||
|
||
---
|
||
|
||
### Color Blindness
|
||
|
||
**8% of men and 0.5% of women** have some form of color blindness.
|
||
|
||
**Best practices:**
|
||
|
||
- ❌ Don't rely on color alone to convey information
|
||
- ✅ Use icons, text labels, or patterns in addition to color
|
||
- ✅ Test with color blindness simulators
|
||
|
||
**Example:**
|
||
|
||
```tsx
|
||
// ❌ BAD - Color only
|
||
<div className="text-green-600">Success</div>
|
||
<div className="text-red-600">Error</div>
|
||
|
||
// ✅ GOOD - Color + icon + text
|
||
<Alert variant="success">
|
||
<CheckCircle className="h-4 w-4" />
|
||
<AlertTitle>Success</AlertTitle>
|
||
<AlertDescription>Operation completed</AlertDescription>
|
||
</Alert>
|
||
|
||
<Alert variant="destructive">
|
||
<AlertCircle className="h-4 w-4" />
|
||
<AlertTitle>Error</AlertTitle>
|
||
<AlertDescription>Something went wrong</AlertDescription>
|
||
</Alert>
|
||
```
|
||
|
||
---
|
||
|
||
## Keyboard Navigation
|
||
|
||
### Core Requirements
|
||
|
||
All interactive elements must be:
|
||
|
||
1. ✅ **Focusable** - Can be reached with Tab key
|
||
2. ✅ **Activatable** - Can be triggered with Enter or Space
|
||
3. ✅ **Navigable** - Can move between with arrow keys (where appropriate)
|
||
4. ✅ **Escapable** - Can be closed/exited with Escape key
|
||
|
||
---
|
||
|
||
### Tab Order
|
||
|
||
**Natural tab order** follows DOM order (top to bottom, left to right).
|
||
|
||
```tsx
|
||
// ✅ GOOD - Natural tab order
|
||
<form>
|
||
<Input /> {/* Tab 1 */}
|
||
<Input /> {/* Tab 2 */}
|
||
<Button>Submit</Button> {/* Tab 3 */}
|
||
</form>
|
||
|
||
// ❌ BAD - Using tabIndex to force order
|
||
<form>
|
||
<Input tabIndex={2} /> // Don't do this
|
||
<Input tabIndex={1} />
|
||
<Button tabIndex={3}>Submit</Button>
|
||
</form>
|
||
```
|
||
|
||
**When to use `tabIndex`:**
|
||
|
||
- `tabIndex={0}` - Make non-interactive element focusable
|
||
- `tabIndex={-1}` - Remove from tab order (for programmatic focus)
|
||
- `tabIndex={1+}` - ❌ **Avoid** - Breaks natural order
|
||
|
||
---
|
||
|
||
### Keyboard Shortcuts
|
||
|
||
| Key | Action | Example |
|
||
| --------------- | ------------------------- | ----------------------------- |
|
||
| **Tab** | Move focus forward | Navigate through form fields |
|
||
| **Shift + Tab** | Move focus backward | Go back to previous field |
|
||
| **Enter** | Activate button/link | Submit form, follow link |
|
||
| **Space** | Activate button/checkbox | Toggle checkbox, click button |
|
||
| **Escape** | Close overlay | Close dialog, dropdown |
|
||
| **Arrow keys** | Navigate within component | Navigate dropdown items |
|
||
| **Home** | Jump to start | First item in list |
|
||
| **End** | Jump to end | Last item in list |
|
||
|
||
---
|
||
|
||
### Implementing Keyboard Navigation
|
||
|
||
**Button (automatic):**
|
||
|
||
```tsx
|
||
// ✅ Button is keyboard accessible by default
|
||
<Button onClick={handleClick}>Click me</Button>
|
||
// Enter or Space triggers onClick
|
||
```
|
||
|
||
**Custom clickable div (needs work):**
|
||
|
||
```tsx
|
||
// ❌ BAD - Not keyboard accessible
|
||
<div onClick={handleClick}>
|
||
Click me
|
||
</div>
|
||
|
||
// ✅ GOOD - Make it accessible
|
||
<div
|
||
role="button"
|
||
tabIndex={0}
|
||
onClick={handleClick}
|
||
onKeyDown={(e) => {
|
||
if (e.key === 'Enter' || e.key === ' ') {
|
||
e.preventDefault();
|
||
handleClick();
|
||
}
|
||
}}
|
||
>
|
||
Click me
|
||
</div>
|
||
|
||
// ✅ BETTER - Just use a button
|
||
<button onClick={handleClick}>
|
||
Click me
|
||
</button>
|
||
```
|
||
|
||
**Dropdown navigation:**
|
||
|
||
```tsx
|
||
<DropdownMenu>
|
||
<DropdownMenuContent>
|
||
<DropdownMenuItem>Edit</DropdownMenuItem> {/* Arrow down */}
|
||
<DropdownMenuItem>Delete</DropdownMenuItem> {/* Arrow down */}
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
// shadcn/ui handles arrow key navigation automatically
|
||
```
|
||
|
||
---
|
||
|
||
### Skip Links
|
||
|
||
**Allow keyboard users to skip navigation:**
|
||
|
||
```tsx
|
||
// Add to layout
|
||
<a
|
||
href="#main-content"
|
||
className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-primary focus:text-primary-foreground focus:rounded-lg"
|
||
>
|
||
Skip to main content
|
||
</a>
|
||
|
||
<nav>{/* Navigation */}</nav>
|
||
|
||
<main id="main-content">
|
||
{/* Main content */}
|
||
</main>
|
||
```
|
||
|
||
---
|
||
|
||
## Screen Reader Support
|
||
|
||
### Screen Reader Basics
|
||
|
||
**Popular screen readers:**
|
||
|
||
- **NVDA** (Windows) - Free, most popular for testing
|
||
- **JAWS** (Windows) - Industry standard, paid
|
||
- **VoiceOver** (macOS/iOS) - Built-in to Apple devices
|
||
- **TalkBack** (Android) - Built-in to Android
|
||
|
||
**What screen readers announce:**
|
||
|
||
- Semantic element type (button, link, heading, etc.)
|
||
- Element text content
|
||
- Element state (expanded, selected, disabled)
|
||
- ARIA labels and descriptions
|
||
|
||
---
|
||
|
||
### Semantic HTML
|
||
|
||
**Use the right HTML element for the job:**
|
||
|
||
```tsx
|
||
// ✅ GOOD - Semantic HTML
|
||
<header>
|
||
<nav>
|
||
<ul>
|
||
<li><a href="/">Home</a></li>
|
||
<li><a href="/about">About</a></li>
|
||
</ul>
|
||
</nav>
|
||
</header>
|
||
|
||
<main>
|
||
<article>
|
||
<h1>Page Title</h1>
|
||
<p>Content...</p>
|
||
</article>
|
||
</main>
|
||
|
||
<footer>
|
||
<p>© 2025 Company</p>
|
||
</footer>
|
||
|
||
// ❌ BAD - Div soup
|
||
<div>
|
||
<div>
|
||
<div>
|
||
<div onClick={goHome}>Home</div>
|
||
<div onClick={goAbout}>About</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<div>
|
||
<div>Page Title</div>
|
||
<div>Content...</div>
|
||
</div>
|
||
</div>
|
||
```
|
||
|
||
**Semantic elements:**
|
||
|
||
- `<header>` - Page header
|
||
- `<nav>` - Navigation
|
||
- `<main>` - Main content (only one per page)
|
||
- `<article>` - Self-contained content
|
||
- `<section>` - Thematic grouping
|
||
- `<aside>` - Sidebar content
|
||
- `<footer>` - Page footer
|
||
- `<h1>` - `<h6>` - Headings (hierarchical)
|
||
- `<button>` - Buttons
|
||
- `<a>` - Links
|
||
|
||
---
|
||
|
||
### Alt Text for Images
|
||
|
||
```tsx
|
||
// ✅ GOOD - Descriptive alt text
|
||
<img src="/chart.png" alt="Bar chart showing 20% increase in sales from January to February" />
|
||
|
||
// ✅ GOOD - Decorative images
|
||
<img src="/decorative.png" alt="" /> // Empty alt for decorative
|
||
// OR
|
||
<img src="/decorative.png" aria-hidden="true" />
|
||
|
||
// ❌ BAD - Generic or missing alt
|
||
<img src="/chart.png" alt="image" />
|
||
<img src="/chart.png" /> // No alt
|
||
```
|
||
|
||
**Icon-only buttons:**
|
||
|
||
```tsx
|
||
// ✅ GOOD - ARIA label
|
||
<Button size="icon" aria-label="Close dialog">
|
||
<X className="h-4 w-4" />
|
||
</Button>
|
||
|
||
// ❌ BAD - No label
|
||
<Button size="icon">
|
||
<X className="h-4 w-4" />
|
||
</Button>
|
||
```
|
||
|
||
---
|
||
|
||
## ARIA Attributes
|
||
|
||
### Common ARIA Attributes
|
||
|
||
**ARIA roles:**
|
||
|
||
```tsx
|
||
<div role="button" tabIndex={0}>Custom Button</div>
|
||
<div role="alert">Error message</div>
|
||
<div role="status">Loading...</div>
|
||
<div role="navigation">...</div>
|
||
```
|
||
|
||
**ARIA states:**
|
||
|
||
```tsx
|
||
<button aria-expanded={isOpen}>Toggle Menu</button>
|
||
<button aria-pressed={isActive}>Toggle</button>
|
||
<input aria-invalid={!!errors.email} />
|
||
<div aria-disabled="true">Disabled Item</div>
|
||
```
|
||
|
||
**ARIA properties:**
|
||
|
||
```tsx
|
||
<button aria-label="Close">×</button>
|
||
<input aria-describedby="email-help" />
|
||
<input aria-required="true" />
|
||
<div aria-live="polite">Status updates</div>
|
||
<div aria-hidden="true">Decorative content</div>
|
||
```
|
||
|
||
---
|
||
|
||
### Form Accessibility
|
||
|
||
**Label association:**
|
||
|
||
```tsx
|
||
// ✅ GOOD - Explicit association
|
||
<Label htmlFor="email">Email</Label>
|
||
<Input id="email" type="email" />
|
||
|
||
// ❌ BAD - No association
|
||
<div>Email</div>
|
||
<Input type="email" />
|
||
```
|
||
|
||
**Error messages:**
|
||
|
||
```tsx
|
||
// ✅ GOOD - Linked with aria-describedby
|
||
<Label htmlFor="password">Password</Label>
|
||
<Input
|
||
id="password"
|
||
type="password"
|
||
aria-invalid={!!errors.password}
|
||
aria-describedby={errors.password ? 'password-error' : undefined}
|
||
/>
|
||
{errors.password && (
|
||
<p id="password-error" className="text-sm text-destructive">
|
||
{errors.password.message}
|
||
</p>
|
||
)}
|
||
|
||
// ❌ BAD - No association
|
||
<Input type="password" />
|
||
{errors.password && <p>{errors.password.message}</p>}
|
||
```
|
||
|
||
**Required fields:**
|
||
|
||
```tsx
|
||
// ✅ GOOD - Marked as required
|
||
<Label htmlFor="name">
|
||
Name <span className="text-destructive">*</span>
|
||
</Label>
|
||
<Input id="name" required aria-required="true" />
|
||
|
||
// Screen reader announces: "Name, required, edit text"
|
||
```
|
||
|
||
---
|
||
|
||
### Live Regions
|
||
|
||
**Announce dynamic updates:**
|
||
|
||
```tsx
|
||
// Polite (waits for user to finish)
|
||
<div aria-live="polite" aria-atomic="true">
|
||
{statusMessage}
|
||
</div>
|
||
|
||
// Assertive (interrupts immediately)
|
||
<div aria-live="assertive" role="alert">
|
||
{errorMessage}
|
||
</div>
|
||
|
||
// Example: Toast notifications (sonner uses this)
|
||
toast.success('User created');
|
||
// Announces: "Success. User created."
|
||
```
|
||
|
||
---
|
||
|
||
## Focus Management
|
||
|
||
### Visible Focus Indicators
|
||
|
||
**All interactive elements must have visible focus:**
|
||
|
||
```tsx
|
||
// ✅ GOOD - shadcn/ui components have focus rings
|
||
<Button>Click me</Button>
|
||
// Shows ring on focus
|
||
|
||
// ✅ GOOD - Custom focus styles
|
||
<div
|
||
tabIndex={0}
|
||
className="focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
||
>
|
||
Focusable content
|
||
</div>
|
||
|
||
// ❌ BAD - Removing focus outline
|
||
<button style={{ outline: 'none' }}>Bad</button>
|
||
```
|
||
|
||
**Use `:focus-visible` instead of `:focus`:**
|
||
|
||
- `:focus` - Shows on mouse click AND keyboard
|
||
- `:focus-visible` - Shows only on keyboard (better UX)
|
||
|
||
---
|
||
|
||
### Focus Trapping
|
||
|
||
**Dialogs should trap focus:**
|
||
|
||
```tsx
|
||
// shadcn/ui Dialog automatically traps focus
|
||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||
<DialogContent>
|
||
{/* Focus trapped inside */}
|
||
<Input autoFocus /> {/* Focus first field */}
|
||
<Button>Submit</Button>
|
||
</DialogContent>
|
||
</Dialog>
|
||
|
||
// When dialog closes, focus returns to trigger button
|
||
```
|
||
|
||
---
|
||
|
||
### Programmatic Focus
|
||
|
||
**Set focus after actions:**
|
||
|
||
```tsx
|
||
const inputRef = useRef<HTMLInputElement>(null);
|
||
|
||
const handleDelete = () => {
|
||
deleteUser();
|
||
// Return focus to a relevant element
|
||
inputRef.current?.focus();
|
||
};
|
||
|
||
<Input ref={inputRef} />;
|
||
```
|
||
|
||
---
|
||
|
||
## Testing
|
||
|
||
### Automated Testing Tools
|
||
|
||
**Browser extensions:**
|
||
|
||
- [axe DevTools](https://www.deque.com/axe/devtools/) - Free, comprehensive
|
||
- [WAVE](https://wave.webaim.org/extension/) - Visual feedback
|
||
- [Lighthouse](https://developer.chrome.com/docs/lighthouse/) - Built into Chrome
|
||
|
||
**CI/CD testing:**
|
||
|
||
- [@axe-core/react](https://github.com/dequelabs/axe-core-npm/tree/develop/packages/react) - Runtime accessibility testing
|
||
- [jest-axe](https://github.com/nickcolley/jest-axe) - Jest integration
|
||
- [Playwright accessibility testing](https://playwright.dev/docs/accessibility-testing)
|
||
|
||
---
|
||
|
||
### Manual Testing Checklist
|
||
|
||
#### Keyboard Testing
|
||
|
||
1. [ ] Unplug mouse
|
||
2. [ ] Tab through entire page
|
||
3. [ ] All interactive elements focusable?
|
||
4. [ ] Focus indicators visible?
|
||
5. [ ] Can activate with Enter/Space?
|
||
6. [ ] Can close modals with Escape?
|
||
7. [ ] Tab order logical?
|
||
|
||
#### Screen Reader Testing
|
||
|
||
1. [ ] Install NVDA (Windows) or VoiceOver (Mac)
|
||
2. [ ] Navigate page with screen reader on
|
||
3. [ ] All content announced?
|
||
4. [ ] Interactive elements have labels?
|
||
5. [ ] Form errors announced?
|
||
6. [ ] Heading hierarchy correct?
|
||
|
||
#### Contrast Testing
|
||
|
||
1. [ ] Use contrast checker on all text
|
||
2. [ ] Check UI components (buttons, borders)
|
||
3. [ ] Test in dark mode too
|
||
4. [ ] All elements meet 4.5:1 (text) or 3:1 (UI)?
|
||
|
||
---
|
||
|
||
### Testing with Real Users
|
||
|
||
**Considerations:**
|
||
|
||
- Test with actual users who rely on assistive technologies
|
||
- Different screen readers behave differently
|
||
- Mobile screen readers (VoiceOver, TalkBack) differ from desktop
|
||
- Keyboard-only users have different needs than screen reader users
|
||
|
||
---
|
||
|
||
## Accessibility Checklist
|
||
|
||
### General
|
||
|
||
- [ ] Page has `<title>` and `<meta name="description">`
|
||
- [ ] Page has proper heading hierarchy (h1 → h2 → h3)
|
||
- [ ] Landmarks used (`<header>`, `<nav>`, `<main>`, `<footer>`)
|
||
- [ ] Skip link present for keyboard users
|
||
- [ ] No content relies on color alone
|
||
|
||
### Color & Contrast
|
||
|
||
- [ ] Text has 4.5:1 contrast (normal) or 3:1 (large)
|
||
- [ ] UI components have 3:1 contrast
|
||
- [ ] Tested in both light and dark modes
|
||
- [ ] Color blindness simulator used
|
||
|
||
### Keyboard
|
||
|
||
- [ ] All interactive elements focusable
|
||
- [ ] Focus indicators visible (ring, outline, etc.)
|
||
- [ ] Tab order is logical
|
||
- [ ] No keyboard traps
|
||
- [ ] Enter/Space activates buttons
|
||
- [ ] Escape closes dialogs/dropdowns
|
||
- [ ] Arrow keys navigate lists/menus
|
||
|
||
### Screen Readers
|
||
|
||
- [ ] All images have alt text
|
||
- [ ] Icon-only buttons have aria-label
|
||
- [ ] Form labels associated with inputs
|
||
- [ ] Form errors use aria-describedby
|
||
- [ ] Required fields marked with aria-required
|
||
- [ ] Live regions for dynamic updates
|
||
- [ ] ARIA roles used correctly
|
||
|
||
### Forms
|
||
|
||
- [ ] Labels associated with inputs (`htmlFor` + `id`)
|
||
- [ ] Error messages linked (`aria-describedby`)
|
||
- [ ] Invalid inputs marked (`aria-invalid`)
|
||
- [ ] Required fields indicated (`aria-required`)
|
||
- [ ] Submit button disabled during submission
|
||
|
||
### Focus Management
|
||
|
||
- [ ] Dialogs trap focus
|
||
- [ ] Focus returns after dialog closes
|
||
- [ ] Programmatic focus after actions
|
||
- [ ] No focus outline removed without alternative
|
||
|
||
---
|
||
|
||
## Quick Wins for Accessibility
|
||
|
||
**Easy improvements with big impact:**
|
||
|
||
1. **Add alt text to images**
|
||
|
||
```tsx
|
||
<img src="/logo.png" alt="Company Logo" />
|
||
```
|
||
|
||
2. **Associate labels with inputs**
|
||
|
||
```tsx
|
||
<Label htmlFor="email">Email</Label>
|
||
<Input id="email" />
|
||
```
|
||
|
||
3. **Use semantic HTML**
|
||
|
||
```tsx
|
||
<button> instead of <div onClick>
|
||
```
|
||
|
||
4. **Add aria-label to icon buttons**
|
||
|
||
```tsx
|
||
<Button aria-label="Close">
|
||
<X />
|
||
</Button>
|
||
```
|
||
|
||
5. **Use semantic color tokens**
|
||
|
||
```tsx
|
||
className = 'text-foreground'; // Auto contrast
|
||
```
|
||
|
||
6. **Test with keyboard only**
|
||
- Tab through page
|
||
- Fix anything unreachable
|
||
|
||
---
|
||
|
||
## Next Steps
|
||
|
||
- **Test Now**: Run [axe DevTools](https://www.deque.com/axe/devtools/) on your app
|
||
- **Learn More**: [W3C ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/)
|
||
- **Components**: [Review accessible components](./02-components.md)
|
||
- **Forms**: [Accessible form patterns](./06-forms.md)
|
||
|
||
---
|
||
|
||
**Related Documentation:**
|
||
|
||
- [Forms](./06-forms.md) - Accessible form patterns
|
||
- [Components](./02-components.md) - All components are accessible
|
||
- [Foundations](./01-foundations.md) - Color contrast tokens
|
||
|
||
**External Resources:**
|
||
|
||
- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/)
|
||
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
|
||
- [A11y Project Checklist](https://www.a11yproject.com/checklist/)
|
||
- [axe DevTools](https://www.deque.com/axe/devtools/)
|
||
|
||
**Last Updated**: November 2, 2025
|