Claude Codeでアクセシビリティ対応を効率化する方法
Claude Codeを活用してWebアクセシビリティ(WCAG準拠)の実装・監査・修正を効率的に行う方法を解説します。
アクセシビリティ対応にClaude Codeを使うメリット
Webアクセシビリティの対応は重要ですが、WCAG基準の理解やARIA属性の正しい使い方は複雑です。Claude Codeはアクセシビリティのベストプラクティスを踏まえたコード生成と、既存コードの監査を効率的に行えます。
既存コードのアクセシビリティ監査
> src/components/ 配下の全コンポーネントをアクセシビリティ観点でレビューして。
> WCAG 2.1 AA基準でチェックして、問題と修正案を一覧にして。
Claude Codeは以下のような問題を検出します。
- 画像にalt属性が不足している
- フォーム要素にlabel要素が関連付けられていない
- カラーコントラスト比が不足している
- キーボード操作ができないインタラクティブ要素がある
- ARIA属性が不適切に使われている
アクセシブルなコンポーネントの生成
モーダルダイアログ
> アクセシブルなモーダルダイアログを作成して。
> フォーカストラップ、ESCキーで閉じる、背景のスクロールロックを実装して。
import { useEffect, useRef, useCallback } from 'react';
interface ModalProps {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
}
function Modal({ isOpen, onClose, title, children }: ModalProps) {
const modalRef = useRef<HTMLDivElement>(null);
const previousFocus = useRef<HTMLElement | null>(null);
// フォーカストラップ
const trapFocus = useCallback((e: KeyboardEvent) => {
if (!modalRef.current) return;
const focusable = modalRef.current.querySelectorAll<HTMLElement>(
'a[href], button:not([disabled]), input:not([disabled]), select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
if (e.key === 'Escape') onClose();
}, [onClose]);
useEffect(() => {
if (isOpen) {
previousFocus.current = document.activeElement as HTMLElement;
document.body.style.overflow = 'hidden';
document.addEventListener('keydown', trapFocus);
// モーダル内の最初のフォーカス可能要素にフォーカス
setTimeout(() => {
modalRef.current?.querySelector<HTMLElement>('[autofocus], button')?.focus();
}, 0);
}
return () => {
document.body.style.overflow = '';
document.removeEventListener('keydown', trapFocus);
previousFocus.current?.focus();
};
}, [isOpen, trapFocus]);
if (!isOpen) return null;
return (
<div
className="fixed inset-0 z-50 flex items-center justify-center"
role="presentation"
>
<div
className="fixed inset-0 bg-black/50"
aria-hidden="true"
onClick={onClose}
/>
<div
ref={modalRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
className="relative z-10 mx-4 w-full max-w-lg rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800"
>
<h2 id="modal-title" className="text-xl font-bold mb-4">
{title}
</h2>
{children}
<button
onClick={onClose}
aria-label="閉じる"
className="absolute right-4 top-4 rounded-full p-1 hover:bg-gray-100 dark:hover:bg-gray-700"
>
<svg aria-hidden="true" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" />
</svg>
</button>
</div>
</div>
);
}
アクセシブルなドロップダウンメニュー
function DropdownMenu({ label, items }: { label: string; items: MenuItem[] }) {
const [isOpen, setIsOpen] = useState(false);
const [activeIndex, setActiveIndex] = useState(-1);
const menuRef = useRef<HTMLUListElement>(null);
const handleKeyDown = (e: React.KeyboardEvent) => {
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
setActiveIndex(prev => Math.min(prev + 1, items.length - 1));
break;
case 'ArrowUp':
e.preventDefault();
setActiveIndex(prev => Math.max(prev - 1, 0));
break;
case 'Enter':
case ' ':
e.preventDefault();
if (activeIndex >= 0) items[activeIndex].onClick();
setIsOpen(false);
break;
case 'Escape':
setIsOpen(false);
break;
}
};
return (
<div className="relative" onKeyDown={handleKeyDown}>
<button
aria-haspopup="true"
aria-expanded={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
{label}
</button>
{isOpen && (
<ul ref={menuRef} role="menu" className="absolute mt-1 rounded-md border bg-white shadow-lg dark:bg-gray-800">
{items.map((item, i) => (
<li
key={i}
role="menuitem"
tabIndex={-1}
className={`cursor-pointer px-4 py-2 ${i === activeIndex ? 'bg-blue-100 dark:bg-blue-900' : ''}`}
onClick={item.onClick}
>
{item.label}
</li>
))}
</ul>
)}
</div>
);
}
フォームのアクセシビリティ
> お問い合わせフォームをアクセシブルに作り直して。
> バリデーションエラーもスクリーンリーダーに伝わるようにして。
function ContactForm() {
const [errors, setErrors] = useState<Record<string, string>>({});
return (
<form aria-label="お問い合わせフォーム" noValidate>
<div className="mb-4">
<label htmlFor="name" className="block font-medium mb-1">
お名前 <span aria-label="必須">*</span>
</label>
<input
id="name"
type="text"
required
aria-required="true"
aria-invalid={!!errors.name}
aria-describedby={errors.name ? 'name-error' : undefined}
className="w-full rounded border p-2"
/>
{errors.name && (
<p id="name-error" role="alert" className="mt-1 text-sm text-red-600">
{errors.name}
</p>
)}
</div>
<div className="mb-4">
<label htmlFor="email" className="block font-medium mb-1">
メールアドレス <span aria-label="必須">*</span>
</label>
<input
id="email"
type="email"
required
aria-required="true"
aria-invalid={!!errors.email}
aria-describedby="email-hint email-error"
className="w-full rounded border p-2"
/>
<p id="email-hint" className="mt-1 text-xs text-gray-500">
例: [email protected]
</p>
{errors.email && (
<p id="email-error" role="alert" className="mt-1 text-sm text-red-600">
{errors.email}
</p>
)}
</div>
<button type="submit" className="rounded bg-blue-600 px-4 py-2 text-white">
送信
</button>
</form>
);
}
自動テストの導入
> jest-axeを使ったアクセシビリティテストを追加して。
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
describe('Modal accessibility', () => {
it('should have no accessibility violations', async () => {
const { container } = render(
<Modal isOpen={true} onClose={() => {}} title="Test Modal">
<p>Content</p>
</Modal>
);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});
まとめ
Claude Codeを使えば、WCAG基準に沿ったアクセシブルなコンポーネントの生成から既存コードの監査まで効率的に行えます。フック機能でアクセシビリティテストを自動実行する設定も有効です。詳しくはフック機能ガイドを参照してください。日々の開発フローに組み込むコツは生産性を3倍にするTipsで紹介しています。
Claude Codeの詳細はAnthropic公式ドキュメントをご覧ください。WCAGガイドラインの詳細はW3C WCAG 2.1を参照してください。
#Claude Code
#アクセシビリティ
#WCAG
#a11y
#React
関連記事
Tips & Tricks
Tips & Tricks
Claude CodeでCanvas/WebGL開発を効率化する方法
Claude Codeを活用してCanvas 2D描画・WebGLシェーダー・インタラクティブなグラフィックスを効率的に実装する方法を解説します。
Tips & Tricks
Tips & Tricks
Claude CodeでMarkdown処理・変換を実装する方法
Claude Codeを使ってMarkdownの解析・変換・拡張を効率的に実装。remarkプラグイン、カスタムコンポーネント、目次生成の実践コード付き。
Tips & Tricks
Tips & Tricks
Claude CodeでAIペアプログラミングを実践する方法
Claude Codeを使ったAIペアプログラミングの実践的な手法を解説。効果的なプロンプト、ワークフロー、チーム開発での活用法まで紹介します。