web-accessibility について
Web 開発者向けに、WCAG 2.1 に沿ったアクセシブルなアプリの構築方法をまとめたスキルです。ARIA パターン、コントラスト要件、フォーム例を扱います。
主な機能
- 01操作要素とナビゲーションの ARIA パターン
- 02WCAG AA のコントラスト比
- 03アクセシブルなフォーム例
活用シーン
- 01モーダルダイアログやメニューの実装
- 02テキストと UI の色コントラストの確認
- 03必須項目を示すフォームの設計
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ hoodini/ ai-agents-skills --skill web-accessibilityインストール後、Claude Code で/web-accessibilityを入力して読み込まれたか確認します。
詳細
ウェブアクセシビリティ(WCAG 2.1)
誰もが利用できる、アクセシブルなウェブアプリケーションを構築します。
ARIA パターン
ボタン
<button
type="button"
aria-pressed={isPressed}
aria-disabled={isDisabled}
onClick={handleClick}
>
Toggle Feature
</button>
モーダルダイアログ
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
aria-describedby="modal-description"
>
<h2 id="modal-title">Confirm Action</h2>
<p id="modal-description">Are you sure you want to proceed?</p>
<button onClick={onConfirm}>Confirm</button>
<button onClick={onCancel}>Cancel</button>
</div>
ナビゲーションメニュー
<nav aria-label="Main navigation">
<ul role="menubar">
<li role="none">
<a role="menuitem" href="/home">Home</a>
</li>
<li role="none">
<button
role="menuitem"
aria-haspopup="true"
aria-expanded={isOpen}
>
Products
</button>
{isOpen && (
<ul role="menu" aria-label="Products submenu">
<li role="none">
<a role="menuitem" href="/products/new">New</a>
</li>
</ul>
)}
</li>
</ul>
</nav>
キーボード操作
フォーカス管理
import { useEffect, useRef } from 'react';
function Modal({ isOpen, onClose, children }) {
const modalRef = useRef<HTMLDivElement>(null);
const previousFocus = useRef<HTMLElement | null>(null);
useEffect(() => {
if (isOpen) {
previousFocus.current = document.activeElement as HTMLElement;
modalRef.current?.focus();
} else {
previousFocus.current?.focus();
}
}, [isOpen]);
// Trap focus within modal
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Escape') {
onClose();
}
if (e.key === 'Tab') {
const focusable = modalRef.current?.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (focusable && focusable.length > 0) {
const first = focusable[0] as HTMLElement;
const last = focusable[focusable.length - 1] as HTMLElement;
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}
};
if (!isOpen) return null;
return (
<div
ref={modalRef}
role="dialog"
aria-modal="true"
tabIndex={-1}
onKeyDown={handleKeyDown}
>
{children}
</div>
);
}
色のコントラスト
最小コントラスト比(WCAG AA):
- 通常のテキスト:4.5:1
- 大きなテキスト(18pt 以上):3:1
- UI コンポーネント:3:1
function getContrastRatio(color1: string, color2: string): number {
const lum1 = getLuminance(color1);
const lum2 = getLuminance(color2);
const lighter = Math.max(lum1, lum2);
const darker = Math.min(lum1, lum2);
return (lighter + 0.05) / (darker + 0.05);
}
function getLuminance(hex: string): number {
const rgb = hexToRgb(hex);
const [r, g, b] = rgb.map((c) => {
c = c / 255;
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
アクセシブルなフォーム
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="email">
Email address
<span aria-hidden="true">*</span>
<span className="sr-only">(required)</span>
</label>
<input
id="email"
type="email"
aria-required="true"
aria-invalid={errors.email ? 'true' : 'false'}
aria-describedby={errors.email ? 'email-error' : undefined}
/>
{errors.email && (
<p id="email-error" role="alert" className="error">
{errors.email}
</p>
)}
</div>
<button type="submit">Submit</button>
</form>
スクリーンリーダー専用コンテンツ
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
テスト
# Automated testing
npm install -D axe-core @axe-core/react
# In tests
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('component is accessible', async () => {
const { container } = render(<MyComponent />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
リソース
- WCAG 2.1 ガイドライン: https://www.w3.org/WAI/WCAG21/quickref/
- ARIA オーサリングプラクティス: https://www.w3.org/WAI/ARIA/apg/
コメント
Markdown 対応