Help us improve
Share bugs, ideas, or general feedback.
Test web applications with screen readers including VoiceOver, NVDA, and JAWS for accessibility compliance. Use when validating screen reader compatibility, debugging accessibility issues, ensuring assistive technology support, writing ARIA markup, verifying live region announcements, or testing keyboard navigation with screen readers. Also use when a new interactive component is built and needs manual verification, when automated a11y tools report no issues but you suspect screen reader problems, or when reviewing Templ components, Twig templates, or Datastar-enhanced pages for screen reader compatibility. Covers VoiceOver (macOS), NVDA (Windows), testing protocols, and common failure patterns.
npx claudepluginhub design-machines-studio/depot --plugin accessibility-complianceHow this skill is triggered — by the user, by Claude, or both
Slash command
/accessibility-compliance:screen-reader-testingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Automated tools catch ~30-40% of accessibility issues. The rest requires manual testing with screen readers. This skill provides structured testing protocols, VoiceOver-first workflows (since DM projects are built on macOS), and patterns for the most common failures.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.
Share bugs, ideas, or general feedback.
Automated tools catch ~30-40% of accessibility issues. The rest requires manual testing with screen readers. This skill provides structured testing protocols, VoiceOver-first workflows (since DM projects are built on macOS), and patterns for the most common failures.
Screen reader testing is not about making the screen reader say the right words. It is about verifying that the information architecture — headings, landmarks, labels, relationships — communicates the same meaning to a non-visual user as the visual design communicates to a sighted user.
The question is never "Does the screen reader read this?" but "Does a screen reader user understand the page structure, current state, and available actions?"
VoiceOver is the primary testing tool for Design Machines projects.
| Action | Keys | Notes |
|---|---|---|
| Next element | VO + Right Arrow | VO = Control + Option |
| Previous element | VO + Left Arrow | |
| Activate | VO + Space | Click/press the focused element |
| Read heading | VO + Cmd + H | Next heading |
| Read landmark | VO + Cmd + L | Next landmark region |
| Rotor | VO + U | Navigate by type (headings, links, forms) |
| Read from cursor | VO + A | Read everything from current position |
| Stop reading | Control | |
| Web rotor | VO + U, then Left/Right arrows | Switch between heading/link/form/landmark lists |
Always test in Safari first — it has the best VoiceOver integration on macOS. Chrome and Firefox have quirks with certain ARIA patterns.
Open the VoiceOver Rotor (VO + U) and navigate to the Landmarks list.
Expected landmarks for a typical page:
banner — site headernavigation — primary nav (may have multiple, each labeled)main — primary content areacomplementary — sidebar content (if applicable)contentinfo — site footerFailures to catch:
<main> element (most common)<nav> elements without aria-label to distinguish them<div> where a landmark element should be used<header> and <footer> inside <article> creating unexpected nested landmarksOpen the Rotor and navigate to the Headings list.
Check:
<h1> per page (the page title)Common failures in Templ/Twig templates:
<h2> when nested inside an <h3> contextNavigate the Links list in the Rotor.
Check:
opens in new tab)aria-label or .visually-hidden textTab through every form on the page.
For each form field, verify VoiceOver announces:
aria-describedby)Common failures:
aria-describedby<fieldset> + <legend>For pages that update without full page reload:
Live region requirements:
role="status" or aria-live="polite"role="alert" or aria-live="assertive"Datastar-specific concerns:
data-merge-morph can destroy and recreate DOM nodes, losing focusFor each custom widget (tabs, accordions, dialogs, menus):
| Widget | What to verify |
|---|---|
| Tabs | Arrow keys switch tabs, VO announces "selected", panel content changes |
| Accordion | Enter/Space toggles, VO announces "expanded/collapsed" |
| Dialog | Focus moves into dialog, Escape closes, focus returns to trigger |
| Dropdown menu | Arrow keys navigate items, Escape closes, VO announces current item |
| Toast/notification | VO announces when it appears (live region) |
| Loading spinner | VO announces loading state and completion |
Navigate through images:
alt="" (empty alt, not missing alt)aria-describedby or adjacent textrole="img" with aria-label, or aria-hidden="true" if decorative| Pattern | Problem | Fix |
|---|---|---|
| Icon-only button | No accessible name | Add <span class="visually-hidden">Label</span> |
| Color scheme toggle | State not announced | Add aria-pressed or aria-checked |
| Reel (horizontal scroll) | Not announced as scrollable | Add role="region" with aria-label |
| Status indicator | Color-only meaning | Add .visually-hidden text or aria-label |
| Custom checkbox | Native input hidden with display:none | Use .visually-hidden class instead |
| Pattern | Problem | Fix |
|---|---|---|
| Partial page update | Content changes silently | Wrap target in aria-live="polite" |
| Form submission | Success/error not announced | Use role="alert" for errors, role="status" for success |
| Focus after morph | Focus lost to document body | Set focus explicitly with data-on-load or JS |
| Optimistic UI | Intermediate state confusing | Announce "Saving..." then "Saved" via live region |
| Pattern | Problem | Fix |
|---|---|---|
| Matrix blocks | Heading levels wrong in context | Use dynamic heading level based on nesting depth |
| Rich text output | Missing alt on inline images | Enforce alt text in CMS field settings |
| Entry links | "Read more" everywhere | Use entry title as link text |
| Navigation | Multiple <nav> without labels | Add aria-label to each nav element |
Use this for each page audit:
## Screen Reader Audit: [Page Name]
**Tested with:** VoiceOver + Safari [version]
**Date:** YYYY-MM-DD
### Landmarks
- [ ] banner present
- [ ] navigation labeled (if multiple navs)
- [ ] main present
- [ ] contentinfo present
### Headings
- [ ] Single h1
- [ ] No skipped levels
- [ ] Meaningful outline
### Links & Buttons
- [ ] All links descriptive
- [ ] Buttons describe action
- [ ] Icon-only elements have accessible names
### Forms
- [ ] All inputs labeled
- [ ] Required fields announced
- [ ] Error messages associated
- [ ] Fieldsets group related inputs
### Dynamic Content
- [ ] Updates announced via live regions
- [ ] Focus managed after DOM changes
- [ ] Loading states communicated
### Images
- [ ] Informative images have meaningful alt
- [ ] Decorative images have empty alt
- [ ] Complex images have extended descriptions
### Keyboard
- [ ] All interactive elements reachable
- [ ] Focus order logical
- [ ] Focus visible on all elements
- [ ] Escape closes overlays
- [ ] No keyboard traps
| File | Contents |
|---|---|
| ${CLAUDE_SKILL_DIR}/references/voiceover-testing.md | Detailed VoiceOver commands, Safari quirks, and macOS configuration |
| ${CLAUDE_SKILL_DIR}/references/manual-testing-protocols.md | Cross-browser testing protocols, NVDA reference, and regression testing |