Accessibility

ClawSkills 作者 veeramanikandanr48 v0.1.0

Build WCAG 2.1 AA compliant websites with semantic HTML, proper ARIA, focus management, and screen reader support. Includes color contrast (4.5:1 text), keyboard navigation, form labels, and live regions. Use when implementing accessible interfaces, fixing screen reader issues, keyboard navigation, or troubleshooting "focus outline missing", "aria-label required", "insufficient contrast".

源码 ↗

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install clawskills:veeramanikandanr48~accessibility
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Aveeramanikandanr48~accessibility/file -o accessibility.md
Git 仓库获取源码
git clone https://github.com/clawdbot/skills/commit/a7b180b1fad04f625060d755333630a1615c6bb3
# Web Accessibility (WCAG 2.1 AA)

**Status**: Production Ready ✅
**Last Updated**: 2026-01-14
**Dependencies**: None (framework-agnostic)
**Standards**: WCAG 2.1 Level AA

---

## Quick Start (5 Minutes)

### 1. Semantic HTML Foundation

Choose the right element - don't use `div` for everything:

```html
<!-- ❌ WRONG - divs with onClick -->
<div onclick="submit()">Submit</div>
<div onclick="navigate()">Next page</div>

<!-- ✅ CORRECT - semantic elements -->
<button type="submit">Submit</button>
<a href="/next">Next page</a>
```

**Why this matters:**
- Semantic elements have built-in keyboard support
- Screen readers announce role automatically
- Browser provides default accessible behaviors

### 2. Focus Management

Make interactive elements keyboard-accessible:

```css
/* ❌ WRONG - removes focus outline */
button:focus { outline: none; }

/* ✅ CORRECT - custom accessible outline */
button:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
```

**CRITICAL:**
- Never remove focus outlines without replacement
- Use `:focus-visible` to show only on keyboard focus
- Ensure 3:1 contrast ratio for focus indicators

### 3. Text Alternatives

Every non-text element needs a text alternative:

```html
<!-- ❌ WRONG - no alt text -->
<img src="logo.png">
<button><svg>...</svg></button>

<!-- ✅ CORRECT - proper alternatives -->
<img src="logo.png" alt="Company Name">
<button aria-label="Close dialog"><svg>...</svg></button>
```

---

## The 5-Step Accessibility Process

### Step 1: Choose Semantic HTML

**Decision tree for element selection:**

```
Need clickable element?
├─ Navigates to another page? → <a href="...">
├─ Submits form? → <button type="submit">
├─ Opens dialog? → <button aria-haspopup="dialog">
└─ Other action? → <button type="button">

Grouping content?
├─ Self-contained article? → <article>
├─ Thematic section? → <section>
├─ Navigation links? → <nav>
└─ Supplementary info? → <aside>

Form element?
├─ Text input? → <input type="text">
├─ Multiple choice? → <select> or <input type="radio">
├─ Toggle? → <input type="checkbox"> or <button aria-pressed>
└─ Long text? → <textarea>
```

**See `references/semantic-html.md` for complete guide.**

### Step 2: Add ARIA When Needed

**Golden rule: Use ARIA only when HTML can't express the pattern.**

```html
<!-- ❌ WRONG - unnecessary ARIA -->
<button role="button">Click me</button>  <!-- Button already has role -->

<!-- ✅ CORRECT - ARIA fills semantic gap -->
<div role="dialog" aria-labelledby="title" aria-modal="true">
  <h2 id="title">Confirm action</h2>
  <!-- No HTML dialog yet, so role needed -->
</div>

<!-- ✅ BETTER - Use native HTML when available -->
<dialog aria-labelledby="title">
  <h2 id="title">Confirm action</h2>
</dialog>
```

**Common ARIA patterns:**
- `aria-label` - When visible label doesn't exist
- `aria-labelledby` - Reference existing text as label
- `aria-describedby` - Additional description
- `aria-live` - Announce dynamic updates
- `aria-expanded` - Collapsible/expandable state

**See `references/aria-patterns.md` for complete patterns.**

### Step 3: Implement Keyboard Navigation

**All interactive elements must be keyboard-accessible:**

