@kood/claude-code 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +21 -243
- package/package.json +1 -1
- package/templates/hono/CLAUDE.md +10 -6
- package/templates/hono/docs/deployment/index.md +5 -0
- package/templates/hono/docs/library/hono/index.md +6 -0
- package/templates/hono/docs/library/prisma/index.md +3 -0
- package/templates/npx/CLAUDE.md +8 -2
- package/templates/tanstack-start/CLAUDE.md +103 -255
- package/templates/tanstack-start/docs/deployment/cloudflare.md +37 -424
- package/templates/tanstack-start/docs/deployment/index.md +57 -286
- package/templates/tanstack-start/docs/deployment/nitro.md +36 -318
- package/templates/tanstack-start/docs/deployment/railway.md +40 -409
- package/templates/tanstack-start/docs/deployment/vercel.md +43 -465
- package/templates/tanstack-start/docs/design/accessibility.md +56 -326
- package/templates/tanstack-start/docs/design/color.md +37 -179
- package/templates/tanstack-start/docs/design/components.md +77 -311
- package/templates/tanstack-start/docs/design/index.md +24 -87
- package/templates/tanstack-start/docs/design/safe-area.md +51 -250
- package/templates/tanstack-start/docs/design/spacing.md +57 -276
- package/templates/tanstack-start/docs/design/tailwind-setup.md +45 -359
- package/templates/tanstack-start/docs/design/typography.md +40 -284
- package/templates/tanstack-start/docs/library/better-auth/2fa.md +27 -115
- package/templates/tanstack-start/docs/library/better-auth/advanced.md +22 -105
- package/templates/tanstack-start/docs/library/better-auth/index.md +17 -66
- package/templates/tanstack-start/docs/library/better-auth/plugins.md +11 -88
- package/templates/tanstack-start/docs/library/better-auth/session.md +12 -92
- package/templates/tanstack-start/docs/library/better-auth/setup.md +9 -91
- package/templates/tanstack-start/docs/library/prisma/cloudflare-d1.md +30 -358
- package/templates/tanstack-start/docs/library/prisma/config.md +27 -327
- package/templates/tanstack-start/docs/library/prisma/crud.md +46 -174
- package/templates/tanstack-start/docs/library/prisma/index.md +23 -113
- package/templates/tanstack-start/docs/library/prisma/relations.md +31 -153
- package/templates/tanstack-start/docs/library/prisma/schema.md +40 -217
- package/templates/tanstack-start/docs/library/prisma/setup.md +12 -112
- package/templates/tanstack-start/docs/library/prisma/transactions.md +20 -110
- package/templates/tanstack-start/docs/library/tanstack-query/index.md +12 -99
- package/templates/tanstack-start/docs/library/tanstack-query/invalidation.md +28 -107
- package/templates/tanstack-start/docs/library/tanstack-query/optimistic-updates.md +44 -146
- package/templates/tanstack-start/docs/library/tanstack-query/setup.md +11 -70
- package/templates/tanstack-start/docs/library/tanstack-query/use-mutation.md +33 -127
- package/templates/tanstack-start/docs/library/tanstack-query/use-query.md +49 -149
- package/templates/tanstack-start/docs/library/tanstack-start/auth-patterns.md +19 -112
- package/templates/tanstack-start/docs/library/tanstack-start/index.md +33 -80
- package/templates/tanstack-start/docs/library/tanstack-start/middleware.md +28 -106
- package/templates/tanstack-start/docs/library/tanstack-start/routing.md +21 -118
- package/templates/tanstack-start/docs/library/tanstack-start/server-functions.md +34 -246
- package/templates/tanstack-start/docs/library/tanstack-start/setup.md +6 -39
- package/templates/tanstack-start/docs/library/zod/basic-types.md +33 -145
- package/templates/tanstack-start/docs/library/zod/complex-types.md +32 -156
- package/templates/tanstack-start/docs/library/zod/index.md +22 -150
- package/templates/tanstack-start/docs/library/zod/transforms.md +20 -129
- package/templates/tanstack-start/docs/library/zod/validation.md +39 -155
- package/templates/hono/docs/commands/git.md +0 -145
- package/templates/hono/docs/mcp/context7.md +0 -106
- package/templates/hono/docs/mcp/index.md +0 -176
- package/templates/hono/docs/mcp/sequential-thinking.md +0 -101
- package/templates/hono/docs/mcp/serena.md +0 -269
- package/templates/hono/docs/mcp/sgrep.md +0 -105
- package/templates/hono/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/hono/docs/skills/gemini-review/references/checklists.md +0 -136
- package/templates/hono/docs/skills/gemini-review/references/prompt-templates.md +0 -303
- package/templates/npx/docs/commands/git.md +0 -145
- package/templates/npx/docs/mcp/index.md +0 -60
- package/templates/npx/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/npx/docs/skills/gemini-review/references/checklists.md +0 -134
- package/templates/npx/docs/skills/gemini-review/references/prompt-templates.md +0 -301
- package/templates/tanstack-start/docs/commands/git.md +0 -145
- package/templates/tanstack-start/docs/mcp/context7.md +0 -204
- package/templates/tanstack-start/docs/mcp/index.md +0 -177
- package/templates/tanstack-start/docs/mcp/sequential-thinking.md +0 -180
- package/templates/tanstack-start/docs/mcp/serena.md +0 -269
- package/templates/tanstack-start/docs/mcp/sgrep.md +0 -174
- package/templates/tanstack-start/docs/skills/gemini-review/SKILL.md +0 -220
- package/templates/tanstack-start/docs/skills/gemini-review/references/checklists.md +0 -144
- package/templates/tanstack-start/docs/skills/gemini-review/references/prompt-templates.md +0 -292
|
@@ -1,337 +1,128 @@
|
|
|
1
1
|
# 접근성 (Accessibility)
|
|
2
2
|
|
|
3
|
-
> **상위 문서**: [UI/UX 디자인 가이드](./index.md)
|
|
4
|
-
|
|
5
|
-
접근성(A11y)은 장애가 있는 사용자도 웹사이트를 사용할 수 있게 만드는 것입니다. 이는 윤리적 의무이자 법적 요구사항이기도 합니다.
|
|
6
|
-
|
|
7
|
-
## 왜 접근성이 중요한가?
|
|
8
|
-
|
|
9
|
-
### 접근성이 필요한 사용자
|
|
10
|
-
|
|
11
|
-
| 유형 | 예시 | 필요한 지원 |
|
|
12
|
-
|------|------|------------|
|
|
13
|
-
| **시각 장애** | 전맹, 저시력, 색맹 | 스크린 리더, 고대비, 큰 텍스트 |
|
|
14
|
-
| **청각 장애** | 난청, 전농 | 자막, 시각적 알림 |
|
|
15
|
-
| **운동 장애** | 손떨림, 마비 | 키보드 네비게이션, 큰 클릭 영역 |
|
|
16
|
-
| **인지 장애** | 읽기 장애, 집중력 장애 | 단순한 언어, 명확한 구조 |
|
|
17
|
-
|
|
18
|
-
### 접근성의 이점
|
|
19
|
-
|
|
20
|
-
- 법적 준수 (ADA, 장애인차별금지법)
|
|
21
|
-
- 더 넓은 사용자층
|
|
22
|
-
- SEO 개선 (검색 엔진도 접근성 기준 활용)
|
|
23
|
-
- 전반적인 UX 향상 (모든 사용자에게 도움)
|
|
24
|
-
|
|
25
3
|
## WCAG 기준
|
|
26
4
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Level A - 최소 기준 (필수)
|
|
33
|
-
Level AA - 권장 기준 (일반적 목표)
|
|
34
|
-
Level AAA - 최고 기준 (특수한 경우)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### 4가지 원칙 (POUR)
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
P - Perceivable (인식 가능) 모든 정보를 인식할 수 있어야 함
|
|
41
|
-
O - Operable (조작 가능) 모든 기능을 조작할 수 있어야 함
|
|
42
|
-
U - Understandable (이해 가능) 내용을 이해할 수 있어야 함
|
|
43
|
-
R - Robust (견고함) 다양한 기술에서 동작해야 함
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## 색상 접근성
|
|
47
|
-
|
|
48
|
-
### 색상 대비 요구사항
|
|
49
|
-
|
|
50
|
-
| 텍스트 유형 | 최소 대비 (AA) | 권장 대비 (AAA) |
|
|
51
|
-
|------------|---------------|----------------|
|
|
52
|
-
| 일반 텍스트 (< 18px) | 4.5:1 | 7:1 |
|
|
53
|
-
| 큰 텍스트 (≥ 18px bold, ≥ 24px) | 3:1 | 4.5:1 |
|
|
54
|
-
| UI 컴포넌트, 그래픽 | 3:1 | - |
|
|
55
|
-
|
|
56
|
-
### 안전한 색상 조합
|
|
5
|
+
| 레벨 | 설명 |
|
|
6
|
+
|------|------|
|
|
7
|
+
| A | 최소 기준 (필수) |
|
|
8
|
+
| AA | 권장 기준 (일반 목표) |
|
|
9
|
+
| AAA | 최고 기준 |
|
|
57
10
|
|
|
58
|
-
|
|
59
|
-
// ✅ 좋은 대비
|
|
60
|
-
<p className="text-gray-900 bg-white">검정 on 흰색 (21:1)</p>
|
|
61
|
-
<p className="text-white bg-blue-700">흰색 on 진한 파랑 (8.6:1)</p>
|
|
62
|
-
<p className="text-gray-700 bg-gray-100">진한 회색 on 밝은 회색 (5.4:1)</p>
|
|
63
|
-
|
|
64
|
-
// ❌ 나쁜 대비
|
|
65
|
-
<p className="text-gray-400 bg-white">밝은 회색 on 흰색 (2.7:1)</p>
|
|
66
|
-
<p className="text-yellow-500 bg-white">노란색 on 흰색 (1.3:1)</p>
|
|
67
|
-
```
|
|
11
|
+
## 색상 대비
|
|
68
12
|
|
|
69
|
-
|
|
13
|
+
| 기준 | 대비 | 대상 |
|
|
14
|
+
|------|------|------|
|
|
15
|
+
| AA | 4.5:1 | 일반 텍스트 |
|
|
16
|
+
| AA (큰 텍스트) | 3:1 | 18px 이상 |
|
|
17
|
+
| AAA | 7:1 | 최고 접근성 |
|
|
70
18
|
|
|
71
19
|
```tsx
|
|
72
|
-
// ❌
|
|
20
|
+
// ❌ 색상만으로 구분
|
|
73
21
|
<span className="text-red-600">오류</span>
|
|
74
|
-
<span className="text-green-600">성공</span>
|
|
75
22
|
|
|
76
|
-
// ✅
|
|
23
|
+
// ✅ 아이콘/텍스트 병행
|
|
77
24
|
<span className="text-red-600 flex items-center gap-1">
|
|
78
25
|
<XCircleIcon className="w-4 h-4" />
|
|
79
26
|
오류가 발생했습니다
|
|
80
27
|
</span>
|
|
81
|
-
<span className="text-green-600 flex items-center gap-1">
|
|
82
|
-
<CheckCircleIcon className="w-4 h-4" />
|
|
83
|
-
성공적으로 저장됨
|
|
84
|
-
</span>
|
|
85
28
|
```
|
|
86
29
|
|
|
87
30
|
## 키보드 접근성
|
|
88
31
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
Tab
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
Escape 모달 닫기, 취소
|
|
97
|
-
Arrow 드롭다운, 슬라이더 조작
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### 포커스 표시
|
|
32
|
+
| 키 | 동작 |
|
|
33
|
+
|-----|------|
|
|
34
|
+
| Tab | 다음 요소 |
|
|
35
|
+
| Shift+Tab | 이전 요소 |
|
|
36
|
+
| Enter | 클릭, 링크 이동 |
|
|
37
|
+
| Space | 체크박스, 버튼 |
|
|
38
|
+
| Escape | 모달 닫기 |
|
|
101
39
|
|
|
102
40
|
```tsx
|
|
103
41
|
// ✅ 명확한 포커스 스타일
|
|
104
|
-
<button className="focus:
|
|
105
|
-
focus:ring-offset-2">
|
|
106
|
-
버튼
|
|
107
|
-
</button>
|
|
108
|
-
|
|
109
|
-
// 입력 필드
|
|
110
|
-
<input className="focus:outline-none focus:ring-2 focus:ring-blue-500
|
|
111
|
-
focus:border-blue-500" />
|
|
42
|
+
<button className="focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
|
|
112
43
|
|
|
113
44
|
// ❌ 포커스 제거 금지
|
|
114
45
|
<button className="outline-none focus:outline-none">
|
|
115
|
-
{/* 접근성 위반! */}
|
|
116
|
-
</button>
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
### 포커스 순서
|
|
120
|
-
|
|
121
|
-
```tsx
|
|
122
|
-
// 논리적인 탭 순서 유지
|
|
123
|
-
<form>
|
|
124
|
-
<input tabIndex={0} /> {/* 1번째 */}
|
|
125
|
-
<input tabIndex={0} /> {/* 2번째 */}
|
|
126
|
-
<button tabIndex={0}>취소</button> {/* 3번째 */}
|
|
127
|
-
<button tabIndex={0}>저장</button> {/* 4번째 */}
|
|
128
|
-
</form>
|
|
129
|
-
|
|
130
|
-
// ❌ tabIndex로 순서 강제 변경 피하기
|
|
131
|
-
<button tabIndex={2}>저장</button>
|
|
132
|
-
<button tabIndex={1}>취소</button>
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### 포커스 트랩 (모달)
|
|
136
|
-
|
|
137
|
-
```tsx
|
|
138
|
-
// 모달이 열리면 포커스가 모달 안에만 머물러야 함
|
|
139
|
-
const Modal = ({ isOpen, onClose }) => {
|
|
140
|
-
const modalRef = useRef(null)
|
|
141
|
-
|
|
142
|
-
useEffect(() => {
|
|
143
|
-
if (isOpen) {
|
|
144
|
-
// 모달 열릴 때 첫 번째 요소에 포커스
|
|
145
|
-
modalRef.current?.querySelector('button')?.focus()
|
|
146
|
-
}
|
|
147
|
-
}, [isOpen])
|
|
148
|
-
|
|
149
|
-
const handleKeyDown = (e) => {
|
|
150
|
-
if (e.key === 'Escape') onClose()
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return (
|
|
154
|
-
<div
|
|
155
|
-
ref={modalRef}
|
|
156
|
-
role="dialog"
|
|
157
|
-
aria-modal="true"
|
|
158
|
-
onKeyDown={handleKeyDown}
|
|
159
|
-
>
|
|
160
|
-
{/* 모달 내용 */}
|
|
161
|
-
</div>
|
|
162
|
-
)
|
|
163
|
-
}
|
|
164
46
|
```
|
|
165
47
|
|
|
166
48
|
## 시맨틱 HTML
|
|
167
49
|
|
|
168
|
-
### 올바른 HTML 요소 사용
|
|
169
|
-
|
|
170
50
|
```tsx
|
|
171
51
|
// ✅ 시맨틱 HTML
|
|
172
52
|
<header>...</header>
|
|
173
53
|
<nav>...</nav>
|
|
174
|
-
<main>
|
|
175
|
-
<article>
|
|
176
|
-
<h1>제목</h1>
|
|
177
|
-
<section>...</section>
|
|
178
|
-
</article>
|
|
179
|
-
</main>
|
|
54
|
+
<main><article><section>...</section></article></main>
|
|
180
55
|
<footer>...</footer>
|
|
181
56
|
|
|
182
57
|
// ❌ div 남용
|
|
183
58
|
<div className="header">...</div>
|
|
184
|
-
<div className="nav">...</div>
|
|
185
|
-
<div className="main">...</div>
|
|
186
59
|
```
|
|
187
60
|
|
|
188
61
|
### 제목 계층
|
|
189
|
-
|
|
190
62
|
```tsx
|
|
191
|
-
// ✅
|
|
63
|
+
// ✅ 순서대로
|
|
192
64
|
<h1>페이지 제목</h1>
|
|
193
65
|
<h2>섹션 1</h2>
|
|
194
|
-
<h3>하위
|
|
195
|
-
<h2>섹션 2</h2>
|
|
196
|
-
|
|
197
|
-
// ❌ 제목 레벨 건너뛰기
|
|
198
|
-
<h1>페이지 제목</h1>
|
|
199
|
-
<h3>섹션</h3> {/* h2를 건너뜀 */}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### 버튼 vs 링크
|
|
66
|
+
<h3>하위 섹션</h3>
|
|
203
67
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
<
|
|
207
|
-
|
|
208
|
-
// 링크: 페이지 이동
|
|
209
|
-
<a href="/about">소개 페이지</a>
|
|
210
|
-
|
|
211
|
-
// ❌ 잘못된 사용
|
|
212
|
-
<div onClick={handleSave}>저장</div> {/* div를 버튼처럼 */}
|
|
213
|
-
<a onClick={handleAction}>클릭</a> {/* 링크를 버튼처럼 */}
|
|
68
|
+
// ❌ 건너뛰기 금지
|
|
69
|
+
<h1>제목</h1>
|
|
70
|
+
<h3>섹션</h3>
|
|
214
71
|
```
|
|
215
72
|
|
|
216
73
|
## ARIA 속성
|
|
217
74
|
|
|
218
|
-
### 필수 ARIA 사용
|
|
219
|
-
|
|
220
75
|
```tsx
|
|
221
76
|
// 레이블 연결
|
|
222
77
|
<label htmlFor="email">이메일</label>
|
|
223
78
|
<input id="email" type="email" />
|
|
224
79
|
|
|
225
|
-
//
|
|
80
|
+
// aria-label
|
|
226
81
|
<input aria-label="검색어 입력" type="search" />
|
|
227
82
|
|
|
228
|
-
// 설명 연결
|
|
229
|
-
<input aria-describedby="email-hint" />
|
|
230
|
-
<p id="email-hint">업무용 이메일을 입력하세요</p>
|
|
231
|
-
|
|
232
83
|
// 에러 상태
|
|
233
84
|
<input aria-invalid="true" aria-describedby="email-error" />
|
|
234
|
-
<p id="email-error" role="alert"
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### 상태 알림
|
|
238
|
-
|
|
239
|
-
```tsx
|
|
240
|
-
// 동적 콘텐츠 변경 알림
|
|
241
|
-
<div aria-live="polite">
|
|
242
|
-
{/* 변경되면 스크린 리더가 읽어줌 */}
|
|
243
|
-
{message}
|
|
244
|
-
</div>
|
|
245
|
-
|
|
246
|
-
// 긴급 알림
|
|
247
|
-
<div aria-live="assertive" role="alert">
|
|
248
|
-
{errorMessage}
|
|
249
|
-
</div>
|
|
250
|
-
```
|
|
85
|
+
<p id="email-error" role="alert">오류 메시지</p>
|
|
251
86
|
|
|
252
|
-
|
|
87
|
+
// 동적 알림
|
|
88
|
+
<div aria-live="polite">{message}</div>
|
|
253
89
|
|
|
254
|
-
|
|
255
|
-
// 시각적으로만 숨김 (스크린 리더는 읽음)
|
|
90
|
+
// 숨김 처리 (스크린 리더만)
|
|
256
91
|
<span className="sr-only">메뉴 열기</span>
|
|
257
92
|
|
|
258
|
-
//
|
|
259
|
-
.sr-only {
|
|
260
|
-
position: absolute;
|
|
261
|
-
width: 1px;
|
|
262
|
-
height: 1px;
|
|
263
|
-
padding: 0;
|
|
264
|
-
margin: -1px;
|
|
265
|
-
overflow: hidden;
|
|
266
|
-
clip: rect(0, 0, 0, 0);
|
|
267
|
-
border: 0;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// 스크린 리더에서도 숨김
|
|
93
|
+
// 장식용 숨김
|
|
271
94
|
<div aria-hidden="true">장식용 아이콘</div>
|
|
272
95
|
```
|
|
273
96
|
|
|
274
97
|
## 폼 접근성
|
|
275
98
|
|
|
276
|
-
### 레이블 필수
|
|
277
|
-
|
|
278
|
-
```tsx
|
|
279
|
-
// ✅ 명시적 레이블
|
|
280
|
-
<label htmlFor="name">이름</label>
|
|
281
|
-
<input id="name" type="text" />
|
|
282
|
-
|
|
283
|
-
// ✅ 암묵적 레이블
|
|
284
|
-
<label>
|
|
285
|
-
이름
|
|
286
|
-
<input type="text" />
|
|
287
|
-
</label>
|
|
288
|
-
|
|
289
|
-
// ✅ aria-label (시각적 레이블 없을 때)
|
|
290
|
-
<input type="search" aria-label="사이트 검색" />
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### 에러 처리
|
|
294
|
-
|
|
295
99
|
```tsx
|
|
296
100
|
<div>
|
|
297
|
-
<label htmlFor="email"
|
|
101
|
+
<label htmlFor="email">
|
|
102
|
+
이메일 <span className="text-red-600" aria-hidden="true">*</span>
|
|
103
|
+
<span className="sr-only">(필수)</span>
|
|
104
|
+
</label>
|
|
298
105
|
<input
|
|
299
106
|
id="email"
|
|
300
|
-
|
|
107
|
+
required
|
|
108
|
+
aria-required="true"
|
|
301
109
|
aria-invalid={hasError}
|
|
302
110
|
aria-describedby={hasError ? "email-error" : undefined}
|
|
303
|
-
className={hasError ? "border-red-500" : "border-gray-300"}
|
|
304
111
|
/>
|
|
305
|
-
{hasError &&
|
|
306
|
-
<p id="email-error" className="text-red-600 text-sm mt-1" role="alert">
|
|
307
|
-
올바른 이메일 형식을 입력하세요
|
|
308
|
-
</p>
|
|
309
|
-
)}
|
|
112
|
+
{hasError && <p id="email-error" role="alert">오류 메시지</p>}
|
|
310
113
|
</div>
|
|
311
114
|
```
|
|
312
115
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
```tsx
|
|
316
|
-
<label htmlFor="email">
|
|
317
|
-
이메일 <span className="text-red-600" aria-hidden="true">*</span>
|
|
318
|
-
<span className="sr-only">(필수)</span>
|
|
319
|
-
</label>
|
|
320
|
-
<input id="email" required aria-required="true" />
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
## 이미지 접근성
|
|
324
|
-
|
|
325
|
-
### 대체 텍스트 (alt)
|
|
116
|
+
## 이미지 대체 텍스트
|
|
326
117
|
|
|
327
118
|
```tsx
|
|
328
|
-
// 정보 전달 이미지
|
|
329
|
-
<img src="chart.png" alt="2024년
|
|
119
|
+
// 정보 전달 이미지
|
|
120
|
+
<img src="chart.png" alt="2024년 매출: 1월 100만원에서 12월 500만원으로 성장" />
|
|
330
121
|
|
|
331
|
-
// 장식용
|
|
122
|
+
// 장식용
|
|
332
123
|
<img src="decorative.png" alt="" />
|
|
333
124
|
|
|
334
|
-
// 아이콘 버튼
|
|
125
|
+
// 아이콘 버튼
|
|
335
126
|
<button aria-label="닫기">
|
|
336
127
|
<img src="close.svg" alt="" />
|
|
337
128
|
</button>
|
|
@@ -339,95 +130,34 @@ const Modal = ({ isOpen, onClose }) => {
|
|
|
339
130
|
|
|
340
131
|
## 터치 접근성
|
|
341
132
|
|
|
342
|
-
### 최소 터치 영역
|
|
343
|
-
|
|
344
133
|
```tsx
|
|
345
|
-
// 최소 44x44px
|
|
134
|
+
// 최소 44x44px
|
|
346
135
|
<button className="min-w-[44px] min-h-[44px] p-2">
|
|
347
136
|
<Icon className="w-6 h-6" />
|
|
348
137
|
</button>
|
|
349
138
|
|
|
350
|
-
//
|
|
351
|
-
<label className="flex items-center gap-2 p-2 -m-2 cursor-pointer">
|
|
352
|
-
<input type="checkbox" className="w-4 h-4" />
|
|
353
|
-
<span>동의합니다</span>
|
|
354
|
-
</label>
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### 터치 타겟 간격
|
|
358
|
-
|
|
359
|
-
```tsx
|
|
360
|
-
// 터치 타겟 사이 최소 8px 간격
|
|
139
|
+
// 터치 타겟 간격 8px 이상
|
|
361
140
|
<div className="flex gap-2">
|
|
362
|
-
<button className="p-3">버튼 1</button>
|
|
363
|
-
<button className="p-3">버튼 2</button>
|
|
364
|
-
</div>
|
|
365
141
|
```
|
|
366
142
|
|
|
367
|
-
##
|
|
368
|
-
|
|
369
|
-
### 자동화 도구
|
|
143
|
+
## 테스트 도구
|
|
370
144
|
|
|
371
145
|
| 도구 | 용도 |
|
|
372
146
|
|------|------|
|
|
373
|
-
|
|
|
374
|
-
|
|
|
375
|
-
|
|
|
376
|
-
| **eslint-plugin-jsx-a11y** | 코드 레벨 검사 |
|
|
377
|
-
|
|
378
|
-
### 수동 테스트
|
|
379
|
-
|
|
380
|
-
```
|
|
381
|
-
1. 키보드만으로 모든 기능 사용 가능한가?
|
|
382
|
-
2. Tab 순서가 논리적인가?
|
|
383
|
-
3. 포커스 표시가 명확한가?
|
|
384
|
-
4. 스크린 리더로 내용을 이해할 수 있는가?
|
|
385
|
-
5. 200% 확대해도 사용 가능한가?
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### ESLint 설정
|
|
389
|
-
|
|
390
|
-
```bash
|
|
391
|
-
yarn add -D eslint-plugin-jsx-a11y
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
```js
|
|
395
|
-
// eslint.config.js
|
|
396
|
-
import jsxA11y from 'eslint-plugin-jsx-a11y'
|
|
397
|
-
|
|
398
|
-
export default [
|
|
399
|
-
{
|
|
400
|
-
plugins: { 'jsx-a11y': jsxA11y },
|
|
401
|
-
rules: {
|
|
402
|
-
'jsx-a11y/alt-text': 'error',
|
|
403
|
-
'jsx-a11y/anchor-has-content': 'error',
|
|
404
|
-
'jsx-a11y/click-events-have-key-events': 'error',
|
|
405
|
-
'jsx-a11y/no-noninteractive-element-interactions': 'error',
|
|
406
|
-
},
|
|
407
|
-
},
|
|
408
|
-
]
|
|
409
|
-
```
|
|
147
|
+
| axe DevTools | 브라우저 확장 |
|
|
148
|
+
| Lighthouse | Chrome 내장 |
|
|
149
|
+
| eslint-plugin-jsx-a11y | 코드 레벨 검사 |
|
|
410
150
|
|
|
411
151
|
## 체크리스트
|
|
412
152
|
|
|
413
153
|
### 필수 (Level A)
|
|
414
|
-
|
|
415
|
-
- [ ]
|
|
416
|
-
- [ ]
|
|
417
|
-
- [ ] 키보드로 모든 기능 접근 가능
|
|
154
|
+
- [ ] 모든 이미지에 alt
|
|
155
|
+
- [ ] 폼 요소에 레이블
|
|
156
|
+
- [ ] 키보드 접근 가능
|
|
418
157
|
- [ ] 포커스 표시 유지
|
|
419
|
-
- [ ] 색상만으로 정보
|
|
158
|
+
- [ ] 색상만으로 정보 전달 금지
|
|
420
159
|
|
|
421
160
|
### 권장 (Level AA)
|
|
422
|
-
|
|
423
|
-
- [ ] 텍스트 대비 4.5:1 이상
|
|
424
|
-
- [ ] 페이지 제목 명확
|
|
161
|
+
- [ ] 대비 4.5:1 이상
|
|
425
162
|
- [ ] 제목 계층 논리적
|
|
426
|
-
- [ ] 에러 메시지 명확
|
|
427
163
|
- [ ] 200% 확대 시 가로 스크롤 없음
|
|
428
|
-
|
|
429
|
-
### 최고 수준 (Level AAA)
|
|
430
|
-
|
|
431
|
-
- [ ] 텍스트 대비 7:1 이상
|
|
432
|
-
- [ ] 읽기 수준 고려
|
|
433
|
-
- [ ] 약어 설명 제공
|