Files
llm-wiki/raw/branch-notes/feature-accessibility-baseline-contract.md

290 lines
27 KiB
Markdown

---
title: branch / feature-accessibility-baseline-contract
source_type: branch-note
status: raw
branch: feature-accessibility-baseline-contract
parent_branch:
related_projects: [ca-skeleton-frontend, ca-skeleton]
governing_docs: [raw/project-notes/ca-skeleton-frontend-operational-contract]
tags: [branch, ca-skeleton, frontend, testing, react, static-analysis]
created: 2026-07-18
target_merge:
status_label: in-progress
id: BR-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-019
kind: project-work-item
project: ca-skeleton-frontend-operational-contract
work_item: WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-019
inherits: [DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-STYLING-001@1, DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1]
refines: []
overrides: []
depends_on: [WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-013]
contract_packet: 1
contract_packet_sha256: c7f5f84ade7d63ed9663a6988f27472d1bef629d546049c86ab104ba2314fcac
imports: [FE-GATE-006@1, FE-OC-001@1, FE-OC-011@1, FE-OC-020@1, FE-OC-021@1, FE-OC-026@1]
delegates: [DELEG-FE-004@1]
---
# branch: feature-accessibility-baseline-contract
> Layer: `raw/branch-notes/` — TODO·결정·진행 기록. 구현 결과는 검증 뒤 `/ingest`로만 추출한다.
<!-- section-id: branch-parent -->
## 부모 (필수)
[[raw/project-notes/ca-skeleton-frontend-operational-contract]]
형제 branch (같은 project 의 다른 자식, 본 branch 가 의존/기여):
- [[raw/branch-notes/feature-async-ui-state-contract]] — async surface state 모델 owner (`FE-OC-011`). 본 branch 가 그 state 위에 a11y semantics 를 얹음(그 branch 가 명시적으로 위임).
- [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] — gate/fixture/artifact 분리 owner (`FE-OC-020`). a11y gate 는 그 taxonomy 의 한 gate.
- [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] — sample route owner (`FE-OC-024`). a11y 증거를 측정할 대상 route 제공.
<!-- GENERATED: branch-contract:start -->
<!-- section-id: branch-contract-packet -->
## 브랜치 계약 패킷
- **생성 시 프로젝트 개정**: `1`
- **패킷 스키마**: `contract_packet: 1`
- **완료 조건**: sample route에서 axe·keyboard·focus evidence가 남는다
<!-- section-id: inherited-project-decisions -->
### 상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-STYLING-001@1` | styling default는 Tailwind theme token과 component primitive다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TEST-STACK-001@1` | test stack default는 Vitest, RTL, MSW, Playwright, axe다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
<!-- section-id: branch-local-decisions -->
### 브랜치 지역 결정
> 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|---|---|---|---|
<!-- section-id: declared-overrides -->
### 선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|---|---|---|---|
<!-- GENERATED: branch-contract:end -->
<!-- section-id: branch-goal -->
## 목표
- 이 branch 는 hub §10.3 Accessibility baseline 의 planned 요구를 *되묻지 않고 구현·검증 가능한* 계약으로 내린다. 스스로 Primary `FE-OC-*` 를 소유하지 않고 (§20 branch 분해표: Primary `—`), `FE-OC-019`·`FE-OC-020`·`FE-OC-021`·`FE-OC-024` 에 **기여**한다: (a) `FE-OC-020` 의 gate/fixture/artifact 분리에 a11y gate(`FE-GATE-009`) 와 그 fixture·artifact 를 공급, (b) `FE-OC-021` 의 context 동반 측정 NFR 에 `FE-NFR-009`(axe critical/serious 0) 를 공급, (c) `FE-OC-024` sample route 를 a11y 증거의 측정 대상으로 사용, (d) `FE-OC-019` browser 안전 경계(untrusted HTML 금지) 위에서만 접근 가능한 콘텐츠를 렌더한다는 전제를 명문화.
- 완료의 measurable 정의(§20): **axe + keyboard/focus manual evidence for sample routes**. automated(axe) 와 manual(keyboard/focus/screen-reader) 두 증거를 모두 요구한다.
- 이슈: 없음 (스캐폴딩 단계)
- PR: 없음
<!-- section-id: branch-scope -->
## 범위
### 포함 범위
- hub §10.3 accessibility baseline 요구의 계약화: keyboard 도달성, visible focus, route 변경 후 deterministic focus target, loading state 의 live region + 반복 announcement 억제, error 의 programmatic association, color 단독 금지, modal focus trap/restore, reduced-motion 존중.
- automated axe gate 설정: severity threshold(critical/serious = 0), 측정 대상(sample route), artifact(`pnpm test:a11y``artifacts/tests/a11y.json`), 컴포넌트 수준 a11y fixture(`pnpm test:component` 의 a11y fixtures → `artifacts/tests/component.xml`).
- manual keyboard/focus/screen-reader 체크리스트 + 증거 형식(`FE-GATE-009` 의 "signed manual review").
- hub §9.1 async surface state(§9.1 표)의 **a11y 표현 semantics**(live-region/focus attribute) — state 모델 자체가 아니라 그 위의 a11y hook.
### 제외 범위
> 의도적으로 제외. 다른 owner branch 소유이므로 여기서 detail 을 정하지 않고 그 branch 를 가리킨다(CLAUDE.md §15.5 R3).
- async surface 에 *어떤 state 가 존재하고 언제 전이하는가* → [[raw/branch-notes/feature-async-ui-state-contract]] (`FE-OC-011`) 소유. 본 branch 는 state 목록을 consume 만 한다.
- CI gate orchestration / gate·fixture·artifact 분리 프레임워크 → [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`) 소유. 본 branch 는 a11y gate 의 내용물만 공급.
- untrusted HTML injection 금지·sanitization·CSP → [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] (`FE-OC-019`) 소유. a11y 는 "정제된 콘텐츠" 전제만 소비.
- color contrast token 값 / design token → [[raw/branch-notes/feature-tailwind-design-token-styling-contract]] (`FE-OC-021` 기여) 소유. 본 branch 는 "color 를 state 의 유일 신호로 쓰지 않는다" 규칙만.
- render/error boundary 배치 → `feature-frontend-render-recovery-boundary-contract` (`FE-OC-015`) 소유.
- Web Vitals/performance NFR 측정 machinery → `feature-web-vitals-performance-budget-contract` (`FE-OC-021`) 소유. axe NFR 은 a11y 가, 측정 컨텍스트 규약은 그 branch 가.
- 제품별 실제 화면 구현과 실제 audit 결과의 verified 승격.
## 근거 (필수, 최소 1개+)
| Source | 정당화하는 결정 |
|---|---|
| [[raw/official-docs/react-ui-library-official]] | D3·D4 — a11y attribute 가 부착되는 React 컴포넌트 구조의 source. **a11y 규칙 자체의 근거는 아님**(a11y 규칙은 hub §10.3). |
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §10.3 | D1~D6 — accessibility baseline planned 요구(keyboard/focus/live-region/programmatic association/color/focus trap/reduced-motion/axe threshold)의 primary 근거 + "automated axe ≠ manual review" + "WCAG 미주장" 경계. |
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §9.1 | D3 — async surface required/non-blocking state 모델(a11y hook 을 부착할 대상). |
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §14.2·§14.3·§15 | D1·D2·D4 — `FE-NFR-009`(axe critical/serious 0, sample routes), `pnpm test:a11y``artifacts/tests/a11y.json`, `FE-GATE-009`(axe + signed manual review). |
| axe-core `doc/API.md` (external research, 2026-07-19) — https://github.com/dequelabs/axe-core/blob/develop/doc/API.md | D1 — impact severity taxonomy. verbatim: *"How serious the violation is. Can be one of 'minor', 'moderate', 'serious', or 'critical'."* 또한 verbatim: *"Axe does not test hidden regions, such as inactive menus or modal windows."* ⚠️ 아직 `raw/official-docs/` 미아카이브 → follow-up: `wiki-source-summarizer``raw/official-docs/axe-core-official.md` 아카이브 권고. |
## TODO
- [ ] automated axe gate 설정(severity threshold critical/serious=0 + sample-route scope + `a11y.json` artifact) 명세 — 등급: `planned`
- [ ] manual keyboard/focus/screen-reader 체크리스트 + signed evidence 형식 설계 — 등급: `planned`
- [ ] hub §9.1 async state 별 live-region/focus a11y semantics 표 작성 — 등급: `planned`
- [ ] reduced-motion + color-signal 규칙 명세 — 등급: `planned`
- [ ] evidence-grade boundary(WCAG 미주장, planned 유지) 문서화 — 등급: `planned`
## 진행 중 메모
- `/branch-spec` 로 hub §10.3/§9.1/§14/§15 + axe-core `doc/API.md`(1건 bounded research) 근거로 채움. frontend 코드는 아직 존재하지 않으므로 모든 항목 `planned`.
- axe severity(critical/serious/moderate/minor) 정의는 axe-core 문서로 grounding. axe 는 hidden region(inactive menu/modal)을 검사하지 않는다는 점이 manual review 필수성의 기술적 근거 하나.
## 결정 사항
> 아래 Decision Evidence Map 의 prose mirror. 근거는 hub §10.3/§9.1/§14/§15 + axe-core `doc/API.md`.
- **D1**: automated a11y gate 는 axe 를 사용하고 impact `critical`·`serious` violation 0 을 sample route 에서 blocking default 로 한다(`moderate`/`minor` 는 report-only backlog). / 이유: hub §10.3 이 axe critical/serious 0 을 blocking 으로 규정하고 `FE-NFR-009` 가 이를 NFR 로 고정 / 검토한 대안: 전면 manual audit(느리고 결정론 재현 불가) / 근거: hub §10.3·§14.2 + [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D022 + axe-core `doc/API.md`.
- **D2**: automated axe 통과는 완료 판정을 단독으로 만들 수 없다 — axe(automated) + keyboard/focus/screen-reader(manual) 두 증거를 모두 요구한다. / 이유: hub §10.3 "automated axe 통과는 manual review 를 대체하지 않는다" + axe 가 hidden region 을 검사하지 않음 / 검토한 대안: automated-only(위양성 안심) — 거부 / 근거: hub §10.3·§20.
- **D3**: async surface(§9.1)의 각 visible state 에 a11y 표현 semantics 를 부착한다(initial-loading = skeleton, focus theft 금지 / refreshing = subtle live region, 반복 announcement 억제 / terminal-error = programmatic 연결 + action focus). state 모델 자체는 async-ui branch 소유이고 본 branch 는 그 hook 만 소유. / 이유: hub §9.1 state 표 + §10.3 live-region/association 요구 + async-ui branch 의 명시적 위임 / 근거: hub §9.1·§10.3 + [[raw/branch-notes/feature-async-ui-state-contract]] (`FE-OC-011`).
- **D4**: keyboard/focus baseline — 모든 interactive action 이 keyboard 로 도달, visible focus indicator, route 변경 후 deterministic focus target, modal focus trap + restore. / 이유: hub §10.3 planned 요구 + `FE-GATE-006` 컴포넌트 gate 의 keyboard 축 / 근거: hub §10.3·§15.
- **D5**: axe 로 잡히지 않는 신호 — prefers-reduced-motion 존중 + color 를 state 의 유일 신호로 쓰지 않음(icon/text 병행). color token 값 자체는 tailwind branch 위임. / 이유: hub §10.3 / 근거: hub §10.3.
- **D6**: evidence-grade boundary — repo 실행 증거 없이는 WCAG 적합을 주장하지 않고 모든 a11y 주장을 `planned` 로 유지하며, 외부 답변에서 목표 수치를 측정 결과처럼 말하지 않는다(`FE-OC-001`·`FE-OC-026`·§16 answer boundary). / 이유: hub §10.3 "WCAG 적합성은 실제 audit 없이 주장 금지" / 근거: hub §10.3·§2.1.
## 결정-근거 매핑
> 각 결정의 근거 claim 과 선택 조건. `Decision ID` 는 이 note 안에서 안정.
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | axe automated gate: impact `critical`·`serious` violation 0 을 sample route 에서 blocking default, `moderate`/`minor` 는 report-only backlog (`FE-OC-020`·`FE-OC-021` 기여, `FE-NFR-009`) | sample route 가 존재하는 한 axe blocking default / organization test platform 이 axe 를 대체하거나 더 엄격한 threshold 를 강제하면 재검토(test stack revisit trigger) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §10.3 (axe critical/serious 0 blocking)·§14.2 `FE-NFR-009`·FE-D022 (test stack incl. axe); axe-core `doc/API.md` impact taxonomy("minor/moderate/serious/critical") | `project-decision` + `conditional-default (test stack)` + `official-doc (axe severity)` | axe automated 는 a11y 이슈의 일부만 포착(→ D2 manual 필수). `moderate`/`minor` backlog 처리 정책과 rule-set 튜닝 미확정 |
| D2 | 완료 판정 = axe(automated) **AND** keyboard/focus/screen-reader(manual) 이중 증거. automated pass 단독으로 완료 주장 금지 (`FE-OC-020` 기여) | 모든 a11y 완료 판정에서 불변 — 대안 없음(hub §10.3 문장 + axe 가 hidden region 미검사) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §10.3 ("automated axe 통과는 manual keyboard/screen-reader review 를 대체하지 않는다")·§20 measurable completion("axe + keyboard/focus manual evidence"); axe-core `doc/API.md`("Axe does not test hidden regions") | `project-decision` + `official-doc (axe scope 한계)` | manual review 는 사람 판단 → `FE-GATE-009` 의 "signed manual review" artifact 형식/서명 메커니즘 미확정 |
| D3 | async surface(§9.1) state 별 a11y 표현: initial-loading=skeleton·focus theft 금지 / refreshing=subtle live region·반복 announcement 억제 / stale-degraded=stale 안내·manual retry 도달 / terminal-error=programmatic 연결·action focus / mutation-pending=aria-busy·중복 차단 (`FE-OC-011` consume) | async surface(원격 데이터 view)가 존재하는 한 적용 / 순수 정적 view(원격 데이터 없음)엔 async a11y hook 불필요. state 목록/전이가 바뀌면 async-ui owner 를 따라 재정렬 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §9.1 (visible state 표)·§10.3 (loading live region + 반복 announcement 억제, error programmatic association); [[raw/branch-notes/feature-async-ui-state-contract]] `FE-OC-011` 위임(async 는 a11y hook point 만 노출) | `project-decision` (cross-branch delegation) | aria-live politeness(polite vs assertive) 와 announcement debounce 메커니즘은 hub 미규정 → §구현 가이드 UNSUPPORTED_IMPL |
| D4 | keyboard/focus baseline: 모든 interactive action keyboard 도달 + visible focus + route 변경 후 deterministic focus target + modal focus trap/restore (`FE-OC-020` 기여, `FE-GATE-006` keyboard 축) | 모든 interactive/route surface 에 적용 / 대안 없음(§10.3 planned 요구) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §10.3 (keyboard/visible focus/deterministic route focus/modal trap·restore)·§15 `FE-GATE-006` | `project-decision` | manual keyboard walk-through 는 automated 로 완전 대체 불가. route 변경 시 focus target 선택 규칙(main landmark vs heading)은 §10.3 미규정 → UNSUPPORTED_IMPL |
| D5 | prefers-reduced-motion 존중 + color 단독 state 신호 금지(icon/text 병행). color contrast token 값은 tailwind branch 위임 | 항상 적용 / 대안 없음(§10.3) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §10.3 (reduced-motion 존중, color 만으로 state 구분 금지) | `project-decision` | reduced-motion 적용 범위(어떤 animation/transition)는 컴포넌트별. color contrast 값은 [[raw/branch-notes/feature-tailwind-design-token-styling-contract]] (`FE-OC-021` 기여) 소유 — 위임 |
| D6 | evidence-grade boundary: repo 증거 없이 WCAG 적합 미주장, a11y 주장 `planned` 유지, 외부 답변에서 목표를 측정치처럼 표현 금지 (`FE-OC-001`·`FE-OC-026`) | repo evidence 없는 한 불변 / 실제 audit 후에만 conformance 주장 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §10.3 ("WCAG 적합성은 실제 audit 없이 주장하지 않는다")·§2.1 `FE-OC-001`("repo evidence 없이 완료 주장 MUST NOT")·`FE-OC-026` | `project-decision` (evidence invariant) | N/A (usage boundary). 다만 §16 answer boundary 를 파생 산출물에서 준수해야 함 |
## 구현 가이드
> `planned` blueprint. frontend 코드가 없으므로 경로/명령은 hub §14/§15 가 고정한 planned anchor 다. 3-rule(R1 Trace / R2 UNSUPPORTED_IMPL_DECISION / R3 OUT_OF_BRANCH_SCOPE) 준수.
### 1. Automated axe gate — severity threshold · scope · artifact
> **Trace**: D1 + `FE-OC-020`·`FE-OC-021` (기여) + [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §10.3·§14.2 `FE-NFR-009`·§14.3 `pnpm test:a11y`·FE-D022.
>
> - **`a11y.json` 스키마는 해소됨(2026-07-21)**: hub §2.1.3 `ART-FE-004@1` 로 등록됐고 **Schema Owner 는 본 branch** 다(`harness/source/artifact-schemas/ca-skeleton-frontend/a11y-report.schema.json`). impact 어휘는 axe-core 4단계를 그대로 쓰고, `blockingCount`(serious·critical)가 0 이 아니면 `FE-GATE-009` FAIL 이다.
> - **UNSUPPORTED_IMPL_DECISION**: axe integration 메커니즘(@axe-core/playwright 로 route-level e2e-a11y + vitest-axe 로 component-level) 과 rule-set config 는 hub 가 규정하지 않음. Trade-off: FE-D022 의 Playwright+RTL 스택과 정합을 위해 위 조합을 제안하되, 최종 runner binding 은 test-taxonomy owner 확정에 위임.
| 항목 | planned 값 | 근거 |
|---|---|---|
| 대상 scope | sample route (제품 route 아님) | §14.2 `FE-NFR-009` context = sample routes |
| blocking severity | impact ∈ {`critical`, `serious`} → fail | §10.3 + axe-core impact taxonomy |
| non-blocking severity | impact ∈ {`moderate`, `minor`} → report-only backlog | axe-core impact taxonomy(4단계) |
| route-level 실행 | `pnpm test:a11y``artifacts/tests/a11y.json` | §14.3 planned command 표 |
| component-level 실행 | `pnpm test:component` 의 a11y fixtures → `artifacts/tests/component.xml` | §14.3(component = async/error/**a11y** fixtures) |
> **R3 위임**: a11y gate 를 CI 파이프라인에 blocking gate 로 배선하는 orchestration 은 [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`) 소유. 본 절은 gate 의 *내용물*(scope/severity/artifact)만 확정.
### 2. Manual keyboard / focus / screen-reader checklist + evidence format
> **Trace**: D2 + D4 + [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §10.3·§15 `FE-GATE-009`("signed manual review").
>
> - **UNSUPPORTED_IMPL_DECISION**: manual review record 의 경로/포맷(예: `artifacts/tests/a11y-manual/<route>.md`)과 "signed" 메커니즘(리뷰어 서명 방식)은 hub 가 "signed manual review" 라고만 하고 스키마를 규정하지 않음. Trade-off: sample route 당 markdown record 를 `a11y.json` 옆에 co-locate 제안, 최종 경로는 test-taxonomy owner 확정에 위임.
체크리스트 항목(§10.3 요구와 1:1):
| # | 수동 검증 항목 | 통과 기준 |
|---|---|---|
| M1 | keyboard 로 모든 interactive action 도달 | 마우스 없이 전 action 실행 가능 |
| M2 | visible focus indicator | 모든 focusable 요소에 시각적 focus 표시 |
| M3 | route 변경 후 deterministic focus target | route 전환 시 focus 가 정해진 지점으로 이동 |
| M4 | modal focus trap + restore | modal 내부 trap, 닫으면 트리거로 focus 복귀 |
| M5 | error 의 programmatic association | error 메시지가 관련 control 과 aria 로 연결 |
| M6 | color 단독 금지 | state 가 색 외 신호(icon/text)도 가짐 |
| M7 | reduced-motion 존중 | prefers-reduced-motion 시 애니메이션 축소 |
### 3. Async surface a11y semantics (live-region + focus for §9.1 states)
> **Trace**: D3 + `FE-OC-011` (async-ui branch 에서 consume) + [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §9.1·§10.3.
>
> - **UNSUPPORTED_IMPL_DECISION**: aria-live politeness(polite/assertive) 와 반복 announcement 억제(debounce/dedupe) 메커니즘은 §10.3 이 "적절한 live region, 반복 announcement 억제" 원칙만 규정하고 구현 detail 미규정. Trade-off: 기본 `polite` + message-key dedupe, `terminal-error` 만 `assertive` 제안.
| §9.1 state | a11y 표현 요구 | 근거 |
|---|---|---|
| `initial-loading` | 안정적 skeleton, focus theft 금지 | §9.1·§10.3 |
| `refreshing` | 기존 콘텐츠 유지 + subtle live region, 반복 announcement 억제 | §9.1·§10.3 |
| `stale-degraded` | stale 안내 announce + manual retry 를 keyboard 로 도달 | §9.1·§10.3 |
| `terminal-error` | 안전 메시지의 programmatic 연결 + registry action 에 focus | §9.1·§10.3 |
| `mutation-pending` | `aria-busy`/disabled 로 중복 action 차단 announce | §9.1 |
> **R3 위임**: 위 state 가 *존재하는지·언제 전이하는지*는 [[raw/branch-notes/feature-async-ui-state-contract]] (`FE-OC-011`) 소유. 본 절은 그 state 위의 a11y attribute 만 명세(그 branch 가 "a11y hook point 만 노출"이라 위임함).
### 4. Reduced-motion + color-signal (axe 로 잡히지 않는 신호)
> **Trace**: D5 + [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §10.3.
>
> - **UNSUPPORTED_IMPL_DECISION**: reduced-motion 을 적용할 animation 범위는 §10.3 이 원칙만 규정하고 열거하지 않음. Trade-off: loading skeleton + route transition 에 우선 적용, 컴포넌트별 애니메이션은 각 컴포넌트 owner 에 위임.
- `prefers-reduced-motion: reduce` 시 skeleton/route transition 애니메이션 축소 또는 제거.
- state 는 색 외에 icon/text 신호를 병행(color 단독 금지).
> **R3 위임**: color contrast token 값(대비비 등)은 [[raw/branch-notes/feature-tailwind-design-token-styling-contract]] (`FE-OC-021` 기여) 소유. 본 절은 "color 를 유일 신호로 쓰지 않는다" 규칙만.
### 5. Evidence-grade boundary
> **Trace**: D6 + [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §10.3·§2.1 `FE-OC-001`·`FE-OC-026`.
- repo 가 axe + manual 을 실행해 artifact 를 낼 때까지 모든 a11y 주장은 `planned`. WCAG 적합(conformance) 문구를 쓰지 않는다.
- 파생 산출물/외부 답변에서 목표 수치(axe 0, WCAG AA 등)를 측정 결과처럼 표현하지 않는다(§16 answer boundary). (본 절은 boundary 규칙이므로 별도 impl detail 없음.)
## 엣지·실패·의존
- **실패·엣지 경로**:
- dynamic content 변경(route 전환·async state 전이) 시 focus/live-region 이 결정론적으로 발화하지 않으면 screen-reader 사용자가 맥락을 잃음 → §구현 가이드 3 의 live-region + M3 deterministic focus 로 방지.
- modal 닫힘 시 focus restore 실패 → 트리거 복귀 검증(M4).
- hidden region(inactive menu/modal)은 axe 가 검사하지 않음(axe-core `doc/API.md`) → 렌더/활성화 후 재실행하는 fixture 필요.
- 잦은 refetch 시 live-region announcement storm → politeness/dedupe(§구현 가이드 3 UNSUPPORTED_IMPL).
- reduced-motion 미존중 → vestibular 부담(M7).
- **다른 계약 의존**:
- [[raw/branch-notes/feature-async-ui-state-contract]] `FE-OC-011` 에 의존 — async surface state 목록/전이를 consume. 그 state 모델이 바뀌면 본 branch 의 a11y hook 이 재정렬됨.
- [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] `FE-OC-024` 에 의존 — a11y 증거를 측정할 sample route 가 생기기 전엔 검증 불가.
- [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] `FE-OC-020` 에 의존 — a11y gate 를 blocking gate 로 배선/artifact 보존하는 orchestration owner.
- [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] `FE-OC-019` 에 의존 — untrusted HTML 금지 전제. a11y 는 정제된 콘텐츠만 렌더한다고 가정.
- [[raw/branch-notes/feature-tailwind-design-token-styling-contract]] — color contrast token 값 소유(`FE-OC-021` 기여). color-not-sole 규칙만 본 branch.
## 검증해야 할 주장
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
| sample route 에서 axe critical/serious violation 0 | 구현·gate 미존재 | `pnpm test:a11y``artifacts/tests/a11y.json` 결과가 critical/serious 0 | `needs-confirmation` |
| 모든 interactive action 이 keyboard 로 도달 | 화면 미구현 | sample route manual keyboard walk-through + signed record(M1) | `needs-confirmation` |
| route 변경 후 focus 가 deterministic target 으로 이동 | 라우팅 a11y 미구현 | component/e2e focus 이동 test(M3) | `needs-confirmation` |
| async state 전이가 live-region 으로 announce 되되 storm 없음 | live-region 정책 미확정 | component a11y fixture(aria-live assertion + dedupe) `pnpm test:component` | `needs-confirmation` |
| modal focus trap + restore 동작 | modal 미구현 | component test(trap 내부 + 닫힘 시 트리거 복귀, M4) | `needs-confirmation` |
| prefers-reduced-motion 이 존중됨 | 애니메이션 미구현 | media-query 기반 manual/자동 test(M7) | `needs-confirmation` |
## 관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)
- 스캐폴딩 단계: `/coverage` 실행 전 수동 행을 만들지 않는다.
## 마주친 문제
- 없음 — 스캐폴딩 단계.
## 묶음 (이 branch에서 파생된 자료)
<!-- GENERATED: project-contract-imports:start -->
## 가져온 프로젝트 계약
| Ref | Owner | 요약 | Branch 적용 |
|---|---|---|---|
| `FE-GATE-006@1` | [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] | component 레벨이 실패하면 merge 를 MUST 차단 | import 참조로 적용 |
| `FE-OC-001@1` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | 모든 구현 주장은 evidence grade를 MUST 표시하고 repo evidence가 없는 상태에서 구현 완료를 MUST NOT 주장 | import 참조로 적용 |
| `FE-OC-011@1` | [[raw/branch-notes/feature-async-ui-state-contract]] | async surface는 initial-loading, success, empty, terminal-error를 MUST 표현 | import 참조로 적용 |
| `FE-OC-020@1` | [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] | gate 종류별 책임·fixture·artifact를 분리하고 실패를 warning으로 낮추면 안 됨 | import 참조로 적용 |
| `FE-OC-021@1` | [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] | NFR은 device/network/cache/build context와 함께 MUST 측정 | import 참조로 적용 |
| `FE-OC-026@1` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | 외부 답변은 evidence grade를 MUST 보존하고 목표 수치를 측정 결과처럼 말하면 안 됨 | import 참조로 적용 |
<!-- GENERATED: project-contract-imports:end -->
- 없음 — 자식 자료는 생성 후 controller가 parent Cluster와 함께 등록한다.
### 블로그·채용공고 연계 글감
- follow-up 후보: `raw/official-docs/axe-core-official.md` 아카이브(현재 inline research 로만 인용). 생성 시 D1·D2 Supporting Claim 을 wikilink 로 승격.
## 관련 일일 노트
- 없음 — daily note는 이 작업에서 수정하지 않는다.
## 완료 후 정리
- PR 링크: 없음
- 리뷰 메모: 없음
- 머지 결과 / 배포 환경: `planned`
- **wiki 추출 대상** (verified만): 없음
- **추출하지 않을 항목** (planned / documented-only / abandoned): 현재 전체