ワンクリックで
accessibility
WCAG 2.1 AA patterns for inclusive UI. Load when building components needing a11y, ARIA, or keyboard nav
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
WCAG 2.1 AA patterns for inclusive UI. Load when building components needing a11y, ARIA, or keyboard nav
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
External API evaluation and MCP server wrapper generation. Load when integrating a REST/GraphQL API as a workspace tool
Capability-based provider system patterns. Load when creating providers or wiring module-provider integration
Full-stack WebSocket subscription development. Load when adding WS routes, topic services, or debugging WS data flows
Systematic Python type error resolution (mypy/pyright). Load when fixing backend type check failures
Type-first Python with Pydantic, Protocol, and discriminated unions. Load when writing models or enforcing typing
Systematic type error resolution for Python (mypy/pyright) and TypeScript (vue-tsc). Use when fixing type errors, resolving type check failures, or optimizing type imports.
| name | accessibility |
| description | WCAG 2.1 AA patterns for inclusive UI. Load when building components needing a11y, ARIA, or keyboard nav |
| user-invocable | false |
Actionable WCAG 2.1 AA patterns for building inclusive UI — semantic HTML, keyboard navigation, ARIA, focus management, color contrast, and testing checklists.
| Principle | Description |
|---|---|
| Perceivable | Content can be perceived through different senses |
| Operable | Interface can be operated by all users |
| Understandable | Content and interface are understandable |
| Robust | Content works with assistive technologies |
Use native HTML elements before reaching for ARIA:
<!-- ❌ ARIA role on div -->
<div role="button" tabindex="0">Click me</div>
<!-- ✅ Native button -->
<button>Click me</button>
Landmark regions:
<header><!-- site header --></header>
<nav aria-label="Main"><!-- primary nav --></nav>
<main id="main-content"><!-- page content --></main>
<aside><!-- sidebar --></aside>
<footer><!-- site footer --></footer>
Skip link (first focusable element):
<a href="#main-content" class="skip-link">Skip to main content</a>
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px 16px;
z-index: 100;
}
.skip-link:focus { top: 0; }
All interactive elements must be keyboard-operable:
| Key | Action |
|---|---|
Tab | Move to next interactive element |
Shift+Tab | Move to previous interactive element |
Enter / Space | Activate focused control |
Arrow | Navigate within composite widgets (tabs, menus, grids) |
Escape | Close dialogs, menus, popovers |
Rules:
tabindex (except tabindex="-1" for programmatic focus targets like headings)Focus visibility:
/* Never remove focus outlines globally */
:focus-visible {
outline: 2px solid var(--color-focus, #005fcc);
outline-offset: 2px;
}
Roving tabindex for composite widgets (tabs, grids):
tabindex="0"tabindex="-1"Text alternatives:
<!-- Images: descriptive alt -->
<img src="chart.png" alt="Bar chart showing 40% increase in Q3 sales">
<!-- Decorative images: empty alt -->
<img src="border.png" alt="" role="presentation">
<!-- Icon buttons: accessible name -->
<button aria-label="Close dialog">
<svg aria-hidden="true"><!-- icon --></svg>
</button>
ARIA states for interactive widgets:
<!-- Expandable sections -->
<button aria-expanded="false" aria-controls="panel-1">Details</button>
<div id="panel-1" hidden>Content</div>
<!-- Invalid form fields -->
<input aria-invalid="true" aria-describedby="email-error">
<p id="email-error" role="alert">Please enter a valid email</p>
<!-- Loading states -->
<button aria-busy="true" aria-live="polite" disabled>Loading...</button>
Custom tabs pattern:
<div role="tablist" aria-label="Product info">
<button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1">Desc</button>
<button role="tab" id="tab-2" aria-selected="false" aria-controls="panel-2" tabindex="-1">Reviews</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">...</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>...</div>
| Element | AA Minimum Ratio |
|---|---|
| Normal text (< 18px / < 14px bold) | 4.5:1 |
| Large text (>= 18px / >= 14px bold) | 3:1 |
| UI components & graphics | 3:1 |
Never rely on color alone to convey meaning:
<!-- ❌ Only color indicates error -->
<input style="border-color: red">
<!-- ✅ Color + icon + text -->
<input aria-invalid="true" aria-describedby="err">
<span id="err">
<svg aria-hidden="true"><!-- error icon --></svg>
Please enter a valid email
</span>
<!-- Explicit label association -->
<label for="email">Email address</label>
<input type="email" id="email" autocomplete="email" required
aria-describedby="email-hint">
<p id="email-hint">We'll never share your email</p>
On validation failure:
aria-invalid="true" on the fieldaria-describedbyrole="alert" or aria-live="assertive" on the error container<!-- Polite: announced after current speech -->
<div aria-live="polite" aria-atomic="true">
3 items in your cart
</div>
<!-- Assertive: interrupts current speech (use sparingly) -->
<div role="alert" aria-live="assertive">
Connection lost. Reconnecting...
</div>
/* Respect reduced motion preference */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
Modal dialog focus trap:
Route transitions (SPA):
alt textlang attribute on <html>main, nav, header, footer)aria-live for dynamic content updatesprefers-reduced-motion respected<div> or <span> for interactive elements instead of <button>, <a>, <input>*:focus { outline: none } without replacement focus stylesplaceholder as the only label (disappears on input, poor contrast)<nav role="navigation"> is unnecessary)