@infomind-ux/infoux-mcp 0.1.0

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.
Files changed (53) hide show
  1. package/README.md +81 -0
  2. package/bin/server.js +331 -0
  3. package/data/contract.md +215 -0
  4. package/data/manifest.json +286 -0
  5. package/data/references/accessibility.md +243 -0
  6. package/data/references/forbidden-patterns.md +462 -0
  7. package/data/references/guide-import.md +93 -0
  8. package/data/references/html-semantics.md +475 -0
  9. package/data/references/krds-components.md +1426 -0
  10. package/data/references/krds-source.md +1125 -0
  11. package/data/references/krds-tokens.md +201 -0
  12. package/data/references/project-profiles.md +99 -0
  13. package/data/references/snippet-template.md +185 -0
  14. package/data/references/tailwind-mapping.md +53 -0
  15. package/data/rules.json +780 -0
  16. package/data/snippets/accordion.md +49 -0
  17. package/data/snippets/alert.md +37 -0
  18. package/data/snippets/badge.md +29 -0
  19. package/data/snippets/boilerplate.md +125 -0
  20. package/data/snippets/breadcrumb.md +26 -0
  21. package/data/snippets/btn.md +63 -0
  22. package/data/snippets/calendar.md +64 -0
  23. package/data/snippets/card.md +50 -0
  24. package/data/snippets/carousel.md +43 -0
  25. package/data/snippets/check-radio.md +55 -0
  26. package/data/snippets/disclosure.md +44 -0
  27. package/data/snippets/file-upload.md +34 -0
  28. package/data/snippets/form.md +68 -0
  29. package/data/snippets/header.md +56 -0
  30. package/data/snippets/list.md +64 -0
  31. package/data/snippets/main-menu.md +49 -0
  32. package/data/snippets/modal.md +54 -0
  33. package/data/snippets/pagination.md +41 -0
  34. package/data/snippets/progress.md +30 -0
  35. package/data/snippets/select.md +34 -0
  36. package/data/snippets/side-panel.md +57 -0
  37. package/data/snippets/spinner.md +24 -0
  38. package/data/snippets/step-indicator.md +38 -0
  39. package/data/snippets/switch.md +32 -0
  40. package/data/snippets/tab.md +52 -0
  41. package/data/snippets/table.md +63 -0
  42. package/data/snippets/tag.md +42 -0
  43. package/data/snippets/toast.md +48 -0
  44. package/data/snippets/tooltip.md +38 -0
  45. package/data/tokens.css +390 -0
  46. package/data/workflows/change-token.md +20 -0
  47. package/data/workflows/create-component.md +71 -0
  48. package/data/workflows/design-form.md +20 -0
  49. package/data/workflows/design-page.md +22 -0
  50. package/data/workflows/design-widget.md +19 -0
  51. package/data/workflows/init-project.md +68 -0
  52. package/data/workflows/review-ui.md +14 -0
  53. package/package.json +34 -0