```typescript
// Tab order management
function Dialog({ onClose }) {
  const dialogRef = useRef<HTMLDivElement>(null);
  const previousFocus = useRef<HTMLElement | null>(null);

  useEffect(() => {
    // Save previous focus
    previousFocus.current = document.activeElement as HTMLElement;

    // Focus first element in dialog
    const firstFocusable = dialogRef.current?.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
    (firstFocusable as HTMLElement)?.focus();

    // Trap focus within dialog
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
      if (e.key === 'Tab') {
        // Focus trap logic here
      }
    };

    document.addEventListener('keydown', handleKeyDown);
    return () => {
      document.removeEventListener('keydown', handleKeyDown);
      // Restore focus on close
      previousFocus.current?.focus();
    };
  }, [onClose]);

  return <div ref={dialogRef} role="dialog">...</div>;
}
```

**Essential keyboard patterns:**
- Tab/Shift+Tab: Navigate between focusable elements
- Enter/Space: Activate buttons/links
- Arrow keys: Navigate within components (tabs, menus)
- Escape: Close dialogs/menus
- Home/End: Jump to first/last item

**See `references/focus-management.md` for complete patterns.**

### Step 4: Ensure Color Contrast

**WCAG AA requirements:**
- Normal text (under 18pt): 4.5:1 contrast ratio
- Large text (18pt+ or 14pt+ bold): 3:1 contrast ratio
- UI components (buttons, borders): 3:1 contrast ratio

```css
/* ❌ WRONG - insufficient contrast */
:root {
  --background: #ffffff;
  --text: #999999;  /* 2.8:1 - fails WCAG AA */
}

/* ✅ CORRECT - sufficient contrast */
:root {
  --background: #ffffff;
  --text: #595959;  /* 4.6:1 - passes WCAG AA */
}
```

**Testing tools:**
- Browser DevTools (Chrome/Firefox have built-in checkers)
- Contrast checker extensions
- axe DevTools extension

**See `references/color-contrast.md` for complete guide.**

### Step 5: Make Forms Accessible

**Every form input needs a visible label:**

```html
<!-- ❌ WRONG - placeholder is not a label -->
<input type="email" placeholder="Email address">

<!-- ✅ CORRECT - proper label -->
<label for="email">Email address</label>
<input type="email" id="email" name="email" required aria-required="true">
```

**Error handling:**

```html
<label for="email">Email address</label>
<input
  type="email"
  id="email"
  name="email"
  aria-invalid="true"
  aria-describedby="email-error"
>
<span id="email-error" role="alert">
  Please enter a valid email address
</span>
```

**Live regions for dynamic errors:**

```html
<div role="alert" aria-live="assertive" aria-atomic="true">
  Form submission failed. Please fix the errors above.
</div>
```

**See `references/forms-validation.md` for complete patterns.**

---

## Critical Rules

### Always Do

✅ Use semantic HTML elements first (button, a, nav, article, etc.)
✅ Provide text alternatives for all non-text content
✅ Ensure 4.5:1 contrast for normal text, 3:1 for large text/UI
✅ Make all functionality keyboard accessible
✅ Test with keyboard only (unplug mouse)
✅ Test with screen reader (NVDA on Windows, VoiceOver on Mac)
✅ Use proper heading hierarchy (h1 → h2 → h3, no skipping)
✅ Label all form inputs with visible labels
✅ Provide focus indicators (never just `outline: none`)
✅ Use `aria-live` for dynamic content updates

### Never Do

❌ Use `div` with `onClick` instead of `button`
❌ Remove focus outlines without replacement
❌ Use color alone to convey information
❌ Use placeholders as labels
❌ Skip heading levels (h1 → h3)
❌ Use `tabindex` > 0 (messes with natural order)
❌ Add ARIA when semantic HTML exists
❌ Forget to restore focus after closing dialogs
❌ Use `role="presentation"` on focusable elements
❌ Create keyboard traps (no way to escape)

---

## Known Issues Prevention

This skill prevents **12** documented accessibility issues:

### Issue #1: Missing Focus Indicators

**Error**: Interactive elements have no visible focus indicator
**Source**: WCAG 2.4.7 (Focus Visible)
**Why It Happens**: CSS reset removes default outline
**Prevention**: Always provide custom focus-visible styles

### Issue #2: Insufficient Color Contrast

**Error**: Text has less than 4.5:1 contrast ratio
**Source**: WCAG 1.4.3 (Contrast Minimum)
**Why It Happens**: Using light gray text on white background
**Prevention**: Test all text colors with contrast checker

### Issue #3: Missing Alt Text

**Error**: Images missing alt attributes
**Source**: WCAG 1.1.1 (Non-text Content)
**Why It Happens**: Forgot to add or thought it was optional
**Prevention**: Add alt="" for decorative, descriptive alt for meaningful images

### Issue #4: Keyboard Navigation Broken

**Error**: Interactive elements not reachable by k