component-a11y-gate
Paste into Claude Code, Claude.ai, the desktop app, or the browser extension — ⌘/Ctrl+V. ✓ Copied. Switch to Claude and paste — ⌘/Ctrl+V.
Component Accessibility Gate — WCAG 2.2 AA
Static analysis of component source files against WCAG 2.2 AA. This skill catches the structural and semantic issues that automated runtime tools (axe, Lighthouse) cannot evaluate until the component is rendered — so it gates accessibility debt at the point of creation, not after QA.
Usage
/component-a11y-gate <target> [options]
Target: file path, directory, or diff to check only currently staged/changed files.
Options:
| Flag | Values | Default | Description |
|---|---|---|---|
--tokens |
file path | auto-detect | Token file for resolving contrast pairs |
--adapter |
react, razor, astro, vue |
auto-detect | Markup dialect |
--wcag-level |
aa, aaa |
aa |
Contrast gate threshold |
Examples:
/component-a11y-gate src/components/Button/
/component-a11y-gate diff --adapter react --tokens src/tokens/tokens.json
/component-a11y-gate src/components/ --wcag-level aaa
Phase 1 — Gather Files
- Resolve the target to a list of files with UI relevance:
- React/TypeScript:
.tsx,.jsx,.ts,.js,.module.css,.css,.scss - Razor/.NET:
.cshtml,.razor,.html - Astro:
.astro,.css,.scss - Vue:
.vue,.css,.scss
- React/TypeScript:
- If
diffmode: rungit diff --staged --name-onlyand filter to UI-relevant extensions. - Read the token file from
--tokensor auto-detect at common paths:tokens/tokens.json,src/tokens/tokens.json,tokens/tokens.css
Phase 2 — Contrast Analysis
For every hardcoded color value found in the component source, check against the token layer.
2.1 Flag raw color values (not using tokens)
Patterns to detect:
- Inline styles:
style={{ color: '#333' }} - Hardcoded Tailwind arbitrary values:
text-[#333],bg-[#0057B8] - CSS values outside a custom property reference:
color: #333; - SCSS variables with raw values not derived from the token file
Severity: serious — raw values bypass the design system's accessibility guarantees.
2.2 Resolve token references and compute contrast
For each CSS custom property pair or token reference where foreground and background are both identifiable, resolve the values from the token file and compute the contrast ratio.
Use the W3C relative luminance formula:
sRGB channel: c_lin = c <= 0.04045 ? c/12.92 : ((c+0.055)/1.055)^2.4
L = 0.2126*R_lin + 0.7152*G_lin + 0.0722*B_lin
contrast = (L_lighter + 0.05) / (L_darker + 0.05)
Report as a table:
| Element | FG token | BG token | Ratio | Gate | Result |
|---------|----------|----------|-------|------|--------|
| .btn__label | --color-on-interactive-primary | --color-interactive-primary | 6.1:1 | 4.5:1 | PASS |
2.3 Focus indicator contrast (SC 1.4.11 + 2.4.11)
Locate :focus, :focus-visible, and outline CSS rules. Check:
- Focus ring color against adjacent background: must be ≥ 3:1
- Focus indicator perimeter: minimum 2 CSS pixels of enclosed outline (SC 2.4.11 WCAG 2.2)
outline: noneoroutline: 0with no replacement = serious violation
Phase 3 — Semantic and ARIA Review
3.1 Identify interactive patterns
Scan for:
onClick,@click,v-on:click,@onclickhandlers on non-interactive elements (<div>,<span>,<p>) = critical unlessrole+tabindex="0"+ keyboard handler present- Custom interactive elements missing
roleattribute = critical - ARIA roles that do not match the HTML element (e.g.
role="button"on<a>) — flag for review
3.2 Required ARIA attributes by role
| Role | Required attributes | Missing = |
|---|---|---|
button |
aria-disabled when disabled |
moderate |
checkbox |
aria-checked |
critical |
switch |
aria-checked |
critical |
combobox |
aria-expanded, aria-controls |
serious |
dialog |
aria-modal="true", aria-labelledby |
serious |
tab |
aria-selected, aria-controls |
serious |
tabpanel |
aria-labelledby |
moderate |
slider |
aria-valuenow, aria-valuemin, aria-valuemax |
critical |
progressbar |
aria-valuenow, aria-valuemin, aria-valuemax |
serious |
alertdialog |
aria-labelledby, aria-describedby |
serious |
3.3 aria-label quality
Flag:
aria-labelon non-interactive elements without a role = minoraria-labelthat duplicates the visible text exactly — redundant but not harmful; note itaria-labelthat is generic ("icon","button","close") = serious (SC 2.5.3 Label in Name)aria-labelledbyoraria-describedbyreferencing an ID that does not exist = critical
3.4 Image alt text
<img>withoutaltattribute = critical<img alt="">on informative image = critical (checkrole="presentation"oraria-hiddenconsistency)- SVG without
aria-hidden="true"(decorative) or<title>+aria-labelledby(informative) = serious - CMS-managed images (Sitecore
<Image />, Optimizely<img>bound to ImageData): note that alt must come from the content model, not be hardcoded
3.5 Form field labelling (SC 1.3.1, 3.3.1, 3.3.2)
<input>,<select>,<textarea>without an associated<label for>oraria-labelledby= critical- Required fields with no
requiredoraria-required="true"= moderate - Error messages not linked to their field via
aria-describedby= serious
Phase 4 — Keyboard Interaction Model
4.1 Tab stop correctness
tabindex > 0anywhere = serious (breaks natural tab order; SC 2.4.3)- Interactive element with
tabindex="-1"that receives focus programmatically (e.g. modal target, skip link destination) = acceptable — note it
4.2 Keyboard activation (SC 2.1.1)
Composite widget keyboard models expected:
| Widget | Expected keys |
|---|---|
| Button / Link | Enter (and Space for buttons) |
| Menu item | Enter; arrow keys to navigate; Escape to close |
| Tab | Arrow keys to move between tabs; Tab to move into/out of tabpanel |
| Tree item | Arrow keys; Enter/Space to expand; Home/End |
| Grid cell | Arrow keys; Enter to activate; Escape to cancel |
| Combobox | Type to filter; Arrow keys to navigate listbox; Escape to dismiss |
Flag any composite widget that only handles onClick without onKeyDown/onKeyUp = critical.
4.3 Focus management
- Modal/dialog opened without moving focus inside it = critical (SC 2.1.1)
- Modal closed without returning focus to trigger = serious
- Keyboard trap: modal that does NOT trap focus (focus escapes to background) = critical (SC 2.1.2)
- Dynamic content inserted without focus or live region announcement = moderate
Phase 5 — Target Size (SC 2.5.8 — WCAG 2.2)
Scan for explicit width and height values on interactive elements.
- Targets smaller than 24×24 CSS pixels with no 24 px spacing offset = moderate
- Icon-only buttons (
aria-labelpresent, no text) withwidth/height< 24px = moderate - Inline links in dense text blocks: note risk; not automatically flaggable from static analysis
Phase 6 — Output Report
## Accessibility Gate — <ComponentName>
**Target:** <file path>
**Standard:** WCAG 2.2 Level AA
**Token file:** <resolved path or "not found">
### Verdict: PASS / FAIL
### Findings
| # | Severity | SC | Category | Description | Fix |
|---|---|---|---|---|---|
| 1 | critical | 1.1.1 | Alt text | `<img src={props.image}>` missing alt attribute | Add `alt={props.imageAlt ?? ''}` |
| 2 | serious | 1.4.3 | Contrast | `#6B6B6B` on `#C4C4C4` = 3.2:1 (need 4.5:1) | Use `--color-on-interactive-disabled` token |
---
### Contrast Table
<table from Phase 2>
---
### WCAG 2.2 New Criteria Status
- SC 2.4.11 Focus Appearance: PASS / FAIL / MANUAL CHECK
- SC 2.5.8 Target Size: PASS / FLAG / MANUAL CHECK
- SC 3.3.7 Redundant Entry: N/A (no multi-step form)
- SC 3.3.8 Accessible Authentication: N/A (no auth flow)
---
### Coverage Note
Static analysis covers: HTML structure, ARIA attributes, CSS focus styles, hardcoded colors.
Static analysis cannot cover: rendered contrast of dynamically set theme values, keyboard
focus order in a running browser, screen reader announcement order.
Complement with /accessibility-wcag-scan on the running application.
Severity gates for merge decisions:
- Any
critical= recommend blocking merge serious= resolve before merge; companion ticket acceptable if acknowledgedmoderate/minor= advisory backlog items
Technical details
- Plugin
- design-to-code-builder
- Author
- Dakota Smith
- Skill name
component-a11y-gate- Maturity
- experimental
- Security review
- No security review
- Owners
-
- engineering
- design
- Argument hint
<file-or-directory> [--tokens <path>] [--adapter react|razor|astro|vue] [--wcag-level aa|aaa]- Allowed tools
-
ReadGlobGrepWriteBash(git diff *)Bash(git show *)Bash(node *)
- Source
plugins/design-to-code-builder/skills/component-a11y-gate/SKILL.md
Edit this skill on GitHub — opens GitHub's web editor. No git knowledge needed.