Install
$ agentstack add skill-kid-sid-claude-spellbook-accessibility ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Accessibility
Build UIs that work for everyone — keyboard users, screen reader users, users with motor impairments, low-vision users, and users in constrained environments.
When to Activate
- Building or reviewing UI components (forms, modals, menus, tables)
- Implementing keyboard navigation or focus management
- Auditing a page or component for WCAG conformance
- Adding ARIA attributes to custom interactive widgets
- Reviewing color palette or typography choices for contrast
- Writing E2E tests that must be accessible-first
- Preparing for an accessibility audit or compliance review
WCAG Conformance Levels
| Level | What it means | Typical target | |---|---|---| | A | Bare minimum — removes critical blockers | Never ship below this | | AA | Standard legal/compliance baseline (most regulations) | Default target | | AAA | Enhanced — not always achievable for all content | Aim where practical |
Most legal requirements (ADA, WCAG 2.1 AA, EN 301 549) map to AA.
Semantic HTML First
Use the right element before reaching for ARIA.
Submit
Home
Page Title
Submit
Home
Page Title
Landmark Regions
Heading Hierarchy
Site Name
Section Title
Visual heading, not semantic
Page Title
Section
Subsection
Another Section
ARIA
Use ARIA only when native HTML cannot express the semantics.
Rule of thumb
1. Use native HTML element → , ,
2. Use native attribute → disabled, required, checked
3. Use ARIA role + state + property → last resort for custom widgets
Common Roles
States and Properties
Menu
...
Email is required
✕
ARIA Anti-Patterns
Submit
Submit order
Keyboard Navigation
Focus Order
First visually
Second visually
Custom focusable
Keyboard Patterns by Widget
| Widget | Keys required | |---|---| | Button | Enter, Space | | Link | Enter | | Checkbox | Space to toggle | | Radio group | Arrow keys within group, Tab to leave | | Select / Listbox | Arrow keys, Enter, Escape | | Modal dialog | Tab/Shift+Tab trapped inside, Escape closes | | Menu | Arrow keys, Escape, Enter/Space to select | | Tabs | Arrow keys between tabs, Tab into panel | | Slider | Arrow keys, Home, End |
Focus Management
// Move focus into dialog when it opens
function openModal(modalEl: HTMLElement) {
const firstFocusable = modalEl.querySelector(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
firstFocusable?.focus();
}
// Trap focus inside modal
function trapFocus(modalEl: HTMLElement, event: KeyboardEvent) {
if (event.key !== "Tab") return;
const focusable = modalEl.querySelectorAll(
'button:not([disabled]), [href], input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (event.shiftKey && document.activeElement === first) {
last.focus(); event.preventDefault();
} else if (!event.shiftKey && document.activeElement === last) {
first.focus(); event.preventDefault();
}
}
// Return focus to trigger when dialog closes
function closeModal(triggerEl: HTMLElement) {
triggerEl.focus();
}
Color and Visual Design
Contrast Ratios (WCAG AA)
| Text | Minimum ratio | Enhanced (AAA) | |---|---|---| | Normal text (
Active
⚠ Email is required
### Motion and Animation
```css
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Forms
Email address
We'll only use this to send your receipt.
Shipping address
Street
Phone number
Enter a valid 10-digit phone number
Images and Media
...
Testing
Manual checklist (do for every component)
- Tab through the page — every interactive element reachable?
- Nothing requires a mouse — all actions completable by keyboard?
- Focus indicator always visible?
- Test with screen reader (NVDA/Firefox on Windows, VoiceOver/Safari on Mac)
Automated tools
# Axe CLI
npx axe http://localhost:3000 --reporter cli
# Pa11y
npx pa11y http://localhost:3000
# Playwright + axe-core
npm install @axe-core/playwright
// Playwright accessibility scan
import { checkA11y, injectAxe } from "axe-playwright";
test("home page has no accessibility violations", async ({ page }) => {
await page.goto("/");
await injectAxe(page);
await checkA11y(page, null, {
detailedReport: true,
detailedReportOptions: { html: true },
});
});
Contrast check
# Check contrast via CLI
npx contrast-ratio "#595959" "#ffffff"
# Or use browser DevTools: Accessibility panel → Contrast ratio
Red Flags
aria-labelon every element — ARIA overrides native semantics; use semantic HTML first and add ARIA only when native elements can't express the required role or state- **
role="button"on a `or** — custom button roles require manually implementing keyboard behavior; use` and get focus, Enter, and Space for free alt=""on informational images — empty alt hides the image from screen readers entirely; empty alt is only correct for purely decorative images that convey no contentplaceholderas the only label — placeholder text disappears on focus and has insufficient contrast ratio; every input must have an associated visible `` elementoutline: nonewithout a visible replacement — removing the default focus ring makes keyboard navigation invisible; always provide a visible focus indicator that meets 3:1 contrast- Automated scan as the complete accessibility test — axe/pa11y catches ~30% of WCAG issues; focus order, reading order, and screen reader announcements require manual verification
- Modal that doesn't trap focus — focus that escapes an open modal to background content disorients screen reader users; implement a focus trap and return focus to the trigger element on close
Checklist
- [ ] All interactive elements reachable and operable by keyboard alone
- [ ] Focus order follows logical reading order — no
tabindex > 0 - [ ] Focus indicator visible at all times (no
outline: nonewithout replacement) - [ ] Custom widgets implement correct ARIA role, state, and keyboard pattern
- [ ] Every `
hasalt` — descriptive for content, empty for decorative - [ ] All form inputs have associated `` — not placeholder only
- [ ] Error messages programmatically associated with their input (
aria-describedby) - [ ] Color contrast ≥ 4.5:1 for normal text, ≥ 3:1 for large text and UI components
- [ ] No information conveyed by color alone
- [ ]
prefers-reduced-motionrespected for animations - [ ] Modal dialogs trap focus and return it to trigger on close
- [ ] Page has a single `
, logical heading hierarchy starting ath1` - [ ] Automated scan (axe/pa11y) passes with zero violations
> See also: solution-testing, coding-standards
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kid-sid
- Source: kid-sid/claude-spellbook
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.