Files
company-haness/.claude/commands/design-system.md
T

84 lines
12 KiB
Markdown

---
description: 기존 프로젝트를 먼저 discovery하고 reuse/adapt/create를 판단한 뒤, design-brief(제약층)에서 코드 디자인 시스템+화면을 만들고 headless chrome으로 실제 UI를 렌더·품질검증한다. Figma 불필요·rate-limit 없음.
---
당신은 Orchestrator다. **DESIGN-SYSTEM** — 상위 제약(design-brief)에서 **코드 디자인 시스템 + 화면 + 실제 UI 미리보기·품질검증**을 산출한다.
입력(인자): 주제/제품 + 대상 디렉터리(기본 `design-system/`). 예: `/design-system <프로젝트> 개발자 콘솔 · dir=design-system`.
**층 관계(중요)**: design-brief=제약(무엇을), design-craft skill=방법(어떻게), **디자인 시스템=코드로 굳힌 tokens+컴포넌트(재사용 실체)**, 프론트 코드=매체. DESIGN.md·skill의 대체가 아니라 완성이다.
조직 정본은 `org-os/08-design/`이다. `generated/DESIGN.md`는 그 정본에서 생성된 도구 어댑터일 뿐이며 제품 전략·IA·와이어프레임을 대신하지 않는다. 프로젝트는 전체 정본을 복사하지 않고 selected release exact ref/SHA + component subset + 명시적 delta만 `ui-design.design-system-bindings`에 연결한다.
**대원칙: 스택을 못박지 않는다. discovery가 정한다.** 기존 프로젝트가 있으면 그 stack·토큰·컴포넌트를 **먼저 재사용**한다. 스택은 *고정값*이 아니라 **preset(선택지)**다 — 그린필드일 때만 `greenfield-react` 프리셋을 쓴다. 예전처럼 React+CSS+Vite를 전사 기본으로 강제하면, 이미 Vue/Tailwind/디자인시스템/브랜드가 있는 프로젝트를 무시하고 밀도·컨벤션이 안 맞는 화면을 찍어낸다.
## 절차
0. **Pre-work**: `report_tags.py --tag <주제>`로 관련 과거 결정 must-read. workflow-id 정한다(`wf-<slug>`).
0b. **design-direction 승인 게이트(Task 13 — Blocker 10)**: `/design-system``/design` 3b를 거치지 않고 **단독으로도 호출**될 수 있으므로, 여기서 다시 확인한다 — `design.md`의 선행 게이트를 우회해 곧장 이 커맨드로 들어오는 경로를 막는다. **부모 cascade workflow**(`<PARENT-cascade-wf>` — 이 design-system 작업이 속한 상위 제품 cascade. design-direction **child** workflow가 아니다)를 대상으로:
```
python3 .claude/hooks/state_engine.py check-direction-approved --workflow <PARENT-cascade-wf>
```
**부모로 질의해야 하는 이유**: `_has_direction_approval`의 parent-shape 는 child 가 `design-direction-approved` stage 까지 실제로 종료됐음을 요구한다 — child workflow-id 로 질의하면 `design-direction-finalize` 단계에서도 YES 가 나올 수 있어 "전이 가능"과 "최종 승인"을 혼동한다(`design-direction.md` 6번 참고).
- **standard/heavy tier** + `NO`(exit 3) → **진행하지 않는다.** BlockedReport(사유: 승인된 design-direction 없음 — 먼저 `/design-direction`을 완주하거나 `/design`의 선행 게이트를 통해 진입해야 함)를 내고 종료.
- **light tier** + `NO` → 하드 블록 아님. **경고**를 report-header risks 에 남기고, 기존에 부모 원장에 기록된 승인이 있으면(과거 cycle) 그것을 **상속**해 진행한다(없으면 승인 없이 진행 — light 는 과설계 금지 원칙상 허용).
- `YES`(exit 0) → 정상 진행.
- **brief-phase 요구**: 이 게이트를 통과했다는 것은 design-direction 이 이미 `finalize`(brief-phase=system-ready 로 취급)를 지났다는 뜻 — 이 커맨드는 그 승인된 방향의 locked-invariants/selected-direction 을 존중하며 시스템을 만든다(방향을 재발산하지 않는다).
0c. **experience + 조직 release 게이트**:
- `python3 .claude/hooks/state_engine.py check-experience-foundation --workflow <PARENT-cascade-wf>`가 `NO`면 해당 workload가 요구하는 foundation을 먼저 완주한다.
- `python3 .claude/hooks/compile_design_system.py --check` 후 `python3 .claude/hooks/design_registry.py --surface <surface> --state <candidate|stable>`로 필요한 최소 subset을 조회한다.
- `org-os/08-design/releases/<version>.yaml`의 exact SHA와 release-id, component-ids, 프로젝트 delta(tokens/components)를 `ui-design.design-system-bindings`에 기록한다. release state와 project delta는 별도이며 로컬 복제를 stable 정본처럼 승격하지 않는다.
1. **① DISCOVERY (필수·최우선 — brief보다 먼저)**: 대상 프로젝트에 **기존 시스템이 있는지 먼저 조사**한다. 아무 것도 조사하지 않고 스택을 고르는 것은 금지.
- **stack**: `package.json`/lockfile/설정으로 프레임워크(React/Vue/Svelte/…)·번들러(Vite/Next/…)·언어·CSS 방식(CSS변수/Tailwind/CSS-in-JS) 식별. (없으면 = greenfield)
- **design-system**: 기존 디자인시스템/컴포넌트 라이브러리/테마 존재 여부·위치(예: `design-system/`, `packages/ui`, MUI/Chakra/자체).
- **tokens·brand**: 기존 토큰 SoT(CSS 변수/theme 파일)·브랜드 색·타이포·로고·간격 규율.
- **components**: 재사용 가능한 기존 컴포넌트 인벤토리(무엇이 이미 있나 → 다시 만들지 말 것).
- **data-density**: 데이터 밀도(대시보드/테이블 과밀 vs 마케팅/저밀도) — 토큰·레이아웃 결정에 직결.
- 산출: discovery 노트를 `design-brief.yaml`의 `existing-system`(있으면)에 기록.
2. **② 판단: reuse / adapt / create** (근거를 `design-brief.yaml`의 `stack-decision`에):
- **reuse** — 적합한 기존 디자인시스템/토큰/컴포넌트가 있으면 **그것을 소비**한다. 새 시스템을 만들지 않는다. 대상 디렉터리 대신 기존 컴포넌트·토큰 위에서 화면을 조립.
- **adapt** — 부분적 시스템(토큰만/일부 컴포넌트)이면 **그 컨벤션 안에서 확장**한다. 새 축을 함부로 도입하지 않는다.
- **create** — 기존 시스템이 없거나(그린필드) 부적합하면 **preset을 골라** 새로 만든다.
- preset `greenfield-react`: **React + CSS 변수(tokens.css) + Vite**. (이것이 유일한 preset이 아니라 그린필드 React용 기본 preset이다.)
- 대상 프로젝트가 이미 다른 스택이면 create여도 **그 스택의 토큰·컴포넌트 관례**를 따른다(React를 강요하지 않는다).
3. **③ design-brief** (skill: `design-craft` + `design-brief-spec.yaml`): 대상에 `design-brief.yaml`을 세운다 — brief(무엇/누구/달성) → references(구체 신호 3~6, "modern/clean" 금지) → tokens(값+의도+경계) → decisions(판단로직) → donts(5+). **references·tokens는 discovery 결과(기존 브랜드·데이터밀도)를 반영**한다(빈 추론층=generic). **이게 없으면 컴포넌트 생성 금지**.
4. **④ 구현** (판단에 따라):
- **reuse/adapt** — 기존 토큰/컴포넌트 위에서 화면(screens)을 조립. 기존 컴포넌트에 없는 것만 그 시스템의 관례로 추가.
- **context-package(spawn 전 필수 게이트, finding P0-2)**: `des-platform` 및 FAM-ENG-FRONTEND 후보에서 planner가 고른 concrete role을 띄우기 전 `python3 .claude/hooks/context_package.py --compile … && python3 .claude/hooks/context_package.py <pkg>`(exit 0)로 패키지를 만들고, 출력된 package path/hash를 spawn 프롬프트에 포함한다. design-brief는 must-read/shared-constraints로 동봉한다.
- **create · greenfield-react preset** (subagent `des-platform` → 선택된 frontend concrete role):
- `src/tokens.css` — 토큰을 CSS 변수로(값+경계 주석). `--accent`는 primary/focus 전용 등 경계 반영.
- `src/components/*.jsx` (+`components.css`) — Button(variant)·Card·Input 등 재사용 컴포넌트, **토큰만 소비**(`var(--*)`, 하드코딩 색 금지). 컴포넌트별 판단로직·금지 반영. **`:focus-visible` 가시 표식 필수**(outline을 죽이면 box-shadow 등으로 대체).
- `src/screens/*.jsx` — 컴포넌트를 **조립만**(새 스타일 금지). `src/preview.jsx`에 컴포넌트 갤러리 + 화면 + **상태(loading/empty/error/overflow) 데모**를 건다.
- 근거·산출은 `.report.yaml`(report-header BLUF).
5. **⑤ 미리보기 + 품질 게이트(실제 UI 검증)**:
`python3 .claude/hooks/verify_run.py --workflow <wf> --agent <role> --session <id> --category acceptance-criteria --subject ui-render-gate [--source-revision-sha256 <sha>] -- python3 .claude/hooks/preview_ui.py <dir> --out <dir>/preview.png --viewports 360,768,1280 --check-css [--states "loading=/#/loading,empty=/#/empty,error=/#/error"]`
→ npm install→vite build→로컬서버→**렌더 검증(dump-dom)+반응형 스크린샷+정적 CSS 품질(대비·포커스)**. **Figma·rate-limit 없이** 실제 렌더.
**중요 — 스크린샷 존재 ≠ 품질**: preview_ui는 build 성공+PNG 존재만으로 통과시키지 않는다. 앱이 런타임에 안 붙어 `#root`가 비면(빈 화면), 빌드가 빈 번들이면, WCAG 대비가 critical이면, 포커스 표식이 없으면 **게이트가 실패(비영점)**한다. 스크린샷을 읽어 육안 검증도 병행(accent 남발·하드코딩색·정렬·클리핑·데이터밀도).
6. **⑥ 게이트/보고**: report-header(BLUF)로 종합 + 산출 경로(패키지·PNG들·게이트 결과). 게이트 실패 시 **고치고 preview 재실행**(개선 루프).
`python3 .claude/hooks/lint_design_system_adherence.py --ui-report <ui-design.report.yaml> --target <dir>`로 release SHA, raw color token, local component-id 중복, 조직 component의 무신고 로컬 재구현을 검사한다. 정당한 로컬 확장만 `delta.components`에 id와 사유를 남긴다. DESIGN.md 변경 검토는 `python3 .claude/hooks/compile_design_system.py --diff <project-DESIGN.md>`로 정본 대비 unified diff를 확인한다.
## 스택 (preset — 고정 아님)
**기본 스택은 없다. discovery가 결정한다.**
- 기존 프로젝트 있음 → 그 stack/tokens/components **우선 재사용(reuse/adapt)**. 다른 스택을 덮어씌우지 않는다.
- `greenfield-react` preset(그린필드 React용): React + CSS 변수 + Vite. tokens는 `tokens.css`의 CSS 변수 = design-brief 토큰 1:1(SoT). 컴포넌트는 **`var(--*)`만 소비**(하드코딩 색 금지). Tailwind-first(토큰 갇힘)·순수HTML(컴포넌트 없음) 아님.
- 그 외 스택(create이지만 non-React) → 해당 생태계의 토큰·컴포넌트 관례로.
## 규칙 / 불변식
- **discovery-first**: 기존 stack/design-system/brand/components/data-density를 조사하지 않고 스택을 못박지 않는다. 기존 시스템이 있으면 create보다 reuse/adapt 우선.
- design-brief 없이 컴포넌트 생성 금지(제약>묘사). references는 형용사가 아니라 구체 신호(기존 브랜드·밀도 반영).
- 컴포넌트는 토큰만 소비 — 하드코딩 색 금지(component CSS에 hex 금지, 토큰은 tokens.css/기존 토큰 SoT에만).
- 화면은 컴포넌트 조립 — 화면에서 새 컴포넌트 스타일을 만들지 않는다(디자인 시스템 SoT 보존).
- **품질은 게이트를 통과해야 성립**: preview_ui의 렌더 검증·대비·포커스·반응형 게이트를 통과하지 못하면 "완료"가 아니다. 스크린샷 존재만으로 품질 주장 금지. 품질은 반복 개선 루프(build→검증→고치기)에서 나온다 — 무료 Figma와 달리 여기선 무제한.
- report-header 없이 종료 금지. evidence 실존. external side-effect(배포/PR 등) 기본 금지 — npm/vite/chrome 로컬 빌드는 허용 범위.
## 산출/handoff
- `<dir>/`(또는 reuse 시 기존 시스템 위): design-brief.yaml(+existing-system·stack-decision)·tokens·components·screens·preview + `preview.png`(들, 반응형).
- engine 선택은 tool-neutral이다. local HTML/Stitch/Figma/v0/Framer 중 가용 adapter를 쓰되 `.claude/schemas/design-engine-output.artifact.schema.json`의 `screen-refs/editable-source/preview-url/screenshots/design-system-ref/source-provenance/verification`을 항상 내고 `validate_design_engine_output.py`로 검증한다.
- **다음**: 실제 제품 연결은 이 컴포넌트로 화면 확장(`/build`), 또는 디자인 검토가 필요하면 이 코드를 Figma로(선택, gated).