メインコンテンツまでスキップ

Cursor での Web アクセシビリティ (a11y):WCAG、ARIA、axe-core

Web Accessibility in Cursor

Web アクセシビリティ (a11y) は、障害のある人を含むすべての人がアプリケーションを知覚、理解、操作できることを保証します。AI駆動の IDE エディタである Cursor は、ARIA 準拠のマークアップの生成、自動監査の実行、コードベース全体での WCAG 修正の適用により、アクセシブルな開発を加速します。このガイドは、ARIA ラベル、WCAG コンプライアンス、axe-core テスト、AI支援の a11y 修正、スクリーンリーダーテストをカバーします。

前提条件​

始める前に、以下のものを用意してください:

  • IDE エディタとしての Cursor(最新バージョンを推奨)
  • Web プロジェクト(React、Vue、またはプレーンな HTML と JavaScript)
  • テストツール用の Node.js 18 以上
  • HTML セマンティクスに関する基本的な知識
ヒント

アクセシビリティは、後から改修するより早期に組み込む方がはるかに低コストです。最初から Cursor でプロジェクトを開き、コンポーネントを書く際に AI に監査させましょう。開発中に欠落したラベルを修正するのは数秒です。同じ問題を本番アプリ全体で修正するには数週間かかります。

WCAG コンプライアンスの理解​

Web Content Accessibility Guidelines (WCAG) は、Web コンテンツをアクセシブルにする方法を定義します。これらは4つの原則を中心に編成されており、しばしば POUR と呼ばれます:

  • Perceivable(知覚可能):情報とユーザーインターフェースコンポーネントは、ユーザーが知覚できる方法で提示されなければなりません。
  • Operable(操作可能):ユーザーインターフェースコンポーネントとナビゲーションは操作可能でなければなりません。
  • Understandable(理解可能):ユーザーインターフェースのコンテンツと操作は理解可能でなければなりません。
  • Robust(堅牢):コンテンツは支援技術によって解釈できるほど堅牢でなければなりません。

WCAG は3つの適合レベルを定義しています:A(最低限)、AA(ほとんどの組織の法的目標)、AAA(拡張)。Cursor に特定の機能の AA レベル要件の要約を依頼します:

List the WCAG 2.2 Level AA success criteria that apply to a multi-step
checkout form with date pickers and credit-card inputs.

Cursor は関連する基準を返し、具体的なコード提案でそれぞれを満たす方法を説明します。

セマンティック HTML の基礎​

セマンティック HTML はアクセシビリティの基盤です。適切な要素は ARIA の必要性を完全に排除することがよくあります。Cursor は正しいマークアップの選択と生成を支援します。

セマンティックコンポーネントの生成​

Cursor にアクセシビリティを組み込んだナビゲーションバーの構築を依頼します:

Create a responsive site navigation in React that:
- Uses a <nav> landmark with an accessible name
- Has a skip link at the top of the page
- Toggles a mobile menu with aria-expanded and aria-controls
- Closes on Escape and returns focus to the toggle button
- Supports full keyboard navigation

Cursor はコンポーネントを生成します:

import { useState, useRef, useEffect } from 'react';

export function SiteNav({ links }: { links: { href: string; label: string }[] }) {
const [open, setOpen] = useState(false);
const toggleRef = useRef<HTMLButtonElement>(null);

useEffect(() => {
if (!open) return;
const onKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
setOpen(false);
toggleRef.current?.focus();
}
};
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [open]);

return (
<>
<a href="#main" className="skip-link">Skip to main content</a>
<nav aria-label="Main navigation">
<button
ref={toggleRef}
aria-expanded={open}
aria-controls="mobile-menu"
onClick={() => setOpen((v) => !v)}
>
Menu
</button>
<ul id="mobile-menu" hidden={!open}>
{links.map((link) => (
<li key={link.href}>
<a href={link.href}>{link.label}</a>
</li>
))}
</ul>
</nav>
</>
);
}
ヒント