@@ -0,0 +1,1426 @@
1
+ # KRDS 컴포넌트 카탈로그
2
+
3
+ > 자동 생성됨. 직접 수정 금지.
4
+ > 출처: `src/snippets/*.md`
5
+ > 빌드: c8f34dd
6
+
7
+ 아래 카탈로그에 없는 컴포넌트는 임의 생성 금지. § "카탈로그에 없는 컴포넌트 요구 시" 절차 따름.
8
+
9
+ ---
10
+
11
+ ## 인덱스
12
+
13
+ - **그룹 A — 폼/액션**: [btn](#btn) · [check-radio](#check-radio) · [file-upload](#file-upload) · [form](#form) · [select](#select) · [switch](#switch)
14
+ - **그룹 B — 컨테이너/레이아웃**: [accordion](#accordion) · [card](#card) · [disclosure](#disclosure) · [modal](#modal) · [side-panel](#side-panel) · [tab](#tab)
15
+ - **그룹 C — 내비게이션**: [breadcrumb](#breadcrumb) · [header](#header) · [main-menu](#main-menu) · [pagination](#pagination)
16
+ - **그룹 D — 피드백**: [alert](#alert) · [badge](#badge) · [progress](#progress) · [spinner](#spinner) · [step-indicator](#step-indicator) · [tag](#tag) · [toast](#toast) · [tooltip](#tooltip)
17
+ - **그룹 E — 콘텐츠/표현**: [calendar](#calendar) · [carousel](#carousel) · [list](#list) · [table](#table)
18
+
19
+ ---
20
+
21
+ ## 그룹 A — 폼/액션
22
+
23
+ ##### 버튼 (Button) — KRDS {#btn}
24
+
25
+ #### 기본 마크업
26
+
27
+ ```html
28
+ <button type="button" class="btn btn--primary">버튼 텍스트</button>
29
+ ```
30
+
31
+ #### Variant (KRDS 정의 — 4종)
32
+
33
+ | Variant | 클래스 | 용도 |
34
+ |---------|--------|------|
35
+ | Primary | `.btn--primary` | 메인 CTA (저장, 제출, 확인) |
36
+ | Secondary | `.btn--secondary` | 보조 액션 (primary 톤 옅은 채움 + primary border) |
37
+ | Tertiary | `.btn--tertiary` | 약한 액션 (투명 + gray border) |
38
+ | Text | `.btn--text` | 텍스트 링크형 (배경/border 없음) |
39
+
40
+ #### Size (KRDS 정의 — 5종)
41
+
42
+ | Size | 클래스 | 높이 | padding-x | 사용 권장 |
43
+ |------|--------|------|-----------|----------|
44
+ | xsmall | `.btn--xsmall` | 32px | 10px | 데스크탑 dense UI 한정 |
45
+ | small | `.btn--small` | 40px | 12px | 데스크탑 보조 액션 |
46
+ | medium | (기본 — 클래스 없음) | 48px | 16px | **모바일·기본 권장** |
47
+ | large | `.btn--large` | 56px | 20px | 강조 CTA |
48
+ | xlarge | `.btn--xlarge` | 64px | 24px | 히어로/랜딩 CTA |
49
+
50
+ > **모바일 환경에선 medium(48px) 이상 사용.** xsmall(32) · small(40)은 WCAG 권장 터치 영역(44px)보다 작아 모바일 부적합.
51
+
52
+ #### 조합 예시
53
+
54
+ ```html
55
+ <!-- 기본(medium) -->
56
+ <button type="button" class="btn btn--primary">저장</button>
57
+ <button type="button" class="btn btn--secondary">취소</button>
58
+ <button type="button" class="btn btn--tertiary">더보기</button>
59
+ <button type="button" class="btn btn--text">자세히 보기</button>
60
+
61
+ <!-- 사이즈 조합 -->
62
+ <button type="button" class="btn btn--primary btn--small">작게</button>
63
+ <button type="button" class="btn btn--primary btn--large">크게</button>
64
+
65
+ <!-- 비활성 -->
66
+ <button type="button" class="btn btn--primary" disabled>비활성</button>
67
+
68
+ <!-- Block (full width) -->
69
+ <button type="button" class="btn btn--primary btn--block">제출</button>
70
+ ```
71
+
72
+ #### 접근성 (KRDS + WCAG 2.1 AA)
73
+
74
+ - `<button type="button">` 태그 사용 필수. `<a>` 태그를 버튼 용도로 쓰지 않는다
75
+ - 아이콘만 있는 버튼은 `aria-label` 필수: `<button class="btn" aria-label="메뉴 열기">...</button>`
76
+ - 비활성은 `disabled` 속성 (또는 `aria-disabled="true"`)
77
+ - 포커스 outline은 `reset.css`에서 전역 관리 (4px primary 외곽선) — 컴포넌트에서 제거 금지
78
+ - `prefers-reduced-motion` 대응: 모션 감소 설정 시 transition 자동 비활성 (Phase 6에서 추가)
79
+
80
+ #### 출처
81
+
82
+ - KRDS 버튼 명세: https://www.krds.go.kr/html/site/component/component_summary.html
83
+ - 색상: `--color-button-*`, `--color-text-*`
84
+ - 크기/간격/반경: CSS/Tailwind 직접값
85
+ - CSS: `src/styles/6-components/btn.css`
86
+
87
+
88
+ ---
89
+
90
+ ##### 체크박스 & 라디오 — KRDS Form check {#check-radio}
91
+
92
+ #### 기본 마크업
93
+
94
+ ##### 체크박스
95
+
96
+ ```html
97
+ <label class="check">
98
+ <input type="checkbox" name="agree" value="true">
99
+ <span class="check__box" aria-hidden="true"></span>
100
+ <span class="check__label">개인정보 수집·이용에 동의합니다</span>
101
+ </label>
102
+ ```
103
+
104
+ ##### 라디오
105
+
106
+ ```html
107
+ <fieldset>
108
+ <legend class="form-field__label">결제 수단</legend>
109
+ <label class="radio">
110
+ <input type="radio" name="pay" value="card" checked>
111
+ <span class="radio__box" aria-hidden="true"></span>
112
+ <span class="radio__label">신용카드</span>
113
+ </label>
114
+ <label class="radio">
115
+ <input type="radio" name="pay" value="bank">
116
+ <span class="radio__box" aria-hidden="true"></span>
117
+ <span class="radio__label">계좌이체</span>
118
+ </label>
119
+ </fieldset>
120
+ ```
121
+
122
+ #### 시맨틱 구조
123
+
124
+ - **Root 태그**: `<label class="check">` 또는 `<label class="radio">` (input을 감쌈)
125
+ - **자식**: `<input type="checkbox|radio">` → `<span __box aria-hidden="true">` (시각 박스) → `<span __label>` (텍스트)
126
+ - **그룹**: 라디오는 `<fieldset>` + `<legend>` 필수
127
+ - **필수 ARIA**: 시각 박스에 `aria-hidden="true"` — native input이 정보를 담당
128
+ - 상세: `skill/references/html-semantics.md#check-radio`
129
+
130
+ #### 사양
131
+
132
+ - 박스 크기: 24×24
133
+ - 컨테이너 최소 높이: `--touch-target-min` (44px)
134
+ - native `<input>`은 시각적으로 숨김 (sr-only) — 키보드/스크린리더는 정상 작동
135
+
136
+ #### 접근성
137
+
138
+ - `<label>`로 input + box + 텍스트를 묶어 클릭 영역 전체 확보
139
+ - 라디오 그룹은 `<fieldset><legend>`로 묶기
140
+ - 시각 박스(`__box`)는 `aria-hidden="true"` (스크린리더는 native input만 인식)
141
+
142
+ #### 출처
143
+
144
+ - CSS: `src/styles/6-components/check-radio.css`
145
+
146
+
147
+ ---
148
+
149
+ ##### 파일 업로드 (File Upload) — KRDS {#file-upload}
150
+
151
+ #### 기본 마크업
152
+
153
+ ```html
154
+ <label class="file-upload">
155
+ <input type="file" id="file" accept="image/*">
156
+ <span class="file-upload__trigger">파일 선택</span>
157
+ <span class="file-upload__filename" aria-live="polite">선택된 파일 없음</span>
158
+ </label>
159
+ ```
160
+
161
+ > 파일명 표시는 JS로 갱신 — `input.files[0].name`을 `.file-upload__filename`에 채워 넣는다.
162
+
163
+ #### 다중 선택
164
+
165
+ ```html
166
+ <label class="file-upload">
167
+ <input type="file" id="docs" multiple accept=".pdf,.doc,.docx">
168
+ <span class="file-upload__trigger">문서 선택</span>
169
+ <span class="file-upload__filename">선택된 파일 없음</span>
170
+ </label>
171
+ ```
172
+
173
+ #### 접근성
174
+
175
+ - `<label>`로 input과 트리거를 묶어 키보드 포커스 시 트리거 외곽선 노출
176
+ - `aria-live="polite"`로 파일명 변경을 스크린리더에 안내
177
+ - `accept` 속성으로 허용 파일 타입 명시 (브라우저 필터링 + 사용자 안내)
178
+ - 업로드 진행률은 별도 `<progress>` 또는 토스트로 표시
179
+
180
+ #### 출처
181
+
182
+ - CSS: `src/styles/6-components/file-upload.css`
183
+
184
+
185
+ ---
186
+
187
+ ##### 폼 필드 (Form Field) — KRDS {#form}
188
+
189
+ KRDS 입력폼 구성: **레이블 → 보조설명 → 입력박스 → 시스템메시지** (요소 간 8px gap)
190
+
191
+ #### 기본 마크업 (텍스트 입력)
192
+
193
+ ```html
194
+ <div class="form-field">
195
+ <label for="name" class="form-field__label">
196
+ 이름<span class="form-field__required" aria-label="필수">*</span>
197
+ </label>
198
+ <p class="form-field__hint">사업자등록증에 기재된 대표자명</p>
199
+ <input type="text" id="name" class="input" placeholder="홍길동" required>
200
+ <p class="form-field__message">최대 20자까지 입력 가능합니다</p>
201
+ </div>
202
+ ```
203
+
204
+ #### 에러 상태
205
+
206
+ ```html
207
+ <div class="form-field">
208
+ <label for="email" class="form-field__label">이메일</label>
209
+ <input type="email" id="email" class="input input--error" aria-invalid="true" aria-describedby="email-error" value="invalid">
210
+ <p id="email-error" class="form-field__message form-field__message--error">올바른 이메일 형식이 아닙니다</p>
211
+ </div>
212
+ ```
213
+
214
+ #### Textarea
215
+
216
+ ```html
217
+ <div class="form-field">
218
+ <label for="memo" class="form-field__label">메모</label>
219
+ <textarea id="memo" class="textarea" rows="4" placeholder="내용을 입력하세요"></textarea>
220
+ </div>
221
+ ```
222
+
223
+ #### Input 사이즈
224
+
225
+ | Size | 클래스 | 높이 |
226
+ |------|--------|------|
227
+ | small | `.input--small` | 40px |
228
+ | medium | (기본) | 48px |
229
+ | large | `.input--large` | 56px |
230
+
231
+ #### Input type
232
+
233
+ `type="text|email|password|number|tel|url|search|date|time|datetime-local"` 모두 동일 스타일 적용.
234
+
235
+ #### 상태
236
+
237
+ - 기본 — `border: 1px solid var(--color-input-border)`
238
+ - focus — `border-color: var(--color-input-border-active)` (primary)
239
+ - disabled — 회색 배경 + disabled 텍스트, `cursor: not-allowed`
240
+ - read-only — `:read-only`로 자동 처리
241
+ - error — `.input--error` 또는 `aria-invalid="true"`
242
+
243
+ #### 접근성
244
+
245
+ - `<label for="id">` + `<input id="id">` 연결 필수
246
+ - 필수 항목은 `required` 속성 + 시각 표시(`*`)
247
+ - 에러는 `aria-invalid="true"` + `aria-describedby="에러메시지id"`
248
+ - 보조설명은 `aria-describedby`로 연결 권장
249
+ - placeholder만으로 레이블 대체 금지
250
+
251
+ #### 출처
252
+
253
+ - KRDS 입력 명세: https://www.krds.go.kr/html/site/component/component_summary.html
254
+ - CSS: `src/styles/6-components/form.css`
255
+
256
+
257
+ ---
258
+
259
+ ##### 셀렉트 (Select) — KRDS {#select}
260
+
261
+ native `<select>` 기반. input과 동일 사이즈/패딩 토큰.
262
+
263
+ #### 기본 마크업
264
+
265
+ ```html
266
+ <div class="form-field">
267
+ <label for="category" class="form-field__label">카테고리</label>
268
+ <select id="category" class="select">
269
+ <option value="">선택하세요</option>
270
+ <option value="a">옵션 A</option>
271
+ <option value="b">옵션 B</option>
272
+ </select>
273
+ </div>
274
+ ```
275
+
276
+ #### 사이즈
277
+
278
+ - `.select--small` (40px) / 기본 medium (48px) / `.select--large` (56px)
279
+
280
+ #### 상태
281
+
282
+ - 기본 / hover / focus / disabled / error (`.select--error` 또는 `aria-invalid="true"`)
283
+
284
+ #### 접근성
285
+
286
+ - 첫 옵션은 `<option value="">선택하세요</option>` 같은 placeholder 권장
287
+ - `<label for>` + `<select id>` 연결 필수
288
+ - 옵션 텍스트는 명확하고 간결하게
289
+
290
+ #### 출처
291
+
292
+ - CSS: `src/styles/6-components/select.css`
293
+
294
+
295
+ ---
296
+
297
+ ##### 토글 스위치 (Switch) — KRDS {#switch}
298
+
299
+ #### 기본 마크업
300
+
301
+ ```html
302
+ <label class="switch">
303
+ <input type="checkbox" name="notify" role="switch">
304
+ <span class="switch__track" aria-hidden="true"></span>
305
+ <span class="switch__label">알림 받기</span>
306
+ </label>
307
+ ```
308
+
309
+ #### 사양
310
+
311
+ - 트랙: 44×24, 핸들: 20×20
312
+ - 컨테이너 최소 높이: `--touch-target-min` (44px)
313
+ - ON/OFF 즉시 반영되는 설정에 사용 (저장 버튼 없이 즉시 토글)
314
+
315
+ #### 체크박스 vs 스위치 사용 기준
316
+
317
+ - **체크박스**: 옵션 선택, 동의(약관), 폼 제출과 함께 저장
318
+ - **스위치**: 즉시 효과 발생하는 ON/OFF 설정 (알림 ON/OFF, 다크모드 등)
319
+
320
+ #### 접근성
321
+
322
+ - `role="switch"` 권장 — 스크린리더가 "스위치"로 안내
323
+ - 시각 트랙(`__track`)은 `aria-hidden="true"`
324
+ - 상태 변경은 native `:checked`만으로 충분 (별도 aria-checked 불필요)
325
+
326
+ #### 출처
327
+
328
+ - CSS: `src/styles/6-components/switch.css`
329
+
330
+
331
+ ---
332
+
333
+ ## 그룹 B — 컨테이너/레이아웃
334
+
335
+ ##### 아코디언 (Accordion) — KRDS {#accordion}
336
+
337
+ native `<details>`/`<summary>` 활용 — JS 없이 동작.
338
+
339
+ #### 기본 마크업
340
+
341
+ ```html
342
+ <div class="accordion">
343
+ <details class="accordion__item">
344
+ <summary class="accordion__summary">자주 묻는 질문 1</summary>
345
+ <div class="accordion__panel">
346
+ <p>답변 1 내용</p>
347
+ </div>
348
+ </details>
349
+ <details class="accordion__item">
350
+ <summary class="accordion__summary">자주 묻는 질문 2</summary>
351
+ <div class="accordion__panel">
352
+ <p>답변 2 내용</p>
353
+ </div>
354
+ </details>
355
+ <details class="accordion__item" open>
356
+ <summary class="accordion__summary">기본 열린 항목</summary>
357
+ <div class="accordion__panel">
358
+ <p><code>open</code> 속성으로 초기 열림 상태</p>
359
+ </div>
360
+ </details>
361
+ </div>
362
+ ```
363
+
364
+ #### 단일 열림 (한 번에 하나만)
365
+
366
+ `<details name="group">`을 같은 `name`으로 묶으면 한 번에 하나만 열림 (모던 브라우저).
367
+
368
+ ```html
369
+ <div class="accordion">
370
+ <details class="accordion__item" name="faq"><summary class="accordion__summary">Q1</summary>...</details>
371
+ <details class="accordion__item" name="faq"><summary class="accordion__summary">Q2</summary>...</details>
372
+ </div>
373
+ ```
374
+
375
+ #### 접근성
376
+
377
+ - native `<details>`/`<summary>` 사용 시 키보드/스크린리더 자동 지원
378
+ - `<summary>`는 자동으로 button role + aria-expanded 처리됨 — 별도 ARIA 불필요
379
+ - 최소 터치 영역 보장: `--touch-target-min` (44px)
380
+
381
+ #### 출처
382
+
383
+ - CSS: `src/styles/6-components/accordion.css`
384
+
385
+
386
+ ---
387
+
388
+ ##### 카드 (Card) — KRDS {#card}
389
+
390
+ #### 기본 마크업
391
+
392
+ ```html
393
+ <article class="card">
394
+ <header class="card__header">
395
+ <h3 class="card__title">카드 제목</h3>
396
+ </header>
397
+ <div class="card__body">
398
+ <p>카드 본문 내용</p>
399
+ </div>
400
+ <footer class="card__footer">
401
+ <button type="button" class="btn btn--text btn--small">자세히</button>
402
+ </footer>
403
+ </article>
404
+ ```
405
+
406
+ #### 사이즈 (KRDS 정의 — 4종, 반응형)
407
+
408
+ | 사이즈 | 클래스 | Mobile padding | PC padding |
409
+ |--------|--------|---------------|-----------|
410
+ | xsmall | `.card--xsmall` | 12px | 16px |
411
+ | small | `.card--small` | 20px | 24px |
412
+ | medium | (기본) | 24px | 32px |
413
+ | large | `.card--large` | 24px | 40px |
414
+
415
+ #### Variant
416
+
417
+ - `.card` — 기본 (border-light + 흰 배경)
418
+ - `.card--inverse` — 다크 배경 + 흰 텍스트
419
+ - `.card--elevated` — border 없이 그림자만 (`--shadow-2`)
420
+ - `.card--link` 또는 `<a class="card">` — 호버 시 floating
421
+
422
+ ```html
423
+ <a class="card card--link" href="/article/1">
424
+ <div class="card__body">호버 시 그림자 + 살짝 위로 이동</div>
425
+ </a>
426
+ ```
427
+
428
+ #### 접근성
429
+
430
+ - 시맨틱 컨테이너: `<article>` (독립 콘텐츠) / `<section>` (관련 섹션) / `<div>` (장식)
431
+ - 카드 전체가 링크면 `<a class="card">` 또는 카드 내부 `<a>`만 링크 (이중 링크 금지)
432
+ - 카드 내 인터랙티브 요소는 `aria-label`로 컨텍스트 명시 권장
433
+
434
+ #### 출처
435
+
436
+ - 카드 padding/반경은 프로젝트 밀도에 맞는 CSS/Tailwind 직접값 사용
437
+ - CSS: `src/styles/6-components/card.css`
438
+
439
+
440
+ ---
441
+
442
+ ##### 디스클로저 (Disclosure) — KRDS {#disclosure}
443
+
444
+ 단일 "더보기/접기" 토글. Accordion(다중 그룹)과 달리 단독 토글에 사용.
445
+
446
+ #### 기본 마크업
447
+
448
+ ```html
449
+ <button type="button" class="disclosure" aria-expanded="false" aria-controls="more-info">
450
+ 자세히 보기
451
+ </button>
452
+ <div id="more-info" class="disclosure__panel" hidden>
453
+ <p>접혀 있던 추가 정보</p>
454
+ </div>
455
+ ```
456
+
457
+ #### 동작 (JS — 별도 구현)
458
+
459
+ ```js
460
+ const trigger = document.querySelector('.disclosure')
461
+ const panel = document.getElementById(trigger.getAttribute('aria-controls'))
462
+ trigger.addEventListener('click', () => {
463
+ const expanded = trigger.getAttribute('aria-expanded') === 'true'
464
+ trigger.setAttribute('aria-expanded', !expanded)
465
+ panel.hidden = expanded
466
+ })
467
+ ```
468
+
469
+ #### Accordion vs Disclosure
470
+
471
+ | | Accordion | Disclosure |
472
+ |---|-----------|-----------|
473
+ | 용도 | FAQ, 그룹화된 다중 항목 | 단일 "더보기" 토글 |
474
+ | 구조 | `<details>` 그룹 | `<button>` + 패널 |
475
+ | 시각 | 아이템 사이 구분선 | 인라인 버튼 |
476
+
477
+ #### 접근성
478
+
479
+ - `aria-expanded` 상태값 필수 (열림/닫힘)
480
+ - `aria-controls`로 패널 id 연결
481
+ - 패널 `hidden` 속성 또는 CSS `display: none` 사용 (레이아웃에서 완전 제거)
482
+
483
+ #### 출처
484
+
485
+ - CSS: `src/styles/6-components/disclosure.css`
486
+
487
+
488
+ ---
489
+
490
+ ##### 모달 (Modal / Dialog) — KRDS {#modal}
491
+
492
+ #### 기본 마크업
493
+
494
+ ```html
495
+ <div class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title" hidden>
496
+ <div class="modal__overlay"></div>
497
+ <div class="modal__content modal__content--medium">
498
+ <header class="modal__header">
499
+ <h2 id="modal-title" class="modal__title">제목</h2>
500
+ <button type="button" class="modal__close" aria-label="닫기">×</button>
501
+ </header>
502
+ <div class="modal__body">
503
+ <p>모달 본문 내용</p>
504
+ </div>
505
+ <footer class="modal__footer">
506
+ <button type="button" class="btn btn--tertiary">취소</button>
507
+ <button type="button" class="btn btn--primary">확인</button>
508
+ </footer>
509
+ </div>
510
+ </div>
511
+ ```
512
+
513
+ #### 사이즈
514
+
515
+ | 사이즈 | 클래스 | max-width |
516
+ |--------|--------|-----------|
517
+ | small | `.modal__content--small` | 400px |
518
+ | medium | (기본) | 560px |
519
+ | large | `.modal__content--large` | 800px |
520
+ | xlarge | `.modal__content--xlarge` | 1000px |
521
+
522
+ #### 동작 (JS — 별도 구현)
523
+
524
+ - 열기: `modal.removeAttribute('hidden')` + 포커스를 모달 내부 첫 요소로 이동
525
+ - 닫기: `modal.setAttribute('hidden', '')` + 포커스를 트리거 버튼으로 복귀
526
+ - ESC 키 닫기, overlay 클릭 닫기, Tab 트랩 (포커스 가두기)
527
+ - 열려 있는 동안 `<body>`에 `overflow: hidden` 적용
528
+
529
+ #### 접근성 (KRDS + WAI-ARIA)
530
+
531
+ - `role="dialog"` + `aria-modal="true"` 필수
532
+ - `aria-labelledby="제목id"`로 모달 제목 연결 (또는 `aria-label`)
533
+ - 본문 설명이 길면 `aria-describedby`도 추가
534
+ - 닫기 버튼은 `aria-label="닫기"` 필수
535
+ - 첫 포커스는 모달 내부 첫 인터랙티브 요소 (또는 닫기 버튼)
536
+ - ESC 키로 닫기 가능
537
+ - 백드롭은 `--color-bg-dim`
538
+
539
+ #### 출처
540
+
541
+ - Shape 값은 프로젝트 밀도에 맞는 CSS/Tailwind 직접값 사용
542
+ - Shadow: `--shadow-3` (KRDS modal-wrap-shadow 추상화)
543
+ - CSS: `src/styles/6-components/modal.css`
544
+
545
+
546
+ ---
547
+
548
+ ##### 사이드 패널 (Side Panel) — KRDS Help panel 응용 {#side-panel}
549
+
550
+ 우측에서 슬라이드 인하는 보조 패널. 모달과 달리 본문 스크롤을 차단하지 않는다.
551
+
552
+ #### 기본 마크업
553
+
554
+ ```html
555
+ <aside class="side-panel" role="dialog" aria-labelledby="panel-title" aria-hidden="true">
556
+ <header class="side-panel__header">
557
+ <h2 id="panel-title" class="side-panel__title">상세 정보</h2>
558
+ <button type="button" class="side-panel__close" aria-label="닫기">×</button>
559
+ </header>
560
+ <div class="side-panel__body">
561
+ <p>패널 본문 내용</p>
562
+ </div>
563
+ <footer class="side-panel__footer">
564
+ <button type="button" class="btn btn--tertiary">닫기</button>
565
+ <button type="button" class="btn btn--primary">저장</button>
566
+ </footer>
567
+ </aside>
568
+ ```
569
+
570
+ #### 사이즈
571
+
572
+ | 사이즈 | 클래스 | max-width |
573
+ |--------|--------|-----------|
574
+ | small | `.side-panel--small` | 360px |
575
+ | medium | (기본) | 480px |
576
+ | large | `.side-panel--large` | 640px |
577
+
578
+ #### 동작 (JS)
579
+
580
+ - 열기: `panel.setAttribute('aria-hidden', 'false')` (또는 `.side-panel--open` 추가)
581
+ - 닫기: `panel.setAttribute('aria-hidden', 'true')`
582
+ - transform 트랜지션으로 슬라이드 인/아웃
583
+
584
+ #### Modal vs Side Panel
585
+
586
+ | | Modal | Side Panel |
587
+ |---|-------|-----------|
588
+ | 위치 | 화면 중앙 | 화면 우측 (또는 좌측) |
589
+ | 본문 차단 | 차단 (overlay) | 차단 안 함 |
590
+ | 용도 | 결정 강제 (확인/취소) | 보조 정보, 편집 폼 |
591
+ | 그림자 | shadow-3 (deep) | shadow-2 (medium) |
592
+
593
+ #### 접근성
594
+
595
+ - `role="dialog"` + `aria-labelledby`
596
+ - 닫기 버튼 `aria-label="닫기"` 필수
597
+ - ESC 키 닫기 권장
598
+ - 포커스 트랩은 선택 (모달과 달리 본문 인터랙션 허용)
599
+ - `aria-hidden` 토글로 스크린리더 노출 제어
600
+
601
+ #### 출처
602
+
603
+ - Shadow: `--shadow-2` (KRDS help-panel-shadow 추상화)
604
+ - CSS: `src/styles/6-components/side-panel.css`
605
+
606
+
607
+ ---
608
+
609
+ ##### 탭 (Tab) — KRDS {#tab}
610
+
611
+ #### 기본 마크업 (WAI-ARIA tabs pattern)
612
+
613
+ ```html
614
+ <div class="tab">
615
+ <div class="tab__list" role="tablist" aria-label="섹션">
616
+ <button class="tab__item" role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">
617
+ 개요
618
+ </button>
619
+ <button class="tab__item" role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">
620
+ 상세
621
+ </button>
622
+ <button class="tab__item" role="tab" aria-selected="false" aria-controls="panel-3" id="tab-3" tabindex="-1">
623
+ 후기
624
+ </button>
625
+ </div>
626
+ <div class="tab__panel" role="tabpanel" id="panel-1" aria-labelledby="tab-1">
627
+ 개요 내용
628
+ </div>
629
+ <div class="tab__panel" role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
630
+ 상세 내용
631
+ </div>
632
+ <div class="tab__panel" role="tabpanel" id="panel-3" aria-labelledby="tab-3" hidden>
633
+ 후기 내용
634
+ </div>
635
+ </div>
636
+ ```
637
+
638
+ #### 사이즈
639
+
640
+ - `.tab__list--small` (40px height) — 보조 컨텍스트
641
+ - 기본 (48px) — 표준
642
+ - `.tab__list--large` (56px height) — 강조
643
+
644
+ #### 동작 (JS — 별도 구현)
645
+
646
+ - 탭 클릭 → 모든 `aria-selected="false"` + 클릭한 탭만 `aria-selected="true"`
647
+ - 모든 패널 `hidden` + 활성 탭의 `aria-controls` 패널만 `hidden` 제거
648
+ - 키보드 — 좌/우 화살표로 탭 이동, Home/End로 처음/마지막
649
+ - 비활성 탭은 `tabindex="-1"`로 Tab 키에서 제외 (활성 탭만 `tabindex` 없음)
650
+
651
+ #### 접근성 (KRDS + WAI-ARIA)
652
+
653
+ - `role="tablist"` + 각 탭에 `role="tab"`, 패널에 `role="tabpanel"`
654
+ - 탭 ↔ 패널 연결: `aria-controls` (탭 → 패널 id), `aria-labelledby` (패널 → 탭 id)
655
+ - 활성 표시는 `aria-selected="true"` (시각 인디케이터는 CSS가 자동 처리)
656
+ - `aria-label` 또는 `aria-labelledby`로 탭 그룹의 목적 명시 권장
657
+
658
+ #### 출처
659
+
660
+ - CSS: `src/styles/6-components/tab.css`
661
+
662
+
663
+ ---
664
+
665
+ ## 그룹 C — 내비게이션
666
+
667
+ ##### 브레드크럼 (Breadcrumb) — KRDS {#breadcrumb}
668
+
669
+ 페이지 경로 표시. 사용자의 현재 위치를 보여주고 상위 페이지로 빠르게 이동할 수 있게 한다.
670
+
671
+ #### 기본 마크업
672
+
673
+ ```html
674
+ <nav class="breadcrumb" aria-label="페이지 경로">
675
+ <ol class="breadcrumb__list">
676
+ <li class="breadcrumb__item"><a href="/">홈</a></li>
677
+ <li class="breadcrumb__item"><a href="/services">서비스</a></li>
678
+ <li class="breadcrumb__item" aria-current="page">신청하기</li>
679
+ </ol>
680
+ </nav>
681
+ ```
682
+
683
+ #### 접근성
684
+
685
+ - `<nav aria-label="페이지 경로">` 필수 (스크린리더용 식별자)
686
+ - `<ol>` 사용 — 순서가 의미를 가짐
687
+ - 현재 페이지: `aria-current="page"` + `<a>` 없이 텍스트만 (링크 아님)
688
+ - 구분자(`›`)는 CSS `::before`로 그려서 스크린리더에 노출 안 됨 (불필요한 읽기 방지)
689
+
690
+ #### 출처
691
+
692
+ - CSS: `src/styles/6-components/breadcrumb.css`
693
+
694
+
695
+ ---
696
+
697
+ ##### 사이트 헤더 (Site Header) {#header}
698
+
699
+ 사이트 유형과 무관하게 사용할 수 있는 공통 헤더 패턴. 브랜드 + 주 메뉴 + 액션 영역을 기본으로 하며, 공식 배너·정부 상징·운영기관 식별자는 공공서비스/공공기관에서 적용 대상이 확인된 경우에만 별도 추가한다.
700
+
701
+ #### 기본 마크업
702
+
703
+ ```html
704
+ <header id="header" class="site-header">
705
+ <div class="container site-header__inner">
706
+ <a class="site-header__brand" href="/">
707
+ <img src="/logo.svg" alt="기관명">
708
+ <span class="site-header__brand-name">기관명</span>
709
+ </a>
710
+
711
+ <nav class="site-header__nav" aria-label="주 메뉴">
712
+ <ul class="site-header__menu">
713
+ <li><a href="/about">소개</a></li>
714
+ <li><a href="/services" aria-current="page">서비스</a></li>
715
+ <li><a href="/notice">공지</a></li>
716
+ </ul>
717
+ </nav>
718
+
719
+ <div class="site-header__actions">
720
+ <button type="button" class="btn btn--text btn--small">로그인</button>
721
+ <button type="button" class="site-header__toggle" aria-label="메뉴 열기" aria-expanded="false">☰</button>
722
+ </div>
723
+ </div>
724
+ </header>
725
+ ```
726
+
727
+ #### 동작
728
+
729
+ - 모바일/태블릿 (< 1280px): 주 메뉴 숨김 + 햄버거 토글 노출
730
+ - PC (≥ 1280px): 주 메뉴 노출 + 햄버거 숨김
731
+ - `position: sticky` 적용 (스크롤 시에도 상단 유지)
732
+
733
+ #### 접근성
734
+
735
+ - `<header id="header">` 시맨틱 태그 사용 (페이지당 하나)
736
+ - 주 메뉴는 `<nav aria-label="주 메뉴">` (페이지에 nav가 여러 개면 label 필수)
737
+ - 현재 페이지 메뉴: `aria-current="page"`
738
+ - 모바일 토글: `aria-label="메뉴 열기/닫기"` + `aria-expanded` 상태 토글
739
+ - 로고 `<img>` `alt` 텍스트 필수 (KRDS R-09)
740
+
741
+ #### 조건부 공공/정부 요소
742
+
743
+ 아래 요소는 모든 사이트의 기본값이 아니다.
744
+
745
+ - 공식 배너: 공공서비스 중 정부 상징 사용이 명시되었거나 과업에서 요구된 경우만 생성
746
+ - 정부 상징 로고: 정부 상징 사용 대상 서비스에서만 생성
747
+ - 운영기관 식별자: 상위 운영기관 표시가 과업에 포함된 경우만 생성
748
+ - 일반사이트, CMS·관리자, 커머스·예약에서는 위 항목을 생성하지 않고 체크리스트에서 N/A 처리
749
+
750
+ #### 출처
751
+
752
+ - CSS: `src/styles/6-components/header.css`
753
+
754
+
755
+ ---
756
+
757
+ ##### 주 메뉴 (Main Menu) — KRDS {#main-menu}
758
+
759
+ 드롭다운형 주 내비게이션. 헤더 안에서 사용.
760
+
761
+ #### 기본 마크업
762
+
763
+ ```html
764
+ <nav class="main-menu" aria-label="주 메뉴">
765
+ <ul class="main-menu__list">
766
+ <li class="main-menu__item">
767
+ <a class="main-menu__link" href="/about">소개</a>
768
+ </li>
769
+
770
+ <li class="main-menu__item">
771
+ <button type="button" class="main-menu__link" aria-haspopup="true" aria-expanded="false" aria-controls="submenu-services">
772
+ 서비스
773
+ </button>
774
+ <ul id="submenu-services" class="main-menu__submenu" hidden>
775
+ <li><a href="/services/a">서비스 A</a></li>
776
+ <li><a href="/services/b">서비스 B</a></li>
777
+ <li><a href="/services/c" aria-current="page">서비스 C</a></li>
778
+ </ul>
779
+ </li>
780
+
781
+ <li class="main-menu__item">
782
+ <a class="main-menu__link" href="/contact">문의</a>
783
+ </li>
784
+ </ul>
785
+ </nav>
786
+ ```
787
+
788
+ #### 동작 (JS)
789
+
790
+ - 드롭다운 토글: `aria-haspopup` 가진 `<button>` 클릭 → 해당 `aria-controls` 서브메뉴 hidden 토글 + `aria-expanded` 토글
791
+ - 외부 클릭 시 닫기
792
+ - ESC 키로 닫기 + 트리거 버튼으로 포커스 복귀
793
+ - 키보드: 화살표로 항목 이동, Enter/Space로 선택
794
+
795
+ #### 접근성
796
+
797
+ - 서브메뉴 트리거는 `<button>` 권장 (`<a>` 아님 — 링크가 아니므로)
798
+ - `aria-haspopup="true"` + `aria-expanded` 상태값
799
+ - `aria-controls`로 서브메뉴 id 연결
800
+ - 서브메뉴 `<ul>`은 `hidden` 속성으로 노출 제어
801
+ - 현재 페이지: `aria-current="page"`
802
+
803
+ #### 출처
804
+
805
+ - CSS: `src/styles/6-components/main-menu.css`
806
+
807
+
808
+ ---
809
+
810
+ ##### 페이지네이션 (Pagination) — KRDS {#pagination}
811
+
812
+ 목록을 여러 페이지로 나눠 탐색.
813
+
814
+ #### 기본 마크업
815
+
816
+ ```html
817
+ <nav class="pagination" aria-label="페이지 내비게이션">
818
+ <button type="button" class="pagination__nav" aria-label="이전 페이지">‹</button>
819
+ <ol class="pagination__list">
820
+ <li><a class="pagination__item" href="?p=1">1</a></li>
821
+ <li><a class="pagination__item pagination__item--current" href="?p=2" aria-current="page">2</a></li>
822
+ <li><a class="pagination__item" href="?p=3">3</a></li>
823
+ <li><a class="pagination__item" href="?p=4">4</a></li>
824
+ <li><a class="pagination__item" href="?p=5">5</a></li>
825
+ </ol>
826
+ <button type="button" class="pagination__nav" aria-label="다음 페이지">›</button>
827
+ </nav>
828
+ ```
829
+
830
+ #### 사이즈
831
+
832
+ - `.pagination` — 48px (기본)
833
+ - `.pagination--small` — 40px
834
+
835
+ #### 비활성 (첫/마지막 페이지)
836
+
837
+ ```html
838
+ <button class="pagination__nav" aria-label="이전 페이지" aria-disabled="true" disabled>‹</button>
839
+ ```
840
+
841
+ #### 접근성
842
+
843
+ - `<nav aria-label="페이지 내비게이션">` 필수
844
+ - 현재 페이지: `aria-current="page"`
845
+ - 이전/다음 버튼: `aria-label="이전 페이지"` / `aria-label="다음 페이지"` 필수
846
+ - `aria-disabled="true"` + `disabled` 같이 사용 (첫/마지막 페이지)
847
+
848
+ #### 출처
849
+
850
+ - CSS: `src/styles/6-components/pagination.css`
851
+
852
+
853
+ ---
854
+
855
+ ## 그룹 D — 피드백
856
+
857
+ ##### 알림 (Alert / Critical Alerts) — KRDS {#alert}
858
+
859
+ 페이지에 고정 노출되는 정보/경고/오류 메시지.
860
+
861
+ #### 기본 마크업
862
+
863
+ ```html
864
+ <div class="alert alert--info" role="alert">
865
+ <div class="alert__icon" aria-hidden="true">ℹ</div>
866
+ <div class="alert__body">
867
+ <p class="alert__title">신청 기간 안내</p>
868
+ <p class="alert__message">2026년 5월 1일부터 31일까지 신청 가능합니다.</p>
869
+ </div>
870
+ <button type="button" class="alert__close" aria-label="닫기">×</button>
871
+ </div>
872
+ ```
873
+
874
+ #### Variant
875
+
876
+ | Variant | 클래스 | 용도 |
877
+ |---------|--------|------|
878
+ | Info | `.alert--info` | 일반 정보 |
879
+ | Success | `.alert--success` | 성공 알림 |
880
+ | Warning | `.alert--warning` | 주의 |
881
+ | Danger / Critical | `.alert--danger` 또는 `.alert--critical` | 오류·긴급 |
882
+
883
+ #### 접근성
884
+
885
+ - 일반 알림: `role="alert"` (즉시 안내) 또는 `role="status"` (정중한 안내)
886
+ - 긴급(Critical)은 `role="alert"` + `aria-live="assertive"`
887
+ - 일반 정보성은 `role="status"` + `aria-live="polite"` 권장
888
+ - 아이콘은 장식용 — `aria-hidden="true"` (텍스트가 의미 전달)
889
+ - 닫기 버튼 `aria-label="닫기"` 필수
890
+
891
+ #### 출처
892
+
893
+ - CSS: `src/styles/6-components/alert.css`
894
+
895
+
896
+ ---
897
+
898
+ ##### 배지 (Badge) — KRDS {#badge}
899
+
900
+ 작은 알림 표시기 (카운트 / 상태 / 새 항목).
901
+
902
+ #### 기본 마크업
903
+
904
+ ```html
905
+ <!-- 숫자 배지 -->
906
+ <button class="btn btn--text">
907
+ 알림 <span class="badge">3</span>
908
+ </button>
909
+
910
+ <!-- 점만 (dot 변형) -->
911
+ <span class="badge badge--dot" aria-label="새 알림 있음"></span>
912
+ ```
913
+
914
+ #### Variant
915
+
916
+ - `.badge` (기본 — danger 빨강) / `.badge--primary` / `.badge--info` / `.badge--success` / `.badge--warning` / `.badge--gray`
917
+ - `.badge--dot` — 숫자 없는 단순 점 (8×8)
918
+
919
+ #### 접근성
920
+
921
+ - 숫자 배지: 텍스트로 의미 전달됨 (별도 ARIA 불필요)
922
+ - Dot 배지: 시각만 — `aria-label="새 알림 있음"` 필수
923
+
924
+ #### 출처
925
+
926
+ - CSS: `src/styles/6-components/badge.css`
927
+
928
+
929
+ ---
930
+
931
+ ##### 진행률 (Progress) — KRDS {#progress}
932
+
933
+ native `<progress>` + 시각 커스텀.
934
+
935
+ #### 기본 마크업
936
+
937
+ ```html
938
+ <div class="progress">
939
+ <div class="progress__label">
940
+ <span>업로드 중</span>
941
+ <span>60%</span>
942
+ </div>
943
+ <progress class="progress__bar" value="60" max="100" aria-label="업로드 진행률 60%">60%</progress>
944
+ </div>
945
+ ```
946
+
947
+ #### Variant
948
+
949
+ - 기본 (primary)
950
+ - `.progress--success` / `.progress--warning` / `.progress--danger`
951
+
952
+ #### 접근성
953
+
954
+ - native `<progress>` 사용 — 자동 ARIA 처리
955
+ - `aria-label` 또는 `aria-labelledby`로 진행 항목 명시
956
+ - 무한 로딩(불확정 시간)은 `<progress>` 대신 `.spinner` 사용
957
+
958
+ #### 출처
959
+
960
+ - CSS: `src/styles/6-components/progress.css`
961
+
962
+
963
+ ---
964
+
965
+ ##### 스피너 (Spinner) — KRDS {#spinner}
966
+
967
+ 로딩 표시기. 진행 시간을 알 수 없을 때 사용.
968
+
969
+ #### 기본 마크업
970
+
971
+ ```html
972
+ <span class="spinner" role="status" aria-label="로딩 중"></span>
973
+ ```
974
+
975
+ #### 사이즈
976
+
977
+ - `.spinner--small` (16×16)
978
+ - 기본 (24×24)
979
+ - `.spinner--large` (40×40)
980
+
981
+ #### 접근성
982
+
983
+ - `role="status"` + `aria-label="로딩 중"` 필수
984
+ - `prefers-reduced-motion: reduce` 자동 대응 (회전 속도 절반으로)
985
+
986
+ #### 출처
987
+
988
+ - CSS: `src/styles/6-components/spinner.css`
989
+
990
+
991
+ ---
992
+
993
+ ##### 단계 표시기 (Step Indicator) — KRDS {#step-indicator}
994
+
995
+ 다단계 폼/프로세스의 현재 단계 표시.
996
+
997
+ #### 기본 마크업
998
+
999
+ ```html
1000
+ <ol class="step-indicator" aria-label="진행 단계">
1001
+ <li class="step-indicator__item step-indicator__item--done">
1002
+ <span class="step-indicator__num" aria-hidden="true">1</span>
1003
+ <span class="step-indicator__label">정보 입력</span>
1004
+ </li>
1005
+ <li class="step-indicator__item step-indicator__item--current" aria-current="step">
1006
+ <span class="step-indicator__num" aria-hidden="true">2</span>
1007
+ <span class="step-indicator__label">확인</span>
1008
+ </li>
1009
+ <li class="step-indicator__item">
1010
+ <span class="step-indicator__num" aria-hidden="true">3</span>
1011
+ <span class="step-indicator__label">완료</span>
1012
+ </li>
1013
+ </ol>
1014
+ ```
1015
+
1016
+ #### 상태 클래스
1017
+
1018
+ - `.step-indicator__item--done` — 완료 (primary 채움)
1019
+ - `.step-indicator__item--current` — 현재 (primary 테두리)
1020
+ - (없음) — 예정 (회색)
1021
+
1022
+ #### 접근성
1023
+
1024
+ - `<ol>` 사용 — 순서 의미 보존
1025
+ - 현재 단계: `aria-current="step"`
1026
+ - 번호는 시각만 — `aria-hidden="true"` (레이블이 텍스트로 의미 전달)
1027
+
1028
+ #### 출처
1029
+
1030
+ - CSS: `src/styles/6-components/step-indicator.css`
1031
+
1032
+
1033
+ ---
1034
+
1035
+ ##### 태그 (Tag) — KRDS {#tag}
1036
+
1037
+ 카테고리, 필터, 속성 라벨.
1038
+
1039
+ #### 기본 마크업
1040
+
1041
+ ```html
1042
+ <!-- 정적 태그 -->
1043
+ <span class="tag">기본</span>
1044
+
1045
+ <!-- 클릭 가능 (필터 등) -->
1046
+ <button type="button" class="tag tag--primary">선택됨</button>
1047
+
1048
+ <!-- 제거 가능 (선택된 필터) -->
1049
+ <span class="tag tag--info">
1050
+ 카테고리: 디자인
1051
+ <button type="button" class="tag__close" aria-label="카테고리: 디자인 제거">×</button>
1052
+ </span>
1053
+
1054
+ <!-- 링크형 -->
1055
+ <a class="tag tag--success" href="?category=ui">UI</a>
1056
+ ```
1057
+
1058
+ #### Variant
1059
+
1060
+ - 기본 (회색 outline)
1061
+ - 시맨틱: `.tag--primary` / `.tag--info` / `.tag--success` / `.tag--warning` / `.tag--danger`
1062
+
1063
+ #### 사이즈
1064
+
1065
+ - `.tag--small` (20px)
1066
+ - 기본 (24px)
1067
+ - `.tag--large` (32px)
1068
+
1069
+ #### 접근성
1070
+
1071
+ - 제거 버튼은 `aria-label="태그명 제거"` 형식으로 컨텍스트 명시
1072
+ - 클릭 가능 태그는 `<button>` 또는 `<a>` 사용 (div onclick 금지)
1073
+
1074
+ #### 출처
1075
+
1076
+ - CSS: `src/styles/6-components/tag.css`
1077
+
1078
+
1079
+ ---
1080
+
1081
+ ##### 토스트 (Toast) — KRDS {#toast}
1082
+
1083
+ 일시적 피드백 메시지. 화면 우측 상단/하단에 잠시 노출 후 자동 사라짐.
1084
+
1085
+ #### 기본 마크업
1086
+
1087
+ ```html
1088
+ <div class="toast-stack">
1089
+ <div class="toast toast--success" role="status">
1090
+ <span class="toast__icon" aria-hidden="true">✓</span>
1091
+ <p class="toast__message">저장되었습니다</p>
1092
+ <button type="button" class="toast__close" aria-label="닫기">×</button>
1093
+ </div>
1094
+ </div>
1095
+ ```
1096
+
1097
+ #### 위치
1098
+
1099
+ - `.toast-stack` (기본) — 우측 상단
1100
+ - `.toast-stack--bottom` — 우측 하단
1101
+
1102
+ #### Variant
1103
+
1104
+ - `.toast--info` / `.toast--success` / `.toast--warning` / `.toast--danger`
1105
+
1106
+ #### 동작 (JS)
1107
+
1108
+ - 보통 3~5초 후 자동 닫기 (사용자 액션 결과 통보)
1109
+ - 사용자가 닫기 버튼으로 즉시 닫기 가능
1110
+ - 다중 토스트는 위에서 아래로 누적
1111
+
1112
+ #### Alert vs Toast 사용 기준
1113
+
1114
+ | | Alert | Toast |
1115
+ |---|-------|-------|
1116
+ | 위치 | 페이지 인라인 | 화면 고정 |
1117
+ | 지속시간 | 사용자가 닫을 때까지 | 자동 사라짐 |
1118
+ | 용도 | 페이지 컨텍스트 알림 | 즉시 피드백 (저장 완료 등) |
1119
+
1120
+ #### 접근성
1121
+
1122
+ - `role="status"` + `aria-live="polite"` (스크린리더 정중 안내)
1123
+ - 위급 시에만 `role="alert"` + `aria-live="assertive"`
1124
+ - 자동 닫힘 토스트도 사용자 옵션으로 일시정지/지속 가능해야 함 (WCAG 2.2.1)
1125
+
1126
+ #### 출처
1127
+
1128
+ - CSS: `src/styles/6-components/toast.css`
1129
+
1130
+
1131
+ ---
1132
+
1133
+ ##### 툴팁 (Tooltip) — KRDS {#tooltip}
1134
+
1135
+ 짧은 보조 설명 팝업. **JS 트리거 변형 권장** (키보드/터치 호환).
1136
+
1137
+ #### 기본 마크업 (JS 변형)
1138
+
1139
+ ```html
1140
+ <button type="button" class="tooltip-trigger" aria-describedby="tip-1">
1141
+ 도움말
1142
+ </button>
1143
+ <div id="tip-1" class="tooltip" role="tooltip" hidden>
1144
+ 도움말 설명 텍스트
1145
+ </div>
1146
+ ```
1147
+
1148
+ JS: focus/mouseenter 시 `tooltip.removeAttribute('hidden')`, blur/mouseleave 시 `setAttribute('hidden', '')`.
1149
+
1150
+ #### CSS-only hover 변형 (단순 케이스)
1151
+
1152
+ ```html
1153
+ <span class="tooltip-wrap">
1154
+ <button type="button" aria-label="도움말">?</button>
1155
+ <span class="tooltip" role="tooltip">설명 텍스트</span>
1156
+ </span>
1157
+ ```
1158
+
1159
+ > 키보드 사용자는 호버할 수 없으므로 중요 정보는 JS 변형 사용.
1160
+
1161
+ #### 접근성
1162
+
1163
+ - 트리거에 `aria-describedby="툴팁id"` 연결
1164
+ - 툴팁에 `role="tooltip"` 필수
1165
+ - ESC로 닫기 가능 (JS 처리)
1166
+ - 툴팁은 hover/focus 양쪽으로 트리거 가능해야 함 (WCAG 1.4.13)
1167
+
1168
+ #### 출처
1169
+
1170
+ - CSS: `src/styles/6-components/tooltip.css`
1171
+
1172
+
1173
+ ---
1174
+
1175
+ ## 그룹 E — 콘텐츠/표현
1176
+
1177
+ ##### 달력 (Calendar) — KRDS {#calendar}
1178
+
1179
+ 날짜 선택 그리드. 단독 사용 또는 date input의 팝업으로 사용.
1180
+
1181
+ #### 기본 마크업
1182
+
1183
+ ```html
1184
+ <div class="calendar" role="application" aria-label="날짜 선택">
1185
+ <div class="calendar__head">
1186
+ <button type="button" class="calendar__nav" aria-label="이전 달">‹</button>
1187
+ <h2 class="calendar__title" aria-live="polite">2026년 4월</h2>
1188
+ <button type="button" class="calendar__nav" aria-label="다음 달">›</button>
1189
+ </div>
1190
+
1191
+ <table class="calendar__grid" role="grid">
1192
+ <thead>
1193
+ <tr>
1194
+ <th scope="col" abbr="일요일">일</th>
1195
+ <th scope="col" abbr="월요일">월</th>
1196
+ <th scope="col" abbr="화요일">화</th>
1197
+ <th scope="col" abbr="수요일">수</th>
1198
+ <th scope="col" abbr="목요일">목</th>
1199
+ <th scope="col" abbr="금요일">금</th>
1200
+ <th scope="col" abbr="토요일">토</th>
1201
+ </tr>
1202
+ </thead>
1203
+ <tbody>
1204
+ <tr role="row">
1205
+ <td role="gridcell">
1206
+ <button type="button" class="calendar__day calendar__day--other-month" aria-label="2026년 3월 30일">30</button>
1207
+ </td>
1208
+ <td role="gridcell">
1209
+ <button type="button" class="calendar__day" aria-label="2026년 4월 1일">1</button>
1210
+ </td>
1211
+ <td role="gridcell">
1212
+ <button type="button" class="calendar__day calendar__day--today" aria-label="2026년 4월 2일 (오늘)">2</button>
1213
+ </td>
1214
+ <td role="gridcell">
1215
+ <button type="button" class="calendar__day calendar__day--selected" aria-selected="true" aria-label="2026년 4월 3일 (선택됨)">3</button>
1216
+ </td>
1217
+ </tr>
1218
+ </tbody>
1219
+ </table>
1220
+ </div>
1221
+ ```
1222
+
1223
+ #### 상태 클래스
1224
+
1225
+ - `.calendar__day--today` — 오늘 (primary 테두리)
1226
+ - `.calendar__day--selected` 또는 `aria-selected="true"` — 선택됨 (primary 채움)
1227
+ - `.calendar__day--other-month` — 다른 달 날짜 (희미)
1228
+ - `:disabled` — 선택 불가
1229
+
1230
+ #### 접근성
1231
+
1232
+ - 컨테이너 `role="application"` + `aria-label`
1233
+ - `<table role="grid">` 그리드 ARIA
1234
+ - 일자 버튼은 전체 날짜 컨텍스트로 `aria-label="YYYY년 M월 D일"` (그래야 스크린리더가 "1"이 아닌 "4월 1일"로 읽음)
1235
+ - 선택 상태: `aria-selected="true"`
1236
+ - 키보드: 화살표(일 단위), Page Up/Down(월), Home/End(주 시작/끝)
1237
+
1238
+ #### 출처
1239
+
1240
+ - CSS: `src/styles/6-components/calendar.css`
1241
+
1242
+
1243
+ ---
1244
+
1245
+ ##### 캐러셀 (Carousel) — KRDS {#carousel}
1246
+
1247
+ 스크롤 스냅 기반 슬라이드 컨테이너.
1248
+
1249
+ #### 기본 마크업
1250
+
1251
+ ```html
1252
+ <div class="carousel" aria-roledescription="carousel" aria-label="추천 항목">
1253
+ <div class="carousel__viewport">
1254
+ <ol class="carousel__track">
1255
+ <li class="carousel__slide" aria-roledescription="slide" aria-label="1 / 3">
1256
+ <img src="/img1.jpg" alt="설명">
1257
+ </li>
1258
+ <li class="carousel__slide" aria-roledescription="slide" aria-label="2 / 3">
1259
+ <img src="/img2.jpg" alt="설명">
1260
+ </li>
1261
+ <li class="carousel__slide" aria-roledescription="slide" aria-label="3 / 3">
1262
+ <img src="/img3.jpg" alt="설명">
1263
+ </li>
1264
+ </ol>
1265
+ </div>
1266
+
1267
+ <button type="button" class="carousel__nav carousel__nav--prev" aria-label="이전 슬라이드">‹</button>
1268
+ <button type="button" class="carousel__nav carousel__nav--next" aria-label="다음 슬라이드">›</button>
1269
+
1270
+ <div class="carousel__indicators" role="tablist">
1271
+ <button type="button" class="carousel__dot" role="tab" aria-selected="true" aria-label="1번 슬라이드"></button>
1272
+ <button type="button" class="carousel__dot" role="tab" aria-selected="false" aria-label="2번 슬라이드"></button>
1273
+ <button type="button" class="carousel__dot" role="tab" aria-selected="false" aria-label="3번 슬라이드"></button>
1274
+ </div>
1275
+ </div>
1276
+ ```
1277
+
1278
+ #### 접근성 (WCAG 2.2 + KRDS)
1279
+
1280
+ - 자동 재생은 **기본 OFF** 권장 — 사용자 통제권 (WCAG 2.2.2)
1281
+ - 자동 재생 시: 일시정지 버튼 필수 + `prefers-reduced-motion: reduce` 시 자동 비활성
1282
+ - 인디케이터는 키보드 작동 가능
1283
+ - 슬라이드별 `aria-label="N / 총수"`로 위치 안내
1284
+
1285
+ #### 출처
1286
+
1287
+ - CSS: `src/styles/6-components/carousel.css`
1288
+
1289
+
1290
+ ---
1291
+
1292
+ ##### 목록 (List) — KRDS Text list / Structured list {#list}
1293
+
1294
+ 두 가지 변형 — 텍스트 목록과 구조화 목록(정의 목록).
1295
+
1296
+ #### 기본 마크업
1297
+
1298
+ ##### 텍스트 목록 (List — Text)
1299
+
1300
+ ```html
1301
+ <!-- 글머리표 -->
1302
+ <ul class="list--text">
1303
+ <li>첫 번째 항목</li>
1304
+ <li>두 번째 항목
1305
+ <ul>
1306
+ <li>중첩 항목</li>
1307
+ </ul>
1308
+ </li>
1309
+ </ul>
1310
+
1311
+ <!-- 번호 -->
1312
+ <ol class="list--text list--ordered">
1313
+ <li>1단계</li>
1314
+ <li>2단계</li>
1315
+ </ol>
1316
+ ```
1317
+
1318
+ ##### 구조화 목록 (List — Structured / Definition)
1319
+
1320
+ KRDS 정의 목록 패턴. 라벨 + 값 쌍 (예: 사양, 상세 정보).
1321
+
1322
+ ```html
1323
+ <dl class="list--structured">
1324
+ <div class="list__row">
1325
+ <dt>제품명</dt>
1326
+ <dd>예시 제품</dd>
1327
+ </div>
1328
+ <div class="list__row">
1329
+ <dt>출시일</dt>
1330
+ <dd>2026년 4월 30일</dd>
1331
+ </div>
1332
+ <div class="list__row">
1333
+ <dt>가격</dt>
1334
+ <dd>10,000원</dd>
1335
+ </div>
1336
+ </dl>
1337
+ ```
1338
+
1339
+ #### 시맨틱 구조
1340
+
1341
+ - **Root 태그**: `<ul>` (순서 무관) / `<ol>` (순서 의미) / `<dl>` (정의 목록)
1342
+ - **자식**: `<li>` (ul/ol) 또는 `<dt>` + `<dd>` (dl)
1343
+ - **변형 클래스**: `.list--text` · `.list--ordered` · `.list--structured`
1344
+ - **필수 ARIA**: — (시맨틱 태그만으로 충분)
1345
+ - 상세: `skill/references/html-semantics.md#list`
1346
+
1347
+ #### 접근성
1348
+
1349
+ - 순서 의미 — `<ol>` (있음) / `<ul>` (없음)
1350
+ - 정의 목록 — `<dl>/<dt>/<dd>` 시맨틱 사용
1351
+ - 모바일에선 정의 목록이 자동으로 1열로 변환
1352
+
1353
+ #### 출처
1354
+
1355
+ - CSS: `src/styles/6-components/list.css`
1356
+
1357
+
1358
+ ---
1359
+
1360
+ ##### 표 (Table) — KRDS {#table}
1361
+
1362
+ 데이터 표. 모바일에서는 가로 스크롤 컨테이너로 감싸서 사용.
1363
+
1364
+ #### 기본 마크업
1365
+
1366
+ ```html
1367
+ <div class="table-wrap">
1368
+ <table class="table">
1369
+ <caption class="table__caption">2026년 1분기 신청 현황</caption>
1370
+ <thead>
1371
+ <tr>
1372
+ <th scope="col">번호</th>
1373
+ <th scope="col">신청자</th>
1374
+ <th scope="col">신청일</th>
1375
+ <th scope="col">상태</th>
1376
+ </tr>
1377
+ </thead>
1378
+ <tbody>
1379
+ <tr>
1380
+ <td>1</td>
1381
+ <td>홍길동</td>
1382
+ <td>2026-04-01</td>
1383
+ <td><span class="tag tag--success">완료</span></td>
1384
+ </tr>
1385
+ </tbody>
1386
+ </table>
1387
+ </div>
1388
+ ```
1389
+
1390
+ #### Variant
1391
+
1392
+ - 기본
1393
+ - `.table--hover` — 행 호버 강조
1394
+ - `.table--striped` — 줄무늬 (짝수 행 회색)
1395
+ - `.table--compact` — 좁은 패딩 (8px 12px)
1396
+ - `.table--comfortable` — 넓은 패딩 (20px)
1397
+
1398
+ #### 정렬 가능한 헤더
1399
+
1400
+ ```html
1401
+ <th scope="col" aria-sort="ascending">
1402
+ <button type="button">신청일</button>
1403
+ </th>
1404
+ ```
1405
+
1406
+ #### 선택된 행
1407
+
1408
+ ```html
1409
+ <tr aria-selected="true">...</tr>
1410
+ ```
1411
+
1412
+ #### 접근성
1413
+
1414
+ - `<th scope="col">` 또는 `scope="row">` 필수
1415
+ - `<caption>`으로 표 제목 명시
1416
+ - 정렬 헤더는 `aria-sort="ascending|descending|none"`
1417
+ - 선택 행은 `aria-selected="true"`
1418
+ - 모바일 가로 스크롤은 `<div class="table-wrap">` 래퍼로 감싸기
1419
+
1420
+ #### 출처
1421
+
1422
+ - CSS: `src/styles/6-components/table.css`
1423
+
1424
+
1425
+ ---
1426
+