Files
llm-wiki/raw/branch-notes/feature-tailwind-design-token-styling-contract.md
T

310 lines
30 KiB
Markdown

---
title: branch / feature-tailwind-design-token-styling-contract
source_type: branch-note
status: raw
branch: feature-tailwind-design-token-styling-contract
parent_branch:
related_projects: [ca-skeleton-frontend, ca-skeleton]
governing_docs: [raw/project-notes/ca-skeleton-frontend-operational-contract.md]
tags: [branch, ca-skeleton, frontend, tailwind, react]
created: 2026-07-18
target_merge:
status_label: in-progress
id: BR-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-018
kind: project-work-item
project: ca-skeleton-frontend-operational-contract
work_item: WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-018
inherits: [DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-STYLING-001@1]
refines: []
overrides: []
depends_on: [WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-001]
contract_packet: 1
contract_packet_sha256: 8425a0ae2048fd82fe493415296631e7d440d81a443d4555e32bb00e37e64f3f
imports: [FE-OC-011@1, FE-OC-018@1, FE-OC-019@1, FE-OC-020@1, FE-OC-021@1, FE-OC-024@1]
delegates: [DELEG-FE-001@1]
accepts_delegations: [DELEG-FE-004@1]
---
# branch: feature-tailwind-design-token-styling-contract
> Layer: `raw/branch-notes/` — 단일 브랜치의 TODO·결정·진행 기록. 머지/종료 후 verified 결과는 `/ingest`로 `wiki/projects/`에 추출한다.
<!-- section-id: branch-parent -->
## 부모 (필수)
- [[raw/project-notes/ca-skeleton-frontend-operational-contract]]
<!-- GENERATED: branch-contract:start -->
<!-- section-id: branch-contract-packet -->
## 브랜치 계약 패킷
- **생성 시 프로젝트 개정**: `1`
- **패킷 스키마**: `contract_packet: 1`
- **완료 조건**: theme token·arbitrary value policy·sample UI가 검증된다
<!-- 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]] |
<!-- 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 -->
## 목표
`ca-skeleton-frontend`의 styling 결정 `FE-D005`("styling default는 Tailwind theme token + component primitive")를 되묻지 않아도 코드를 작성할 수 있는 implementation-ready styling contract로 내린다. 이 branch는 §20 Branch Decomposition에서 **Primary contract IDs `—`** 인 기여형 branch로, 자체 `FE-OC-*` owner는 아니지만 세 project-wide contract에 **contributes-to**로 참여한다: `FE-OC-011`(async surface의 시각 primitive), `FE-OC-019`(browser bundle에 untrusted class 주입 금지), `FE-OC-021`(token 제약이 CSS surface·CLS budget에 미치는 영향). 근거 결정은 [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D005`(conditional-default)이며, 공식 근거는 [[raw/official-docs/tailwind-css-utility-first-official]]의 `TAILWIND-UTIL-C1`(제약된 primitive 집합), `TAILWIND-UTIL-C2`(마크업 내 single-purpose utility class), `TAILWIND-UTIL-C4`(predefined design system → magic number 방지·시각 일관성)이다. Measurable completion(§20)은 "theme tokens + arbitrary value policy + sample UI", Priority는 P3, Dependency는 [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]](FE-OC-003 toolchain 그릇이 선행). 현재 frontend repository가 존재하지 않으므로 본 노트의 모든 구현 주장은 `planned` 등급이다.
- 이슈:
- PR:
<!-- section-id: branch-scope -->
## 범위
### 포함 범위
- **디자인 토큰 layer** — color/spacing/typography/radius 등 시각 상수를 theme token으로 정의(시각 상수 SSOT). Tailwind theme config 위치와 global stylesheet entry 확정 — 등급: `planned` (`FE-D005`, `TAILWIND-UTIL-C4`; token scale 값은 UNSUPPORTED_IMPL — archived doc가 scale 미정의)
- **arbitrary value policy** — 마크업 magic-number 금지·token 강제, `[...]` arbitrary value는 bounded·reviewed escape hatch, 재발 값은 token 승격 — 등급: `planned` (`FE-D005`, `TAILWIND-UTIL-C4`)
- **component primitive 어휘** — async surface state(initial-loading skeleton / empty / terminal-error)의 token-driven 시각 primitive 정의 — 등급: `planned` (`FE-OC-011` 기여, 근거 §9.1)
- **정적 class 구성 규율** — class name은 compile-time/static, untrusted·runtime-interpolated class 문자열 및 styling 목적 `dangerouslySetInnerHTML` 금지 — 등급: `planned` (`FE-OC-019` 기여, 근거 §13.2)
- **sample UI fixture** — token·primitive 사용을 시연하는 제거 가능한 fixture(product import 금지) — 등급: `planned` (`FE-OC-024` 협업, `FE-D025` 원칙)
### 제외 범위
> 의도적으로 제외한 것. 다른 owner branch 소유 관심사이며 본 §구현 가이드에 detail을 남기지 않는다 (CLAUDE.md §15.5 R3).
- **async surface state machine·required-state 정의·상태 전이** — owner [[raw/branch-notes/feature-async-ui-state-contract]] (`FE-OC-011`). 본 branch는 token-driven 시각 primitive 어휘만 소유하고 어떤 state가 required인지·전이는 위임.
- **CSP/header/secret scan/prohibited-import 강제** — owner [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] (`FE-OC-019`). 본 branch는 class-construction 규율(정책)만 정의.
- **CSS 번들 측정·threshold·web vitals 계측** — owner [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] (`FE-OC-021`) + [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] (`FE-OC-018`). 본 branch는 token 제약으로 기여만.
- **전체 sample feature slice 계약·removal smoke** — owner [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] (`FE-OC-024`). 본 branch는 styling 시연분만.
- **axe/keyboard/contrast a11y baseline** — owner [[raw/branch-notes/feature-accessibility-baseline-contract]] (`FE-OC-020` 협업). 단 "color만으로 state 구분 금지"(§10.3)는 token 설계 시 준수.
- **arbitrary-value·prohibited-import lint rule 구현(강제)** — owner [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] + [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`). 본 branch는 정책만 정의, 강제 tooling은 위임.
- **Vite/PostCSS toolchain·build baseline 자체** — owner [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] (`FE-OC-003`). 본 branch는 그 그릇에 Tailwind config를 plug할 뿐 build 파이프라인은 소유하지 않음.
## 근거 (필수, 최소 1개+)
| Source | 정당화하는 결정 |
|---|---|
| [[raw/official-docs/tailwind-css-utility-first-official]] | D1·D2·D3 — utility-first = 제약된 primitive 집합(`TAILWIND-UTIL-C1`), 마크업 내 single-purpose utility class 조합(`TAILWIND-UTIL-C2`), inline style과 달리 predefined design system에서 값 선택 → magic number 방지·시각 일관성(`TAILWIND-UTIL-C4`). styling default = Tailwind theme token + primitive 및 token 강제 policy의 공식 근거 |
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | D1~D7 governing SSOT — Decision Register(`FE-D005`), contract index(`FE-OC-011`/`FE-OC-019`/`FE-OC-021`/`FE-OC-024`), async surface state model(§9.1), a11y baseline(§10.3), browser security boundary(§13.2), NFR matrix(§14.2), directory blueprint(§4.6), sample-fixture 원칙(`FE-D025`) |
| [[raw/project-notes/ca-skeleton-operational-contract]] | D7의 상위 철학 precedent — backend skeleton의 "sample = 제거 가능 contract fixture" 원칙을 styling sample UI에 적용 (사실 인용이 아닌 rationale precedent) |
## TODO
- [ ] 디자인 토큰 layer 정의(color/spacing/typography/radius 등 category) + Tailwind theme config 위치·global stylesheet entry 확정 — 등급: `planned`
- [ ] arbitrary value policy 문서화(token 강제 + `[...]` escape allowlist + recurring→promote 규칙) — 등급: `planned`
- [ ] component primitive 어휘(skeleton/empty/terminal-error) token-driven 시각 명세 — 등급: `planned`
- [ ] sample UI fixture(토큰·primitive 시연, removable, product import 금지) 설계 — 등급: `planned`
- [ ] 정적 class 구성 규율 명세 + browser-security/lint owner 위임 링크 배선 — 등급: `planned`
- [ ] `/docs/theme` 페이지를 raw-source로 발췌해 token scale 근거 보강 — 등급: `needs-confirmation`
## 진행 중 메모
`/branch-spec` 채움 완료(2026-07-19). frontend repository 미생성 — 전 항목 `planned`. archived Tailwind doc(v4.3)은 utility-first 철학·magic-number 방지만 증명하고 token scale·purge·번들 크기는 미증명(C1/C4 boundary) → 해당 detail은 `UNSUPPORTED_IMPL_DECISION` 라벨 또는 owner 위임으로 분리했다. 실제 코드 착수 전까지 evidence 등급 상향 금지.
## 결정 사항
> 각 결정은 아래 Decision Evidence Map의 prose 미러이다. 근거는 Sources 또는 hub Decision Register를 가리킨다.
- 2026-07-18: styling default를 **Tailwind utility-first + theme-token layer + component primitive**로 채택 / 이유: 제약된 primitive 집합과 predefined design system이 magic number를 막고 시각 일관성을 확보 / 검토한 대안: CSS Modules·CSS-in-JS·plain CSS / 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D005`, [[raw/official-docs/tailwind-css-utility-first-official]] `TAILWIND-UTIL-C1`·`TAILWIND-UTIL-C2`·`TAILWIND-UTIL-C4`. (conditional-default)
- 2026-07-18: **design token layer를 시각 상수 SSOT**로 두고 raw 값 하드코딩을 대체 / 이유: inline style의 magic number를 predefined design system 값 선택으로 대체(C4) / 검토한 대안: 컴포넌트별 임의 값·글로벌 CSS 변수만 사용 / 근거: [[raw/official-docs/tailwind-css-utility-first-official]] `TAILWIND-UTIL-C4`, [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D005`. (token scale 값은 UNSUPPORTED_IMPL)
- 2026-07-18: **arbitrary value policy** — token 강제, `[...]`는 bounded escape hatch, 재발 값은 token 승격 / 이유: escape 상시화 시 magic number가 재유입되어 C4 이점이 무력화 / 검토한 대안: 무제한 arbitrary value 허용 / 근거: [[raw/official-docs/tailwind-css-utility-first-official]] `TAILWIND-UTIL-C4`. (강제 tooling은 `FE-OC-020` 위임)
- 2026-07-18: async surface state의 **token-driven 시각 primitive 어휘**를 본 branch가 소유하되 state machine은 위임 / 이유: 시각 표현과 상태 소유의 경계 분리 / 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-OC-011`, §9.1. (delegation boundary)
- 2026-07-18: **정적 class 구성 규율**(no runtime/untrusted class string, no styling `dangerouslySetInnerHTML`) / 이유: browser bundle은 public artifact이며 untrusted 주입은 default 금지(§13.2) / 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-OC-019`, §13.2. (강제는 browser-security owner 위임)
- 2026-07-18: **token 제약이 perf budget에 기여**(tokenized sizing→CLS 안정, bounded 어휘→CSS surface 억제) / 이유: 시각 상수 재사용이 layout·번들 예측성을 높임 / 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-OC-021`, §14.2. (측정·purge·threshold는 owner 위임)
- 2026-07-18: **sample UI를 제거 가능한 fixture**로 제공(product import 금지) / 이유: backend skeleton의 sample-fixture 원칙 적용 / 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-OC-024`·`FE-D025`. (removal smoke는 sample-slice owner 위임)
## 결정-근거 매핑
> 각 결정과 raw source Claim ID의 연결. `Decision ID`(D1~D7)는 본 노트 안에서 안정적으로 유지한다.
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | styling default = Tailwind utility-first + theme-token layer + component primitive (`FE-D005`; 기여 `FE-OC-011`·`FE-OC-019`·`FE-OC-021`) | 정적 utility 컴파일 + build-time theme token이 디자인 요구를 충족하는 동안 → Tailwind theme token. runtime theming(사용자 런타임 테마 전환) 또는 product design system이 다른 compiler를 요구 → `FE-D005` revisit(다른 styling 엔진 재평가) | [[raw/official-docs/tailwind-css-utility-first-official]] `TAILWIND-UTIL-C1`, `TAILWIND-UTIL-C2`, `TAILWIND-UTIL-C4`; [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D005` | `official-doc` + `conditional-default` (project-decision) | archived doc는 성능/번들 이점을 미증명(C1 boundary); Tailwind 채택이 이 프로젝트 생산성·유지보수를 개선하는지 실측 필요 |
| D2 | design token layer(color/spacing/typography/radius…)를 시각 상수 SSOT로 정의, magic number 대체 (`FE-D005` / `TAILWIND-UTIL-C4`) | 값이 팀 공유 시각 상수인 동안 → theme token 등록. 일회성·컴포넌트 로컬 값이면 → 컴포넌트 스코프 유지(token 오염 방지). token은 hub §5 8-registry 밖 신규 domain이므로 governance는 registry-governance와 협의 | [[raw/official-docs/tailwind-css-utility-first-official]] `TAILWIND-UTIL-C4`; [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D005`, §5 | `official-doc`(원칙) + `project-decision`(신규 제안); token scale 값 = UNSUPPORTED_IMPL | archived doc가 spacing/color scale 구조 미정의(C4 boundary) → `/docs/theme` 별도 raw 필요; token registry가 hub §5 8-registry에 부재(신규 제안) |
| D3 | arbitrary value policy — 마크업 magic-number 금지·token 강제, `[...]`는 bounded·reviewed escape hatch, 재발 값 token 승격 (`FE-D005` / `TAILWIND-UTIL-C4`) | 디자인 값이 token으로 표현 가능한 동안 → token. token 부재 escape가 필요하면 → allowlist 등록 후 `[...]`; 동일 arbitrary value 2회+ 재발 → token 승격(escape 상시화 금지) | [[raw/official-docs/tailwind-css-utility-first-official]] `TAILWIND-UTIL-C4`; [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-D005` | `official-doc`(원칙) + `project-decision`(정책); lint 강제 메커니즘 = UNSUPPORTED_IMPL/위임 | "allowlist 외 arbitrary value 금지" 강제 tooling 미확정(`FE-OC-020` 위임); escape 남용 감지 방법 미검증 |
| D4 | async surface state(skeleton/empty/terminal-error)의 token-driven 시각 primitive 어휘를 본 branch가 소유; state machine·required-state는 위임 (기여 `FE-OC-011`, §9.1) | 시각 표현이면 → styling branch primitive. 어떤 state가 required인지·상태 전이는 → async-ui-state owner(`FE-OC-011`). "loading boolean 하나로 empty/error/refreshing 병합 금지"(§9.1)는 state owner 계약 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-OC-011`, §9.1; [[raw/official-docs/tailwind-css-utility-first-official]] `TAILWIND-UTIL-C1` | `project-decision` (boundary/delegation) | primitive 어휘가 §9.1 4-state matrix를 실제로 커버하는지 component test 필요; "color만으로 state 구분 금지"(§10.3) 준수 여부는 a11y 협업 |
| D5 | class name은 compile-time/static; untrusted·runtime-interpolated class 문자열 금지; styling 목적 `dangerouslySetInnerHTML` 금지 (기여 `FE-OC-019`, §13.2) | 정적 class로 표현 가능한 동안 → static. 진짜 dynamic이 필요하면 → tokenized variant의 bounded allowlist를 통해 매핑(user 입력 문자열 concat 금지). CSP/scan/prohibited-import 강제는 browser-security owner 위임 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-OC-019`, §13.2 | `project-decision` (boundary) | dynamic class 요구가 실제 발생 시 allowlist 설계 미검증; 강제는 browser-security/lint owner에 의존 |
| D6 | token 제약이 perf budget에 기여 — tokenized sizing→layout 안정(CLS `FE-NFR-004` ≤0.10), bounded class 어휘→CSS surface 억제; 측정·purge·threshold는 위임 (기여 `FE-OC-021`, §14.2) | token 재사용으로 CSS surface가 bounded인 동안 → 기여 유지. 번들/CLS threshold 초과가 측정되면 → web-vitals/build owner가 budget 판정·최적화(본 branch는 token 정책 조정으로 협조) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-OC-021`, §14.2 | `project-decision` (boundary); CSS purge/content 메커니즘·번들 수치 = 위임(archived doc 미증명) | "utility 재사용→CSS 축소"·"tokenized sizing→CLS 개선"은 archived doc 미증명·프로젝트 미실측 → lab/bundle report로 검증 필요 |
| D7 | sample UI(token·primitive 시연)는 `src/sample/` 하위 제거 가능 fixture이며 product import 금지 (협업 `FE-OC-024`, `FE-D025`) | styling 시연 목적이면 → sample fixture(제거 가능). 실제 제품 화면이 되면 → 제품 feature branch 소유(본 branch out of scope). 전체 slice 계약·removal smoke는 sample-slice owner | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] `FE-OC-024`, `FE-D025`, §4.6; [[raw/project-notes/ca-skeleton-operational-contract]] | `project-decision` (boundary) + CA precedent | sample removal 시 product 무영향 검증은 sample-slice owner smoke에 의존 |
## 구현 가이드
> `planned` blueprint. 경로는 hub §4.6 planned directory blueprint에서 도출되므로 grounded이지만, frontend 코드가 없으므로 전 구간 `planned`. 각 sub-section은 CLAUDE.md §15.5 3-rule(R1 Trace·R2 UNSUPPORTED_IMPL_DECISION·R3 no OUT_OF_BRANCH_SCOPE)을 따른다.
### 1. 디자인 토큰 layer (theme token SSOT)
> **Trace**: D1(`FE-D005`) + D2(`FE-D005` / `TAILWIND-UTIL-C4`) → 기여 `FE-OC-021`. planned 경로 `src/presentation/styles/`(hub §4.6 presentation dir) 하위 theme config + global stylesheet entry.
>
> - **UNSUPPORTED_IMPL_DECISION**: theme config 파일 위치·메커니즘(Tailwind v4 CSS-first `@theme`(예: `src/presentation/styles/theme.css`) vs v3 `tailwind.config.js`) — hub 미명시, archived doc(v4.3)은 config 메커니즘 미서술. trade-off: v4.3 채택이므로 CSS-first `@theme` 우선, 착수 시 `/docs/theme` 발췌로 확정.
> - **UNSUPPORTED_IMPL_DECISION**: 각 token category의 정확한 scale 값(color palette·spacing step·type scale) — archived `TAILWIND-UTIL-C4`가 scale 구조 미정의. trade-off: 값은 임의 선택 불가 → `/docs/theme` 발췌 + 디자인 요구로 확정, 그 전까지 값 미기재.
| Token category | planned 소스 | 근거 | 소유 경계 |
|---|---|---|---|
| color | theme token | D2 (`TAILWIND-UTIL-C4`) | this branch (값 UNSUPPORTED_IMPL) |
| spacing | theme token | D2 (`TAILWIND-UTIL-C4`) | this branch (값 UNSUPPORTED_IMPL) |
| typography(font family/size/weight/line-height) | theme token | D2 (`TAILWIND-UTIL-C4`) | this branch (값 UNSUPPORTED_IMPL) |
| radius/shadow/z-index/breakpoint | theme token | D2 (`FE-D005`) | this branch (값 UNSUPPORTED_IMPL) |
| token 값 자체 | `/docs/theme` 발췌 후 | needs raw source | this branch (근거 보강 대기) |
### 2. arbitrary value policy
> **Trace**: D3(`FE-D005` / `TAILWIND-UTIL-C4`). 정책은 본 branch 소유, 강제 tooling은 `FE-OC-020` owner 위임(R3).
>
> - **UNSUPPORTED_IMPL_DECISION**: allowlist 저장 위치·형식 + lint rule 이름 — hub 미명시. trade-off: 정책 정의는 본 branch, 강제 rule은 [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] / [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`)로 위임.
| 규칙 | planned 내용 | 근거 |
|---|---|---|
| 기본 | 모든 spacing/color/typography/radius 값은 theme token utility 사용 | D2·D3 (`TAILWIND-UTIL-C4`) |
| escape 조건 | `[value]` arbitrary value는 (a) token 부재 + (b) 리뷰 승인 + (c) allowlist 등록 시에만 | D3 |
| 승격 | 동일 arbitrary value 2회+ 등장 → theme token 승격 | D3 (magic number 재유입 방지, `TAILWIND-UTIL-C4`) |
| 금지 | 무제한 arbitrary value(allowlist 밖 `[...]`) — predefined design system 무력화 | D3 (`TAILWIND-UTIL-C4`) |
### 3. component primitive 어휘 (async surface 시각) — 기여 FE-OC-011
> **Trace**: D4 → 기여 `FE-OC-011`, §9.1. planned 경로 `src/presentation/components/`(hub §4.6). R3: state machine·required-state 정의는 async-ui-state owner 위임.
>
> - **UNSUPPORTED_IMPL_DECISION**: primitive 컴포넌트 명명(예: `<Skeleton>`/`<EmptyState>`/`<ErrorSurface>`) — hub 미명시. trade-off: 명명은 임의 → 착수 시 확정하되 §9.1 required state와 1:1 매핑을 유지.
| §9.1 required state | token-driven 시각 primitive | UI 요구(§9.1) |
|---|---|---|
| `initial-loading` | skeleton primitive | 안정적 skeleton, focus theft 금지; 고정 치수 token으로 layout 안정 |
| `empty` | empty-state primitive | empty reason + primary action slot |
| `terminal-error` | error-surface primitive | safe message + registry action slot |
| `refreshing`/`stale-degraded`/`mutation-*` | non-blocking 시각 hint(subtle indicator/label) | 기존 content 유지; required 여부·의미는 state owner |
- 어떤 state가 required인지·상태 전이는 owner [[raw/branch-notes/feature-async-ui-state-contract]] (`FE-OC-011`). "loading boolean 하나로 병합 금지"(§9.1)는 state owner 계약이며 본 §에 detail 미기재(R3).
- "color만으로 state 구분 금지"(§10.3) 준수 → primitive는 아이콘/텍스트를 색과 병행. a11y 판정은 accessibility owner 협업.
### 4. 정적 class 구성 규율 — 기여 FE-OC-019
> **Trace**: D5 → 기여 `FE-OC-019`, §13.2. R3: CSP/scan/prohibited-import 강제는 browser-security owner 위임.
>
> - **UNSUPPORTED_IMPL_DECISION**: 없음 — 규율(정적 class·no runtime string·no styling `dangerouslySetInnerHTML`)은 §13.2에 grounded.
- class name은 compile-time에 결정한다; user data로 class 문자열을 concat하지 않는다.
- dynamic이 불가피하면 tokenized variant map(정적 키 → 정적 class)을 경유한다.
- styling 목적의 `dangerouslySetInnerHTML`/untrusted inline style 주입을 금지한다(§13.2).
- 위 규율의 정적 강제(prohibited-import lint, secret/injection scan)는 owner [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] (`FE-OC-019`)가 소유하며 본 §에 강제 detail 미기재(R3).
### 5. sample UI fixture — 협업 FE-OC-024
> **Trace**: D7 → 협업 `FE-OC-024`, `FE-D025`, §4.6. planned 경로 `src/sample/contract-fixture/`(hub §4.6).
>
> - **UNSUPPORTED_IMPL_DECISION**: sample UI 화면 구성·컴포넌트 목록 — hub 미명시(styling 시연 재량). trade-off: 최소 시연(token + 3개 async primitive)만 우선, 전체 slice 구성은 sample-slice owner.
- sample UI는 theme token·arbitrary value policy·async primitive를 한 화면에서 시연한다.
- removable: product 코드가 sample을 import하지 않는다(`FE-D025`).
- 전체 contract slice·sample removal smoke는 owner [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] (`FE-OC-024`)가 소유하며 본 §에 slice detail 미기재(R3).
## 엣지·실패·의존
- **실패·엣지 경로**:
- **magic number 재유입**: token 부재 값이 allowlist 없이 하드코딩/arbitrary로 등장 → policy 위반. 기대 동작: lint FAIL(강제는 `FE-OC-020` owner), 리뷰 차단.
- **arbitrary value 남용**: 동일 값 반복 escape인데 token 승격 누락 → magic number 상시화. 기대 동작: 승격 규칙(D3)으로 감지·정리.
- **dynamic class 문자열**: user input 기반 class 생성 → browser security 위반(§13.2). 기대 동작: 정적 variant map으로 대체, prohibited-import lint FAIL(강제는 `FE-OC-019` owner).
- **CLS 회귀**: skeleton/primitive 치수 불안정 → layout shift(`FE-NFR-004` ≤0.10 초과). 기대 동작: 고정 치수 token, web-vitals owner가 lab에서 측정.
- **CSS 번들 팽창**: token 미재사용·arbitrary 남발 → CSS surface 증가(`FE-NFR-001` 압박). 기대 동작: bounded 어휘 정책, build/web-vitals owner가 측정.
- **color-only state**: state를 색만으로 표현 → a11y 위반(§10.3). 기대 동작: 아이콘/텍스트 병행.
- **다른 계약 의존**:
- **상류 의존**: [[raw/branch-notes/feature-frontend-project-bootstrap-toolchain-contract]] (`FE-OC-003`) — Vite/toolchain build 그릇에 Tailwind config를 plug. 이 build baseline이 바뀌면 styling 컴파일에 영향.
- **기여(contributes-to)**: [[raw/branch-notes/feature-async-ui-state-contract]] (`FE-OC-011`)가 본 primitive 어휘를 consume; [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] (`FE-OC-019`)가 정적 class 규율을 강제; [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] (`FE-OC-021`) + [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] (`FE-OC-018`)가 CSS/CLS budget을 측정; [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] (`FE-OC-024`)가 sample removal smoke를 소유; [[raw/branch-notes/feature-accessibility-baseline-contract]] (`FE-OC-020`)가 color/contrast a11y를 판정.
- **강제 tooling 의존**: arbitrary-value·prohibited-import lint는 [[raw/branch-notes/feature-frontend-architecture-enforcement-lint-contract]] / [[raw/branch-notes/feature-frontend-test-taxonomy-contract]] (`FE-OC-020`)가 배선.
- **CA 철학 precedent**: [[raw/project-notes/ca-skeleton-operational-contract]] — "sample = 제거 가능 fixture" 원칙(사실 의존이 아닌 설계 precedent).
## 검증해야 할 주장
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
| theme token layer가 시각 상수를 실제로 SSOT화(모든 시각 값 token화, magic number 제거) | 구현·lint 미존재 | arbitrary-value lint fixture(위반 시 FAIL) + token 커버리지 grep (owner `FE-OC-020`) | `needs-confirmation` |
| arbitrary value escape가 allowlist로 bounded 유지 | 강제 tooling 미확정 | allowlist 밖 `[...]` 사용 시 lint FAIL negative fixture | `needs-confirmation` |
| primitive 어휘가 §9.1 4-state를 커버하고 "loading boolean 병합 금지"를 준수 | component 미존재 | async-ui-state component state matrix test와 cross-ref (owner `FE-OC-011`) | `needs-confirmation` |
| 정적 class 규율이 runtime/untrusted class 및 styling `dangerouslySetInnerHTML`를 차단 | 강제 미구현 | prohibited-import/dynamic-class negative fixture (owner `FE-OC-019`) | `needs-confirmation` |
| tokenized sizing이 CLS ≤0.10, bounded 어휘로 CSS surface가 번들 budget 내 | archived doc 미증명·프로젝트 미실측 | lab CLS report(`FE-NFR-004`) + CSS bundle report (owner `FE-OC-021`/`FE-OC-018`) | `needs-confirmation` |
| Tailwind v4.3 theme token scale이 디자인 요구를 충족 | `/docs/theme` 미발췌 | `/docs/theme` raw-source 발췌 후 token 정의 대조 | `needs-confirmation` |
## 관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)
> `/coverage`가 채우는 생성물이며 손으로 유지하지 않는다 — controller phase에서 생성.
| 관심사 | 상태 | owner | 심각도 | 근거 |
|---|---|---|---|---|
| TODO — `/coverage` 실행 전 | missing | (없음) | 미평가 | TODO |
## 마주친 문제
없음 — `/branch-spec` 채움 단계
## 묶음 (이 branch에서 파생된 자료)
<!-- GENERATED: received-delegations:start -->
### 수신한 위임
| Delegation Ref | From | Concern | Status |
|---|---|---|---|
| `DELEG-FE-004@1` | [[raw/branch-notes/feature-accessibility-baseline-contract]] | `fe.deleg.color-contrast` | accepted |
<!-- GENERATED: received-delegations:end -->
<!-- GENERATED: project-contract-imports:start -->
## 가져온 프로젝트 계약
| Ref | Owner | 요약 | Branch 적용 |
|---|---|---|---|
| `FE-OC-011@1` | [[raw/branch-notes/feature-async-ui-state-contract]] | async surface는 initial-loading, success, empty, terminal-error를 MUST 표현 | import 참조로 적용 |
| `FE-OC-018@1` | [[raw/branch-notes/feature-frontend-build-bundle-supply-chain-contract]] | frozen lockfile, dependency review, secret scan, SBOM 또는 dependency inventory를 release gate에 MUST 포함 | import 참조로 적용 |
| `FE-OC-019@1` | [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] | browser bundle에 secret을 넣지 않고 untrusted HTML injection을 기본 금지 | 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-024@1` | [[raw/branch-notes/feature-sample-feature-slice-contract-fixture]] | sample은 contract fixture이며 production feature가 의존하면 안 됨 | import 참조로 적용 |
<!-- GENERATED: project-contract-imports:end -->
### Sub-branches (세부 작업)
없음 — scaffolding 단계
### 오류 기록 (이 branch 작업 중 발생)
없음 — scaffolding 단계
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
없음 — scaffolding 단계
### 강의 (이 작업을 위해 학습한 강의)
없음 — scaffolding 단계
### job-posting tie-ins (이 작업에서 파생된 글감)
없음 — scaffolding 단계
## 관련 일일 노트
없음 — scaffolding 단계
## 완료 후 정리
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경:
- **wiki 추출 대상** (verified만, `wiki/projects/`로만 추출):
- `actually-implemented` 항목:
- `locally-verified` 항목:
- `prod-verified` 항목:
- **추출하지 않을 항목** (planned / documented-only / abandoned): 현재 전체 (frontend repository 미생성)