ARIA の第一のルールは:ネイティブ HTML 要素が存在する場合は ARIA を使用しないこと。本当の <button> は、キーボードフォーカス、Enter と Space のアクティベーション、スクリーンリーダーのセマンティクスを無料で提供します。非インタラクティブ要素に onClick があるのを見たら、いつでも Cursor に「この div ベースのボタンをセマンティックボタンに置き換えて」と依頼してください。

ARIA ラベルとロール​

ネイティブ HTML が必要なセマンティクスを表現できない場合、ARIA がギャップを埋めます。Cursor は正しい ARIA 属性を生成し、一般的な間違いについて警告します。

アクセシブルなフォーム​

フォーム入力にはラベルが必要です。Cursor にアクセシブルなフォームフィールドの生成を依頼します:

Create a reusable FormField component with:
- An associated <label> using htmlFor
- An aria-required attribute when required
- An aria-describedby link to helper and error text
- Error messaging with role="alert"
import { useId } from 'react';

interface FormFieldProps {
label: string;
name: string;
type?: string;
required?: boolean;
error?: string;
helper?: string;
}

export function FormField({ label, name, type = 'text', required, error, helper }: FormFieldProps) {
const fieldId = useId();
const helperId = `${fieldId}-helper`;
const errorId = `${fieldId}-error`;
const describedBy = [helper && helperId, error && errorId].filter(Boolean).join(' ') || undefined;

return (
<div>
<label htmlFor={fieldId}>
{label}
{required && <span aria-hidden="true">*</span>}
</label>
<input
id={fieldId}
name={name}
type={type}
required={required}
aria-required={required}
aria-describedby={describedBy}
aria-invalid={!!error}
/>
{helper && !error && (
<p id={helperId} className="helper-text">{helper}</p>
)}
{error && (
<p id={errorId} role="alert" className="error-text">{error}</p>
)}
</div>
);
}

動的更新用のライブリージョン​

コンテンツが動的に更新される場合(通知、検索結果)、スクリーンリーダーは変更をアナウンスするライブリージョンを必要とします。Cursor はパターンを生成します:

export function SearchResults({ query, results }: { query: string; results: string[] }) {
return (
<div
aria-live="polite"
aria-atomic="true"
role="status"
>
{results.length === 0 ? (
<p>No results for "{query}".</p>
) : (
<p>{results.length} results found for "{query}".</p>
)}
<ul>
{results.map((r) => <li key={r}>{r}</li>)}
</ul>
</div>
);
}
警告

非緊急の更新には aria-live="polite" を使用し、重大なアラートにのみ aria-live="assertive" を使用してください。assertive ライブリージョンの過剰使用は、スクリーンリーダーユーザーを常に中断させ、アプリを使用不能にします。成功メッセージに role="alert" を使用しないでください。即時注意が必要な実際のエラー用に予約してください。

axe-core 自動テスト​

自動化ツールはアクセシビリティ問題の約 30% から 40% を捕捉します。axe-core は業界標準です。Cursor はこれをテストスイートと CI パイプラインに統合できます。

Jest での axe-core のセットアップ​

Cursor に自動アクセシビリティテストの追加を依頼します:

Add axe-core to my React project. Create a Jest test that renders
the SiteNav component with Testing Library and runs axe rules.

Cursor は依存関係をインストールし、テストを記述します:

npm install -D @axe-core/react axe-core jest-axe @testing-library/react @testing-library/jest-dom
// src/components/SiteNav.test.tsx
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import { SiteNav } from './SiteNav';

expect.extend(toHaveNoViolations);

const links = [
{ href: '/', label: 'Home' },
{ href: '/about', label: 'About' },
];

