Skip to content
Development
Skill

/stitch-a11y

Audits Stitch-generated components for WCAG 2.1 AA accessibility issues and applies fixes — semantic HTML, ARIA attributes, keyboard navigation, focus management, and screen reader support.

From plugin
stitch-kit
4536 skills1 agent2 hooks
Install
$ npx -y skills add gabelul/stitch-kit --skill stitch-a11y --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/stitch-a11y

Context preview

The summary Claude sees to decide when to auto-load this skill.

Audits Stitch-generated components for WCAG 2.1 AA accessibility issues and applies fixes — semantic HTML, ARIA attributes, keyboard navigation, focus management, and screen reader support.

SKILL.md

stitch-a11y.SKILL.md
name: stitch-a11y
description: Audits Stitch-generated components for WCAG 2.1 AA accessibility issues and applies fixes — semantic HTML, ARIA attributes, keyboard navigation, focus management, and screen reader support.
allowed-tools:
  - "Read"
  - "Write"
  - "Bash"

Stitch Accessibility Audit & Fix

You are an accessibility engineer. You audit components generated from Stitch designs, identify WCAG 2.1 AA violations, and apply fixes directly to the source files. You don't just report issues — you fix them.

**Run this skill AFTER** component generation. Components should be working before you audit them.

When to use this skill

Use this skill when:

  • Components are generated and working, and need accessibility review before shipping
  • The design has complex interactive patterns (modals, dropdowns, tab panels, accordions, carousels)
  • The user mentions "accessibility", "a11y", "WCAG", "screen reader", "keyboard navigation"
  • Preparing for a production launch or accessibility audit

Step 1: Discover components to audit

Read the project file structure to find all component files:

# Next.js / React
find src -name "*.tsx" -not -path "*/node_modules/*"

# SvelteKit
find src -name "*.svelte" -not -path "*/node_modules/*"

Read each component file before auditing. Focus your energy on interactive components — static content needs less attention than forms, navigation, modals, and dropdowns.

Step 2: The audit — 6 categories

Work through each category systematically for every component.

Category 1: Semantic HTML

**Violations to find:**

  • `<div>` or `<span>` used for navigation, headers, footers, main content, articles, sections
  • `<div onClick>` instead of `<button>` or `<a>`
  • Heading hierarchy out of order (h3 before h2, skipping levels)
  • Tables used for layout (not data)
  • Lists rendered as plain `<div>` elements

**Fixes:**

// ❌ Wrong
<div className="nav">
  <div onClick={goHome}>Home</div>
</div>

// ✅ Fixed
<nav aria-label="Main navigation">
  <a href="/">Home</a>
</nav>

// ❌ Wrong — div button
<div className="btn" onClick={handleClick}>Submit</div>

// ✅ Fixed — real button
<button type="button" onClick={handleClick}>Submit</button>

// ❌ Wrong — visual list as divs
<div className="menu">
  <div>Item 1</div>
  <div>Item 2</div>
</div>

// ✅ Fixed
<ul role="list">
  <li>Item 1</li>
  <li>Item 2</li>
</ul>

Category 2: ARIA attributes

Only add ARIA where semantic HTML doesn't provide sufficient information. Remember: **no ARIA is better than bad ARIA.**

**Violations to find:**

  • Icon-only buttons with no accessible name
  • Multiple `<nav>` landmarks with no `aria-label`
  • Multiple `<main>` elements
  • Status/live regions that update dynamically but have no `aria-live`
  • Interactive elements missing `aria-expanded`, `aria-haspopup`, `aria-controls`

**Fixes:**

// Icon-only button
<button aria-label="Close dialog" type="button">
  <XIcon aria-hidden="true" />
</button>

// Multiple nav regions
<nav aria-label="Main navigation">...</nav>
<nav aria-label="Breadcrumb">...</nav>
<nav aria-label="Pagination">...</nav>

// Dropdown toggle
<button
  aria-expanded={isOpen}
  aria-haspopup="menu"
  aria-controls="user-menu"
>
  Account
</button>
<ul id="user-menu" role="menu" hidden={!isOpen}>
  <li role="menuitem"><a href="/profile">Profile</a></li>
</ul>

// Live status region
<div aria-live="polite" aria-atomic="true" className="sr-only">
  {statusMessage}
</div>

Category 3: Keyboard navigation

Every interactive element must be operable by keyboard. Test this mental model: Tab through the page — can you reach and activate every action?

**Violations to find:**

  • Custom interactive elements that don't receive Tab focus
  • `tabIndex={-1}` used where focus should be reachable
  • `tabIndex={1}` or higher (breaks natural tab order)
  • Modal open — focus not moved into modal
  • Modal closed — focus not returned to trigger
  • Dropdown closed with Escape — focus not returned

**Fixes:**

// Focus management for modal — React
import { useEffect, useRef } from 'react'

export function Modal({ isOpen, onClose, children }: ModalProps) {
  const modalRef = useRef<HTMLDivElement>(null)
  const triggerRef = useRef<HTMLButtonElement>(null)

  useEffect(() => {
    if (isOpen) {
      // Move focus into modal when it opens
      modalRef.current?.focus()
    }
  }, [isOpen])

  function handleClose() {
    onClose()
    // Return focus to trigger when modal closes
    triggerRef.current?.focus()
  }

  return (
    <>
      <button ref={triggerRef} onClick={() => setIsOpen(true)}>
        Open Modal
      </button>
      {isOpen && (
        <div
          ref={modalRef}
          role="dialog"
          aria-modal="true"
          aria-labelledby="modal-title"
          tabIndex={-1}  /* Makes div focusable without entering tab order */
        >
          <h2 id="modal-title">Modal Title</h2>
          {children}
          <button onClick={handleClose}>Close</button>
        </div>
      )}
    </>
  )
}

// Keyboard handler for custom interactive elements
<div
  role="button"
  tabIndex={0}
  onClick={handleAction}
  onKeyDown={(e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault()
      handleAction()
    }
  }}
>
  Custom button behavior
</div>
<!-- Focus management in Svelte -->
<script lang="ts">
  let dialogEl = $state<HTMLDialogElement>()
  let triggerEl = $state<HTMLButtonElement>()
  let isOpen = $state(false)

  function openDialog() {
    isOpen = true
    // tick() ensures DOM is updated before focusing
    tick().then(() => dialogEl?.focus())
  }

  function closeDialog() {
    isOpen = false
    triggerEl?.focus()  // Return focus to trigger
  }
</script>

<button bind:this={triggerEl} onclick={openDialog}>Open</button>

{#if isOpen}
  <dialog
    bind:this={dialogEl}
    tabindex="-1"
    aria-modal="true"
    onkeydown={(e) => e.key === 'Escape' && closeDialog()}
  >
    <button on
Read more
Ships withstitch-kit

Your coding agent writes decent code and designs terrible UI. stitch-kit fixes the second half — it wires agents into Google Stitch (text prompts → genuinely beautiful screens) and teaches them to drive it properly.

Get the whole plugin

Other skills on stitch-kit.