This is a skill that enforces compliance with the web accessibility standard WCAG 2.2.
2025 Context
WCAG 2.2 was adopted as an ISO standard (ISO/IEC 40500) in October 2023. The European Accessibility Act (EAA) takes effect in June 2025.
Core Principles (POUR)
| Principle | Description | Example |
|---|---|---|
| Perceivable | Perceivable | Alternative text, captions, color contrast |
| Operable | Operable | Keyboard access, sufficient time |
| Understandable | Understandable | Clear language, predictable behavior |
| Robust | Robust | Assistive technology compatibility |
Rules
1. Semantic HTML (Required)
// ❌ BAD: div 남용
<div onClick={handleClick}>버튼</div>
<div class="header">제목</div>
// ✅ GOOD: 시맨틱 태그 사용
<button onClick={handleClick}>버튼</button>
<h1>제목</h1>
2. Image Alternative Text (Required)
// ❌ BAD: alt 누락 또는 의미 없음
<img src="logo.png" />
<img src="chart.png" alt="이미지" />
// ✅ GOOD: 의미 있는 alt
<img src="logo.png" alt="회사명 로고" />
<img src="chart.png" alt="2024년 매출 증가 추이 그래프" />
// ✅ 장식용 이미지는 빈 alt
<img src="decoration.png" alt="" role="presentation" />
3. Keyboard Accessibility (Required)
// ❌ BAD: 키보드 접근 불가
<div onClick={handleClick} style={{ cursor: 'pointer' }}>
클릭
</div>
// ✅ GOOD: 키보드 접근 가능
<button onClick={handleClick}>클릭</button>
// 또는 커스텀 요소 사용 시
<div
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={(e) => e.key === 'Enter' && handleClick()}
>
클릭
</div>
4. Focus Management
// ❌ BAD: 포커스 스타일 제거
button:focus {
outline: none;
}
// ✅ GOOD: 명확한 포커스 표시
button:focus {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
button:focus-visible {
outline: 2px solid #005fcc;
}
5. Color Contrast (WCAG AA Standard)
| Text Size | Minimum Contrast Ratio |
|---|---|
| Normal Text | 4.5:1 |
| Large Text (18pt+, 14pt bold+) | 3:1 |
| UI Components/Graphics | 3:1 |
/* ❌ BAD: 낮은 대비 */
.text {
color: #999; /* 회색 on 흰색 = 2.85:1 */
background: #fff;
}
/* ✅ GOOD: 충분한 대비 */
.text {
color: #595959; /* 4.54:1 */
background: #fff;
}
6. Form Labels (Required)
// ❌ BAD: 레이블 없음
<input type="email" placeholder="이메일" />
// ✅ GOOD: 명시적 레이블
<label htmlFor="email">이메일</label>
<input id="email" type="email" />
// 또는 aria-label 사용
<input type="email" aria-label="이메일 주소" placeholder="이메일" />
7. ARIA Roles and Properties
// 모달 다이얼로그
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<h2 id="modal-title">확인</h2>
...
</div>
// 알림 메시지
<div role="alert" aria-live="polite">
저장되었습니다.
</div>
// 로딩 상태
<button aria-busy={isLoading} disabled={isLoading}>
{isLoading ? '처리 중...' : '제출'}
</button>
8. Skip Links
// 페이지 상단에 추가
<a href="#main-content" className="skip-link">
본문으로 건너뛰기
</a>
// CSS
.skip-link {
position: absolute;
top: -40px;
left: 0;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
WCAG 2.2 New Criteria
2.4.11 Focus Not Obscured (AA)
// ❌ BAD: 고정 헤더가 포커스 요소를 가림
.header { position: fixed; top: 0; }
// ✅ GOOD: scroll-margin으로 여유 공간 확보
:target {
scroll-margin-top: 80px;
}
*:focus {
scroll-margin-top: 80px;
}
2.5.7 Dragging Movements (AA)
// ❌ BAD: 드래그만 지원
<DraggableList onDrag={handleReorder} />
// ✅ GOOD: 드래그 + 버튼 대안 제공
<DraggableList onDrag={handleReorder}>
<button onClick={moveUp}>위로 이동</button>
<button onClick={moveDown}>아래로 이동</button>
</DraggableList>
2.5.8 Target Size (AA)
/* 최소 터치 타겟: 24x24px (AA), 44x44px 권장 */
button, a, input[type="checkbox"] {
min-width: 44px;
min-height: 44px;
}
Testing Tools
Automated Tools
# axe-core (React)
npm install @axe-core/react
# eslint-plugin-jsx-a11y
npm install eslint-plugin-jsx-a11y --save-dev
# Lighthouse CI
npm install -g @lhci/cli
lhci autorun
eslint Configuration
{
"extends": ["plugin:jsx-a11y/recommended"],
"rules": {
"jsx-a11y/alt-text": "error",
"jsx-a11y/anchor-is-valid": "error",
"jsx-a11y/click-events-have-key-events": "error",
"jsx-a11y/no-static-element-interactions": "error"
}
}
Manual Testing Checklist
- All functions usable with keyboard only
- Tab order is logical
- Focus indication is clear
- Content understandable with screen reader
- No content loss when zoomed to 200%
- Information not conveyed by color alone
Workflow
1. When Writing Components
체크포인트:
1. 시맨틱 HTML 사용했는가?
2. 키보드 접근 가능한가?
3. 적절한 ARIA 속성이 있는가?
4. 포커스 스타일이 있는가?
2. During Code Review
접근성 체크:
1. img에 alt 있는가?
2. form에 label 있는가?
3. 색상 대비 충분한가?
4. 터치 타겟 크기 충분한가?
Checklist
- Use semantic HTML tags
- Meaningful alt for all images
- Connect labels to form elements
- Operable with keyboard only
- Clear focus indication
- Color contrast of 4.5:1 or higher
- Touch targets 44x44px or larger
- Provide skip links
- Pass axe/Lighthouse tests