describe('SiteNav', () => {
it('has no accessibility violations when closed', async () => {
const { container } = render(<SiteNav links={links} />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('has no accessibility violations when open', async () => {
const { container, getByText } = render(<SiteNav links={links} />);
getByText('Menu').click();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});

再利用可能な axe テストヘルパー​

Cursor は再利用可能なヘルパーを生成し、すべてのコンポーネントテストを一貫させることができます:

import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

export async function expectAccessible(component: React.ReactElement) {
const { container } = render(component);
const results = await axe(container, {
rules: {
// Disable rules you intentionally handle elsewhere
region: { enabled: false },
},
});
expect(results).toHaveNoViolations();
return results;
}

すべてのコンポーネントテストで使用します:

import { expectAccessible } from '../test/expectAccessible';
import { FormField } from './FormField';

it('is accessible', async () => {
await expectAccessible(<FormField label="Email" name="email" required />);
});
ヒント

パッケージスクリプトでコミット前に完全な axe スイートを実行します。Cursor に「package.json に a11y スクリプトを追加して、すべての axe テストを実行し、違反があれば失敗するようにして」と依頼します。これにより、リグレッションがメインブランチに到達するのを防ぎます。

AI 支援のアクセシビリティ修正​

Cursor の AI はコンポーネントファイル全体をスキャンし、アクセシビリティの問題を見つけ、一回のパスで修正を適用できます。これは最も価値のある a11y ワークフローの一つです。

コンポーネントの監査​

Cursor でコンポーネントを開き、AI チャットでこのプロンプトを実行します:

Audit this component for WCAG 2.2 AA compliance. List every issue
with the line number, explain why it fails, and rewrite the code
with fixes. Add comments explaining each change.

Cursor は以下を報告する可能性があります:

  • 12行目:<div onClick> はキーボードでアクセスできません。<button> に置き換えてください。
  • 20行目:<img> に alt 属性がありません。説明的な alt テキストを追加してください。
  • 28行目:色のコントラストが 3.1:1 で、通常テキストの 4.5:1 の最低値を下回っています。
  • 35行目:モーダルダイアログにフォーカストラップがありません。ユーザーがダイアログの後ろに Tab できます。

Cursor は修正を適用します:

// Before
<div className="card" onClick={openDetails}>
<img src={product.image} />
<h3 style={{ color: '#999' }}>{product.name}</h3>
</div>

// After
<button
className="card"
onClick={openDetails}
aria-label={`View details for ${product.name}`}
>
<img src={product.image} alt={`${product.name} product photo`} />
<h3 style={{ color: '#595959' }}>{product.name}</h3>
</button>

フォーカストラップの生成​

モーダルとダイアログにはフォーカストラップが必要で、キーボードユーザーが Tab でダイアログから脱出できないようにします。Cursor に依頼します:

Add a focus trap to this modal component. Move focus to the first
focusable element on open, trap Tab and Shift+Tab within the dialog,
and return focus to the trigger button on close.
import { useEffect, useRef } from 'react';

const FOCUSABLE = 'a[href], button:not([disabled]), input, textarea, select, [tabindex]:not([tabindex="-1"])';

export function useFocusTrap(active: boolean) {
const containerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (!active || !containerRef.current) return;
const container = containerRef.current;
const elements = container.querySelectorAll<HTMLElement>(FOCUSABLE);
if (elements.length === 0) return;

const first = elements[0];
const last = elements[elements.length - 1];
first.focus();

const onKey = (e: KeyboardEvent) => {
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
};

container.addEventListener('keydown', onKey);
return () => container.removeEventListener('keydown', onKey);
}, [active]);

return containerRef;
}
警告

基盤となる HTML がキーボードでアクセス可能でない場合、フォーカストラップは無意味です。トラップは実際にフォーカス可能な要素のみを管理します。ダイアログに onClick ハンドラーを持つ <div> 要素しか含まれていない場合、フォーカストラップにはフォーカスするものがありません。常にトラップをセマンティックでフォーカス可能な要素とペアにしてください。

スクリーンリーダーテスト​

自動テストは構造的な問題を捕捉できますが、スクリーンリーダーでの手動テストのみが実際の体験を明らかにします。Cursor はテストスクリプトとチェックリストを生成できます。

キーボードのみのテストチェックリスト​

Cursor にキーボードテストチェックリストの生成を依頼します:

Create a keyboard-only testing checklist for a web application
covering navigation, forms, widgets, and dynamic content.

Cursor は以下を生成します:

  1. Tab でトラップなしに論理的な順序でページを移動できる。
  2. スキップリンクが最初のフォーカス可能要素で、メインコンテンツにジャンプする。
  3. すべてのインタラクティブ要素が Tab と Shift+Tab で到達可能。
  4. カスタムウィジェット(タブ、アコーディオン)が矢印キーと Home/End をサポートする。
  5. モーダルがフォーカスをトラップし、閉じる時にトリガーに戻す。
  6. すべてのフォーカス可能要素に視覚的なフォーカスインジケーターがある。
  7. Enter と Space がボタンをアクティベートし、Enter がリンクをアクティベートする。
  8. フォームが送信ボタンで Enter を押すと送信される。

NVDA、JAWS、VoiceOver でのテスト​

Cursor は各スクリーンリーダーのテストノートを生成できます:

Write a quick-start guide for testing my app with NVDA on Windows,
VoiceOver on macOS, and TalkBack on Android. Include the shortcut
to start each reader and how to navigate by heading and landmark.

Cursor は比較表とナビゲーションチートシートを返し、プロジェクトの参照ファイルとして保存できます。

色のコントラストとビジュアルデザイン​

ビジュアルアクセシビリティは WCAG の一部です。低コントラストは最も一般的な失敗の一つです。

AI によるコントラストチェック​

Cursor にデザイントークンの評価を依頼します:

Here are my color tokens in tailwind.config.js. Check every text
and background pair against WCAG AA contrast ratios (4.5:1 for normal
text, 3:1 for large text). Flag any failures and suggest fixes.

Cursor は設定を読み取り、コントラスト比を計算し、失敗を報告します:

// tailwind.config.js tokens that fail AA
// text-gray-400 on bg-white: 2.85:1 — FAIL
// Fix: use text-gray-600 (#4B5563) for 7.21:1

Cursor は設定を更新します:

module.exports = {
theme: {
extend: {
colors: {
'text-secondary': '#4B5563', // was #9CA3AF, now passes AA
},
},
},
};
ヒント

意味を伝えるために色のみに依存しないでください。無効なフィールドの周りの赤い境界線は、スクリーンリーダーにも依存する色覚障害のユーザーには見えません。常に色をテキストまたはアイコンとペアにしてください。Cursor に「色と並んでテキストとアイコンを追加してエラー状態を伝えるようにして」と依頼し、フォーム全体で修正します。

CI でのアクセシビリティ​

自動 a11y チェックは継続的インテグレーションパイプラインに属します。Cursor は設定を生成できます。

a11y カテゴリ付きの Lighthouse CI​

Cursor に Lighthouse CI の追加を依頼します:

Add Lighthouse CI to my GitHub Actions workflow with the
accessibility category enabled and a minimum score of 0.9.
# .github/workflows/lighthouse.yml
name: Lighthouse
on: [pull_request]

jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npm run build
- name: Run Lighthouse
uses: treosh/lighthouse-ci-action@v11
with:
urls: |
http://localhost:3000
uploadArtifacts: true
configPath: .lighthouserc.json
// .lighthouserc.json
{
"ci": {
"assert": {
"assertions": {
"categories:accessibility": ["error", { "minScore": 0.9 }]
}
},
"collect": {
"startServerCommand": "npm run start",
"startServerReadyPattern": "ready",
"numberOfRuns": 3
}
}
}

ベストプラクティスのまとめ​

Cursor でアクセシブルなアプリを構築する際、以下の原則を心に留めてください:

  • セマンティック HTML を優先 し、ネイティブ要素が適合する場合は ARIA よりも。
  • すべての入力にラベルを付ける 本当の <label> または aria-label で。
  • すべてをキーボード操作可能にする;マウスはオプションです。
  • すべてのコンポーネントテストで axe-core を自動化する。
  • 少なくとも1つのスクリーンリーダーで手動テストする。
  • 通常テキストに 4.5:1 のコントラストと視覚的なフォーカスインジケーターを維持する。
  • CI で a11y チェックを実行し、リグレッションが出荷されないようにする。

WCAG の知識と Cursor の AI 駆動の監査およびコード生成を組み合わせることで、すべての人に機能するインクルーシブなインターフェースを構築できます。Cursor にアクセシブルなマークアップとテストのスキャフォールディングを任せ、リリース前に実際のスクリーンリーダーで結果を検証してください。