@su-record/vibe 2.8.12 → 2.8.13

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.
@@ -5,7 +5,7 @@ argument-hint: ""
5
5
 
6
6
  # /vibe.figma
7
7
 
8
- Figma 디자인 + 스토리보드 → 프로덕션 코드. **뷰포트별 점진적 빌드** 방식.
8
+ Figma 디자인 + 스토리보드 → 프로덕션 코드. **프레임별 점진적 빌드** 방식.
9
9
 
10
10
  ## Usage
11
11
 
@@ -15,1874 +15,88 @@ Figma 디자인 + 스토리보드 → 프로덕션 코드. **뷰포트별 점진
15
15
  /vibe.figma --refine # 보완 모드 (기존 코드 + Figma 재비교 → 수정)
16
16
  ```
17
17
 
18
- ## Incremental Build Flow (핵심 플로우)
19
-
20
- 한번에 전체를 만들지 않고, **뷰포트별로 점진적으로 쌓아가는 방식**:
18
+ ## Incremental Build Flow
21
19
 
22
20
  ```
23
- Step A: 스토리보드 URL 입력
24
- 브레이크포인트, 인터랙션, 스펙 추출
25
- base 레이아웃 & 컴포넌트 구조 설계 + 파일 생성
26
-
27
- Step B: 모바일 디자인 URL 입력
28
- → 모바일 스타일 반영, 이미지 추출
29
- → 컴포넌트 리팩토링 (모바일 기준)
30
- → 🔄 검증 루프 (Figma vs 코드 비교, P1=0까지)
21
+ Step A: 스토리보드 URL 입력 (선택)
22
+ 기능 정의, 인터랙션, 브레이크포인트 추출
23
+ 실제 동작하는 레이아웃 코드 + 기능 주석 생성
24
+ → 스타일 없이 클릭/팝업/상태 전환 동작하는 수준
31
25
 
32
- Step C: PC 디자인 URL 입력
33
- PC 스타일 반영 (반응형 clamp/breakpoint 추가)
34
- 이미지 추출 (PC용 에셋)
35
- 컴포넌트 리팩토링 (PC 대응)
36
- 🔄 검증 루프 (Figma vs 코드 비교, P1=0까지)
37
-
38
- Step D: 최종 공통화 리팩토링
39
- 모바일/PC 스타일 공통 토큰 추출
40
- 중복 코드 통합
41
- 유사 컴포넌트 variant 통합 (80% rule)
42
- → 🔄 최종 검증 루프 (양쪽 뷰포트 동시 검증)
26
+ Step B: 디자인 URL 입력 → 프레임별 추출 → 코드 반영 → 검증
27
+ 🔄 검증 루프 (P1=0까지)
28
+ "추가 디자인 URL이 있나요?"
29
+ (있으면 반복)
30
+ Step B: 추가 디자인 URL 반응형 레이어 추가 → 검증
31
+ → 🔄 검증 루프 (양쪽 뷰포트 P1=0까지)
32
+ "추가 디자인 URL이 있나요?"
33
+ (없으면)
34
+ Step C: 최종 공통화 리팩토링
35
+ 토큰 통합, 중복 제거, variant 통합
36
+ → 🔄 최종 검증 루프
43
37
  ```
44
38
 
45
- ### 단계별 URL 입력 (AskUserQuestion — options 사용 금지, 자유 텍스트만)
39
+ ### URL 입력 규칙
46
40
 
47
41
  ```
48
- Step에서 AskUserQuestion으로 하나씩 입력:
49
- ⚠️ 절대 선택지(options)를 제공하지 않는다. 순수 텍스트 입력만.
50
- ⚠️ 각 질문의 응답을 받은 후에만 다음으로 진행.
42
+ ⚠️ AskUserQuestion 사용 시 절대 선택지(options) 제공 금지. 자유 텍스트만.
43
+ ⚠️ 질문의 응답을 받은 후에만 다음으로 진행.
51
44
 
52
45
  Step A: "📋 스토리보드 Figma URL을 입력해주세요. (없으면 '없음')"
53
46
  → ⏸️ 응답 대기
54
- → URL 또는 "없음"
55
-
56
- Step B: "📱 모바일 디자인 Figma URL을 입력해주세요."
57
- → ⏸️ 응답 대기
58
- → URL 저장 + 추출 + 스타일 반영 + 검증
59
47
 
60
- Step C: "🖥️ PC 디자인 Figma URL을 입력해주세요. (없으면 '없음')"
48
+ Step B (반복):
49
+ "🎨 디자인 Figma URL을 입력해주세요."
50
+ → ⏸️ 응답 대기 → 프레임별 추출 + 코드 반영 + 검증
51
+ → 검증 완료 후:
52
+ "🎨 추가 디자인 URL이 있나요? (없으면 '없음')"
61
53
  → ⏸️ 응답 대기
62
- → URL 또는 "없음" (없으면 single viewport mode)
63
-
64
- 디자인 URL이 1개만이면: 단일 뷰포트 (Step B만 실행, Step C 스킵)
65
- 디자인 URL이 2개이면: 반응형 (Step B + C + D)
54
+ → URL 입력: 반응형 레이어 추가 검증 → 다시 "추가?" 질문
55
+ → "없음": Step C로 진행
66
56
  ```
67
57
 
68
58
  ### Mode
69
59
 
70
- | Mode | 조건 | 동작 |
71
- |------|------|------|
72
- | **Project integration** | 기본값 | 기존 디자인 시스템/토큰 활용, 프로젝트 컨벤션 준수 |
73
- | **--new** | 플래그 지정 시 | 자체 완결 토큰 + 컴포넌트, 기존 디자인 시스템 무관 |
74
- | **--refine** | 플래그 지정 시 | 기존 코드를 Figma 재비교 → 부족한 부분만 수정 |
75
-
76
- ### 스토리보드에서 추출하는 정보
77
-
78
- | 항목 | 활용 |
60
+ | Mode | 동작 |
79
61
  |------|------|
80
- | 브레이크포인트 / 해상도 가이드 | base 레이아웃 설계 |
81
- | 인터랙션 스펙 (호버, 클릭, 스크롤) | 이벤트 핸들러 + CSS states |
82
- | 애니메이션 스펙 (타이밍, 이징) | transition/animation |
83
- | 상태별 UI (로딩, 에러, 성공) | 조건부 렌더링 |
84
- | 반응형 동작 설명 | responsive 코드 |
85
- | 컬러/타이포 가이드 | 토큰 생성 |
62
+ | **기본** | 기존 디자인 시스템/토큰 활용 |
63
+ | **--new** | 자체 완결 토큰, 기존 시스템 무관 |
64
+ | **--refine** | 기존 코드를 Figma 재비교 → 수정만 |
86
65
 
87
66
  ## Context Reset
88
67
 
89
68
  **이 커맨드 실행 시 이전 대화 무시.**
90
- - Figma 데이터 + 프로젝트 스택 기반으로 판단
91
- - 스토리보드 스펙이 디자인과 충돌 시 → 스토리보드 우선
69
+ 스토리보드 스펙 > 디자인 데이터 > 프로젝트 컨벤션 순으로 우선.
92
70
 
93
71
  ---
94
72
 
95
73
  > **⏱️ Timer**: Call `getCurrentTime` tool at the START. Record the result as `{start_time}`.
96
74
 
97
- ## Model Routing
98
-
99
- | Step | Claude | GPT (Codex) | 이유 |
100
- |------|--------|-------------|------|
101
- | Step A (스토리보드 + 구조) | **Haiku → Sonnet** | — | MCP 추출 → 구조 설계 |
102
- | Step B (모바일 추출) | **Haiku** | — | MCP + 이미지 다운로드 |
103
- | Step B (모바일 코드 생성) | **Sonnet** | **gpt-5.3-codex-spark** (병렬) | 섹션별 컴포넌트 생성 |
104
- | Step B (모바일 검증) | **Sonnet** | — | 이미지 비교 + auto-fix |
105
- | Step C (PC 추출) | **Haiku** | — | MCP + 이미지 다운로드 |
106
- | Step C (PC 반응형 반영) | **Sonnet** | — | 기존 코드에 반응형 레이어 추가 |
107
- | Step C (PC 검증) | **Sonnet** | — | 양쪽 뷰포트 비교 |
108
- | Step D (공통화) | **Sonnet** | — | 리팩토링 + 최종 검증 |
109
- | Post (코드 리뷰) | — | **gpt-5.3-codex** | 전체 코드 품질 검증 |
110
-
111
- ### GPT 모델 선택 기준
112
-
113
- | 모델 | config key | 용도 |
114
- |------|-----------|------|
115
- | `gpt-5.4` | `models.gpt` | 아키텍처 판단, 복잡한 추론 |
116
- | `gpt-5.3-codex` | `models.gptCodex` | 코드 리뷰, 분석 (정확도 우선) |
117
- | `gpt-5.3-codex-spark` | `models.gptCodexSpark` | 코드 생성 (속도 우선) |
118
-
119
- `~/.vibe/config.json`의 `models` 에서 오버라이드 가능.
120
-
121
- ### Codex 병렬 활용 (codex-plugin-cc 설치 시)
122
-
123
- ```
124
- Phase 6 — 컴포넌트 생성:
125
- 섹션 3개 이상 → /codex:rescue --background (gpt-5.3-codex-spark)
126
- 각 섹션 컴포넌트를 Codex에 병렬 위임, Claude는 루트 페이지 + 토큰 담당
127
-
128
- Post — 코드 리뷰:
129
- /codex:review (gpt-5.3-codex)
130
- 생성된 코드의 design fidelity + 코드 품질 크로스 검증
131
-
132
- Codex 미설치 시 자동 스킵 — Claude만으로 순차 생성.
133
- ```
134
-
135
75
  ## Step A: 스토리보드 + Base 구조
136
76
 
137
- ### A-1. 스토리보드 URL 입력
138
-
139
- AskUserQuestion (options 사용 금지, 자유 텍스트만):
140
- ```
141
- question: "📋 스토리보드 Figma URL을 입력해주세요. (없으면 '없음')"
142
- → ⏸️ 응답 대기 (응답 받기 전 다음 진행 금지)
143
- → figma.com URL → storyboardUrl 저장
144
- → "없음" → storyboardUrl = null
145
- ```
146
-
147
- ### A-2. 스토리보드 추출 (storyboardUrl이 있을 때)
148
-
149
- ```
150
- URL에서 fileKey, nodeId 추출:
151
- https://www.figma.com/design/:fileKey/:fileName?node-id=:nodeId
152
- → nodeId: 하이픈을 콜론으로 ("1-109" → "1:109")
153
-
154
- 1. get_metadata(fileKey, nodeId) → 전체 프레임 목록
155
- 2. 관련 섹션별 get_design_context:
156
- - "해상도 대응" / "Media Query" → 브레이크포인트
157
- - "인터랙션" / "Interaction" → 호버/클릭/스크롤 스펙
158
- - "애니메이션" / "Animation" / "Motion" → 트랜지션 스펙
159
- - "상태" / "State" → 로딩/에러/성공 UI
160
- - "컬러" / "Color" / "타이포" / "Typography" → 디자인 가이드
161
- ```
162
-
163
- ### A-3. 프로젝트 스택 감지 + Base 구조 설계
164
-
165
- ```
166
- 1. 프로젝트 스택 감지 (Phase 3 참조)
167
- 2. 디자인 시스템 감지 (--new 미지정 시)
168
- 3. 브레이크포인트 로드 (Phase 3-3 참조, 스토리보드 우선)
169
- 4. 피처명 결정 (Figma 파일명에서 추출)
170
- 5. 파일 구조 생성:
171
- - 루트 페이지 파일 (빈 shell)
172
- - 컴포넌트 디렉토리 (빈 폴더)
173
- - 토큰 파일 (스토리보드 기반 초기값)
174
- ```
175
-
176
- ---
177
-
178
- ## Step B: 모바일 디자인 반영
179
-
180
- ### B-1. 모바일 디자인 URL 입력
181
-
182
- AskUserQuestion (options 사용 금지):
183
- ```
184
- question: "📱 모바일 디자인 Figma URL을 입력해주세요."
185
- → ⏸️ 응답 대기
186
- → URL 저장: mobileUrl
187
- ```
188
-
189
- ### B-2. 모바일 디자인 추출 (MCP)
190
-
191
- ```
192
- 1. get_design_context(fileKey, nodeId) → 코드 + 스크린샷 + 에셋 URL
193
- → 스크린샷으로 전체 디자인 분석 (레이아웃, 타이포, 색상, 컴포넌트 구조)
194
- ```
195
-
196
- ### B-3. 이미지 에셋 다운로드 (BLOCKING)
197
-
198
- > **⛔ 이미지 다운로드 완료 전 코드 생성 진행 금지.**
199
-
200
- ```
201
- 1. get_design_context 응답에서 모든 이미지 URL 추출
202
- (패턴: https://www.figma.com/api/mcp/asset/...)
203
- 2. WebFetch로 각 URL 다운로드
204
- 3. 프로젝트 에셋 디렉토리에 저장 (Nuxt: static/images/{feature}/)
205
- 4. 파일명: 레이어 이름 기반 kebab-case
206
- 5. URL→로컬경로 매핑 테이블 생성
207
- 6. SVG: 작은 아이콘 → 인라인, 큰 SVG → 파일
208
- ```
209
-
210
- ### B-4. 모바일 코드 생성
211
-
212
- ```
213
- 1. 모바일 기준으로 컴포넌트 코드 생성 (Phase 4~6 적용)
214
- 2. 이미지 에셋은 매핑 테이블의 로컬 경로 사용
215
- 3. 스토리보드 스펙 반영 (인터랙션, 애니메이션)
216
- 4. 모바일 전용 스타일 작성 (desktop 미고려)
217
- ```
218
-
219
- ### B-5. 모바일 검증 루프
220
-
221
- ```
222
- 🔄 Phase 9 검증 루프 실행:
223
- - Figma 모바일 스크린샷 vs 생성된 코드 비교
224
- - P1=0 될 때까지 수정 반복
225
- - 이미지 에셋 전부 표시되는지 확인
226
- ```
227
-
228
- ---
229
-
230
- ## Step C: PC 디자인 반영 (반응형)
231
-
232
- ### C-1. PC 디자인 URL 입력
233
-
234
- AskUserQuestion (options 사용 금지):
235
- ```
236
- question: "🖥️ PC 디자인 Figma URL을 입력해주세요. (없으면 '없음')"
237
- → ⏸️ 응답 대기
238
- → URL → pcUrl 저장 (responsive mode)
239
- → "없음" → single viewport mode (Step D로 직행)
240
- ```
241
-
242
- ### C-2. PC 디자인 추출 (MCP)
243
-
244
- ```
245
- 1. get_design_context(fileKey, nodeId) → 코드 + 스크린샷 + 에셋 URL
246
- → 모바일 스크린샷과 side-by-side 비교 → viewport diff table 생성
247
- ```
248
-
249
- ### C-3. PC 이미지 에셋 다운로드 (BLOCKING)
250
-
251
- ```
252
- 모바일과 동일 프로세스.
253
- PC 전용 이미지가 있으면 추가 다운로드.
254
- 동일 이미지는 스킵 (중복 방지).
255
- ```
256
-
257
- ### C-4. PC 스타일 반영 + 반응형 리팩토링
258
-
259
- ```
260
- 기존 모바일 코드에 PC 대응 추가:
261
- 1. 공통 값 → clamp() fluid 토큰으로 변환
262
- 2. 레이아웃 구조 변경 → @media (min-width: {breakpoint}px) 추가
263
- 3. PC 전용 요소 → display toggle (.desktopOnly)
264
- 4. 모바일 전용 요소 → display toggle (.mobileOnly)
265
- 5. PC 전용 배경 이미지 → @media 분기
266
- 6. 기존 모바일 코드는 가능한 보존하고 반응형 레이어만 추가
267
- ```
268
-
269
- ### C-5. PC 검증 루프
270
-
271
- ```
272
- 🔄 Phase 9 검증 루프 실행:
273
- - Figma PC 스크린샷 vs 생성된 코드 (PC viewport) 비교
274
- - P1=0 될 때까지 수정 반복
275
- - 모바일 검증도 재확인 (PC 수정으로 모바일이 깨지지 않았는지)
276
- ```
277
-
278
- ---
279
-
280
- ## Step D: 최종 공통화 리팩토링
281
-
282
- ### D-1. 스타일 공통화
283
-
284
- ```
285
- 1. 모바일/PC에서 동일한 값 → 공통 토큰으로 추출
286
- 2. 중복 CSS/SCSS 규칙 통합
287
- 3. 컴포넌트 내 중복 로직 제거
288
- ```
289
-
290
- ### D-2. 컴포넌트 통합
291
-
292
- ```
293
- 1. 유사 컴포넌트 (80% rule) → variant prop으로 통합
294
- 2. 중복 sub-component → 공유 컴포넌트로 추출
295
- 3. Fragment/template 활용하여 불필요한 래퍼 제거
296
- ```
297
-
298
- ### D-3. 최종 검증 루프
299
-
300
- ```
301
- 🔄 양쪽 뷰포트 동시 검증:
302
- - 모바일 Figma vs 코드 (mobile viewport)
303
- - PC Figma vs 코드 (desktop viewport)
304
- - 양쪽 모두 P1=0, Match Score 95%+
305
- - 이미지 에셋 전부 정상 표시
306
- ```
307
-
308
- ---
309
-
310
- ## Phase 1: Design Analysis (Image-First)
311
-
312
- ### Single mode
313
-
314
- Read `figma-output/frame.png` and analyze:
315
-
316
- | Aspect | What to Extract |
317
- |--------|-----------------|
318
- | Layout | Flex/Grid direction, alignment, wrapping |
319
- | Components | Visual boundaries (cards, buttons, inputs, modals) |
320
- | Spacing | Padding, margins, gaps between elements |
321
- | Typography | Font sizes, weights, line heights, hierarchy |
322
- | Colors | Background, text, border, accent colors |
323
- | States | Hover/active/disabled indicators if visible |
324
- | Responsive hints | Breakpoint indicators, fluid vs fixed widths |
325
-
326
- ### Responsive mode
327
-
328
- Read **ALL** frame images and analyze **side-by-side**:
329
-
330
- | Aspect | What to Compare |
331
- |--------|-----------------|
332
- | Layout shift | Which elements reflow? (e.g., horizontal → vertical stack) |
333
- | Visibility | Which elements hide/show per viewport? |
334
- | Typography scale | Font size ratio between viewports |
335
- | Spacing scale | Padding/gap ratio between viewports |
336
- | Component shape | Does the component change form? (e.g., drawer → sidebar) |
337
- | Navigation | Does nav change? (e.g., hamburger ↔ full nav bar) |
338
-
339
- Build a **viewport diff table**:
340
-
341
- ```
342
- | Element | Mobile (375px) | Desktop (1440px) | Strategy |
343
- |---------------|------------------------|------------------------|-------------------|
344
- | Nav | hamburger + drawer | horizontal bar | component swap |
345
- | Hero title | 24px | 48px | fluid: clamp() |
346
- | Card grid | 1 column | 3 columns | grid auto-fit |
347
- | Sidebar | hidden | visible | display toggle |
348
- | Body text | 14px | 16px | fluid: clamp() |
349
- | Padding | 16px | 48px | fluid: clamp() |
350
- ```
351
-
352
- ## Phase 2: Layer Data Extraction
353
-
354
- ### Single mode
355
-
356
- Read `figma-output/layers.json` and extract:
357
-
358
- 1. **Component hierarchy** — Map nested layers to component tree
359
- 2. **Design tokens** — Colors (fill, stroke), font properties, spacing values, border radius, shadows
360
- 3. **Auto-layout** — Direction, gap, padding (maps directly to flex/grid)
361
- 4. **Constraints** — Fixed vs fluid sizing
362
- 5. **Component instances** — Identify reusable patterns
363
- 6. **Image fills** — Identify layers with `type: "IMAGE"` fills (see Phase 2-A)
364
-
365
- ### Responsive mode
366
-
367
- Read **ALL** `layers.{N}.json` files and extract **per-viewport**:
368
-
369
- 1. **Per-viewport tokens** — Record exact values for each viewport:
370
- ```
371
- { "mobile": { "h1": 24, "body": 14, "padding": 16 },
372
- "desktop": { "h1": 48, "body": 16, "padding": 48 } }
373
- ```
374
- 2. **Layout differences** — Auto-layout direction changes (e.g., HORIZONTAL → VERTICAL)
375
- 3. **Visibility map** — Which layers exist in one viewport but not the other
376
- 4. **Shared tokens** — Values identical across all viewports (colors, border-radius, shadows are usually shared)
377
-
378
- ### Phase 2-A: Image Fill Classification
379
-
380
- Figma에서 이미지는 레이어의 `fills` 배열에 `type: "IMAGE"`로 들어옴. 이를 **용도별로 분류**해야 코드에서 올바른 패턴을 생성할 수 있음.
381
-
382
- #### 감지 방법
383
-
384
- `layers.json`에서 아래 패턴을 탐색:
385
-
386
- ```
387
- fills: [{ type: "IMAGE", scaleMode: "FILL" | "FIT" | "CROP" | "TILE", imageRef: "..." }]
388
- ```
389
-
390
- #### 분류 기준
391
-
392
- | 판별 조건 | 분류 | 코드 패턴 |
393
- |----------|------|----------|
394
- | 레이어가 프레임/섹션의 **직계 배경**이고, 위에 텍스트/UI 요소가 겹침 | **Background Image** | `background-image` + `background-size` |
395
- | 레이어가 독립적이고, 위에 겹치는 요소 없음 | **Content Image** | `<img>` 또는 `<picture>` |
396
- | 레이어 이름에 `icon`, `logo`, `avatar` 포함 | **Inline Asset** | `<img>` (작은 크기) |
397
- | 레이어가 반복 패턴(`scaleMode: "TILE"`) | **Pattern/Texture** | `background-image` + `background-repeat` |
398
- | 레이어가 전체 프레임을 덮고 opacity < 1 또는 blendMode 적용 | **Overlay Image** | `background-image` + overlay `::before`/`::after` |
399
-
400
- #### 이미지-텍스트 겹침 판별 (Background vs Content)
401
-
402
- ```
403
- frame의 fills에 IMAGE가 있고, children에 TEXT 레이어가 있으면:
404
- → Background Image (텍스트 아래 깔리는 배경)
405
-
406
- 독립 레이어의 fills에 IMAGE가 있고, 형제 레이어와 겹치지 않으면:
407
- → Content Image
408
- ```
409
-
410
- #### 이미지 소스 추출
77
+ 1. Load skill `vibe-figma` — 스토리보드 URL 입력 + MCP 추출
78
+ 2. Load skill `vibe-figma-analyze` — 세밀 분석 → 동작하는 레이아웃 코드 + 기능 주석
411
79
 
412
- - `imageRef` 값으로 Figma API의 이미지 렌더링 사용 (`/images/{fileKey}`)
413
- - 추출된 이미지는 `figma-output/assets/` 디렉토리에 저장
414
- - 파일명은 레이어 이름 기반: `hero-bg.png`, `product-photo.jpg`
80
+ ## Step B: 디자인 반영 (반복)
415
81
 
416
- ### Responsive Scaling Calculation
82
+ Load skill `vibe-figma-frame` — 디자인 URL → 프레임별 추출 → 코드 반영 → 검증 루프
417
83
 
418
- Per-viewport 토큰 쌍에서 clamp() 값을 계산. 공식은 **Phase 4-3** 참조.
84
+ - 번째 URL: base 스타일 적용
85
+ - 추가 URL: 반응형 레이어 추가 (clamp + @media)
86
+ - URL이 더 없을 때까지 반복
419
87
 
420
- 핵심: Figma 디자인이 2x 스케일(2560px/720px)이므로, 반드시 타겟 해상도(1920px/480px)로 환산 후 clamp를 계산해야 함.
88
+ ## Step C: 최종 공통화
421
89
 
422
- **Correction rule**: When image and JSON disagree, **image wins**. The image shows designer intent; JSON may have structural artifacts.
90
+ Load skill `vibe-figma-consolidate` 토큰 통합 + 중복 제거 + 최종 검증
423
91
 
424
- ## Phase 3: Project Stack Detection + Mode Resolution
425
-
426
- ### 3-1. Detect Stack
427
-
428
- 1. Read `.claude/vibe/config.json` → check `stacks` field
429
- 2. If no config, detect from project files:
430
- - `package.json` → React, Vue, Svelte, Angular, etc.
431
- - `tailwind.config.*` → Tailwind CSS
432
- - `next.config.*` → Next.js
433
- - `nuxt.config.*` → Nuxt
434
- - `*.module.css` → CSS Modules pattern
435
- - `*.scss` / `sass` in deps → SCSS
436
- - `styled-components` / `@emotion` in deps → CSS-in-JS
437
-
438
- ### 3-2. Load Design Context (Design Skill Integration)
439
-
440
- Read these files **in order** — later sources override earlier ones:
441
-
442
- 1. **`.claude/vibe/design-context.json`** — brand personality, aesthetic direction, constraints
443
- - `aesthetic.style` → guides visual weight (minimal vs bold)
444
- - `aesthetic.colorMood` → warm/cool/vibrant tone for token selection
445
- - `brand.personality` → preserve brand-expressive elements
446
- - `constraints.accessibility` → AA or AAA level (affects contrast, focus, ARIA depth)
447
- - `constraints.devices` → responsive breakpoints priority
448
- 2. **`.claude/vibe/design-system/{project}/MASTER.md`** — authoritative token definitions
449
- - If MASTER.md exists: **map Figma tokens to MASTER.md tokens first**, only create new tokens for values with no match
450
- - If no MASTER.md: generate `figma-tokens.css` as standalone token source
451
-
452
- **Decision rule**: When Figma token ≈ existing MASTER.md token (within 10% color distance or ±2px spacing), **use the existing token** — do not duplicate.
453
-
454
- ### 3-3. Load Breakpoints
455
-
456
- Breakpoints are loaded from multiple sources in priority order:
457
-
458
- #### Source Priority
459
-
460
- | Priority | Source | How |
461
- |----------|--------|-----|
462
- | 1 | **Storyboard** | Phase 0-1에서 추출한 `storyboardSpec.breakpoints` |
463
- | 2 | **`~/.vibe/config.json`** | `figma.breakpoints` — user-customized via `vibe figma breakpoints --set` |
464
- | 3 | **Project CSS/Tailwind** | Grep `tailwind.config.*` → `theme.screens`, or `@media.*min-width` patterns in codebase |
465
- | 4 | **Defaults** | Built-in values (breakpoint: 1024px, etc.) |
466
-
467
- #### Default Breakpoints (built-in)
468
-
469
- Based on game industry responsive storyboard standards:
470
-
471
- ```
472
- breakpoint: 1024px ← PC↔Mobile boundary (@media min-width)
473
- pcTarget: 1920px ← PC main target resolution
474
- mobilePortrait: 480px ← Mobile portrait max width
475
- mobileMinimum: 360px ← Mobile minimum supported width
476
- designPc: 2560px ← Figma PC artboard width (design is 2x scale)
477
- designMobile: 720px ← Figma Mobile artboard width (design is 2x scale)
478
- ```
479
-
480
- #### How to use in code generation
481
-
482
- ```
483
- @media breakpoint:
484
- - Single breakpoint model: @media (min-width: {breakpoint}px)
485
- - Mobile-first: styles below breakpoint = mobile, above = PC
486
-
487
- clamp() range:
488
- - minVw = mobileMinimum (360px) — smallest supported viewport
489
- - maxVw = pcTarget (1920px) — largest target viewport
490
- - Values scale linearly between these bounds
491
-
492
- Design scale factor:
493
- - PC design at {designPc}px targets {pcTarget}px → scale = pcTarget/designPc
494
- - Mobile design at {designMobile}px targets {mobilePortrait}px → scale = mobilePortrait/designMobile
495
- - Apply scale to convert Figma pixel values to target pixel values
496
- ```
497
-
498
- #### User customization
499
-
500
- Users can override any value via CLI:
501
-
502
- ```bash
503
- vibe figma breakpoints # Show current values
504
- vibe figma breakpoints --set breakpoint=768 # Change PC↔Mobile boundary
505
- vibe figma breakpoints --set mobileMinimum=320 # Change mobile minimum
506
- ```
507
-
508
- Stored in `~/.vibe/config.json` → `figma.breakpoints`. Partial overrides merge with defaults.
509
-
510
- ### 3-4. Resolve Generation Mode
511
-
512
- **Both modes use the project's existing directory structure.** Never create `figma-output/generated/`.
513
-
514
- ```
515
- if --new flag:
516
- → detect project's page/component/style directories (same as default)
517
- → generate self-contained token file (no MASTER.md dependency)
518
- → components are self-contained (own tokens, no external design system imports)
519
- → placed in project's standard directories with feature-named subfolder
520
-
521
- default (no flag):
522
- → scan existing component directories, theme files, token definitions
523
- → map output to project's conventions (file location, naming, imports)
524
- → add only NEW tokens that don't exist yet
525
- ```
526
-
527
- ---
528
-
529
- ## Phase 4: Style Architecture
530
-
531
- ### 4-1. Global Styles File
532
-
533
- **Token resolution priority** (default mode):
534
-
535
- 1. **MASTER.md tokens** — if `.claude/vibe/design-system/{project}/MASTER.md` exists, map Figma values to these tokens
536
- 2. **design-context.json tokens** — if `detectedStack.fonts`, `aesthetic.colorMood` exist, align with these
537
- 3. **New figma-tokens** — only for values that have no existing match
538
-
539
- **--new mode**: Generate self-contained token file (no MASTER.md dependency), in project's standard directories.
540
-
541
- #### Output Structure (both modes — project structure 준수)
542
-
543
- 프로젝트의 기존 디렉토리 구조를 감지하여 올바른 위치에 파일 생성.
544
-
545
- **Step 1: 디렉토리 감지**
546
-
547
- ```
548
- 페이지 디렉토리:
549
- Next.js → pages/ or app/
550
- Nuxt → pages/
551
- React → src/pages/ or src/views/
552
- Vue → src/views/
553
-
554
- 컴포넌트 디렉토리:
555
- Next.js → components/ or src/components/
556
- Nuxt → components/
557
- React → src/components/
558
- Vue → src/components/
559
-
560
- 스타일 디렉토리:
561
- SCSS → assets/scss/ or src/scss/ or src/styles/
562
- CSS → src/styles/ or styles/
563
- Tailwind → tailwind.config.* (extend)
564
- ```
565
-
566
- **Step 2: 피처명 기반 폴더 생성**
567
-
568
- Figma 파일명에서 피처명 자동 추출 → kebab-case 변환.
569
-
570
- ```
571
- 예: Figma 파일명 "PUBG 9주년 이벤트" → pubg-anniversary
572
-
573
- Nuxt 프로젝트:
574
- pages/pubg-anniversary.vue ← 루트 페이지
575
- components/pubg-anniversary/
576
- HeroSection.vue ← 섹션 컴포넌트
577
- EventSection.vue
578
- RewardSection.vue
579
- ...
580
- assets/scss/_pubg-anniversary-tokens.scss ← 피처별 토큰
581
-
582
- React + Next.js 프로젝트:
583
- app/pubg-anniversary/page.tsx ← 루트 페이지
584
- components/pubg-anniversary/
585
- HeroSection.tsx
586
- HeroSection.module.css (or .module.scss)
587
- EventSection.tsx
588
- ...
589
- styles/pubg-anniversary-tokens.css ← 피처별 토큰
590
-
591
- Default mode 차이점:
592
- → 토큰 파일에서 기존 MASTER.md / 프로젝트 토큰 참조
593
- → 새 토큰만 추가
594
-
595
- --new mode 차이점:
596
- → 토큰 파일이 자체 완결 (외부 의존 없음)
597
- → 다른 프로젝트에 폴더째 복사 가능
598
- ```
599
-
600
- ### 4-2. Token File Format
601
-
602
- **CSS Custom Properties (default):**
603
-
604
- ```css
605
- /* figma-tokens.css — Auto-generated from Figma. Do not edit manually. */
606
- /* Source: https://www.figma.com/design/{fileKey} */
607
-
608
- :root {
609
- /* Colors */
610
- --figma-primary: #3B82F6;
611
- --figma-primary-hover: #2563EB;
612
- --figma-surface: #FFFFFF;
613
- --figma-surface-secondary: #F9FAFB;
614
- --figma-text-primary: #111827;
615
- --figma-text-secondary: #6B7280;
616
- --figma-border: #E5E7EB;
617
-
618
- /* Typography */
619
- --figma-font-family: 'Inter', system-ui, sans-serif;
620
- --figma-text-xs: 0.75rem; /* 12px */
621
- --figma-text-sm: 0.875rem; /* 14px */
622
- --figma-text-base: 1rem; /* 16px */
623
- --figma-text-lg: 1.125rem; /* 18px */
624
- --figma-text-xl: 1.25rem; /* 20px */
625
- --figma-leading-tight: 1.25;
626
- --figma-leading-normal: 1.5;
627
-
628
- /* Spacing */
629
- --figma-space-1: 0.25rem; /* 4px */
630
- --figma-space-2: 0.5rem; /* 8px */
631
- --figma-space-3: 0.75rem; /* 12px */
632
- --figma-space-4: 1rem; /* 16px */
633
- --figma-space-6: 1.5rem; /* 24px */
634
- --figma-space-8: 2rem; /* 32px */
635
-
636
- /* Shadows */
637
- --figma-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
638
- --figma-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
639
-
640
- /* Border Radius */
641
- --figma-radius-sm: 0.25rem; /* 4px */
642
- --figma-radius-md: 0.5rem; /* 8px */
643
- --figma-radius-lg: 0.75rem; /* 12px */
644
- --figma-radius-full: 9999px;
645
- }
646
- ```
647
-
648
- **Tailwind extend (if Tailwind detected):**
649
-
650
- ```js
651
- // figma.config.ts — merge into tailwind.config.ts theme.extend
652
- export const figmaTokens = {
653
- colors: {
654
- figma: {
655
- primary: '#3B82F6',
656
- 'primary-hover': '#2563EB',
657
- // ...
658
- },
659
- },
660
- spacing: { /* ... */ },
661
- borderRadius: { /* ... */ },
662
- };
663
- ```
664
-
665
- **SCSS (if `*.scss` or `sass` detected):**
666
-
667
- ```scss
668
- // _figma-tokens.scss — Auto-generated from Figma. Do not edit manually.
669
-
670
- // ── Variables ──
671
- $figma-primary: #3B82F6;
672
- $figma-primary-hover: #2563EB;
673
- $figma-surface: #FFFFFF;
674
- $figma-text-primary: #111827;
675
- $figma-text-secondary: #6B7280;
676
- $figma-border: #E5E7EB;
677
-
678
- $figma-font-family: 'Inter', system-ui, sans-serif;
679
- $figma-text-xs: 0.75rem;
680
- $figma-text-sm: 0.875rem;
681
- $figma-text-base: 1rem;
682
- $figma-text-lg: 1.125rem;
683
- $figma-text-xl: 1.25rem;
684
-
685
- $figma-space-1: 0.25rem;
686
- $figma-space-2: 0.5rem;
687
- $figma-space-4: 1rem;
688
- $figma-space-6: 1.5rem;
689
- $figma-space-8: 2rem;
690
-
691
- $figma-radius-sm: 0.25rem;
692
- $figma-radius-md: 0.5rem;
693
- $figma-radius-lg: 0.75rem;
694
-
695
- $figma-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
696
- $figma-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
697
-
698
- // ── Breakpoints ──
699
- $figma-bp: 1024px;
700
- $figma-bp-mobile-min: 360px;
701
- $figma-bp-pc-target: 1920px;
702
-
703
- // ── Mixins ──
704
- @mixin figma-pc {
705
- @media (min-width: $figma-bp) { @content; }
706
- }
707
-
708
- @mixin figma-mobile-only {
709
- @media (max-width: $figma-bp - 1px) { @content; }
710
- }
711
-
712
- // ── Functions ──
713
- @function figma-fluid($mobile, $desktop, $min-vw: $figma-bp-mobile-min, $max-vw: $figma-bp-pc-target) {
714
- $slope: ($desktop - $mobile) / ($max-vw - $min-vw);
715
- $intercept: $mobile - $slope * $min-vw;
716
- @return clamp(#{$mobile}, #{$intercept} + #{$slope * 100}vw, #{$desktop});
717
- }
718
-
719
- // Usage: font-size: figma-fluid(1rem, 2rem);
720
- ```
721
-
722
- **SCSS 사용 시 추가 규칙:**
723
- - CSS custom properties 대신 `$변수` 사용 (프로젝트 컨벤션에 따라 둘 다 가능)
724
- - `@mixin figma-pc` 로 breakpoint 일관성 유지 — `@media` 직접 사용 금지
725
- - `figma-fluid()` 함수로 clamp() 계산 자동화 — 수동 계산 금지
726
- - 파일명: `_figma-tokens.scss` (partial, `_` prefix)
727
- - `@use 'figma-tokens' as figma;` 로 네임스페이스 import
728
-
729
- ### 4-3. Responsive Token Format (responsive mode only)
730
-
731
- When `responsive.json` exists, tokens that **differ across viewports** use `clamp()` for fluid scaling.
732
- Tokens that are **identical** across viewports remain static.
733
-
734
- **clamp() range uses breakpoints from Phase 3-3:**
735
-
736
- ```
737
- minVw = mobileMinimum (default: 360px)
738
- maxVw = pcTarget (default: 1920px)
739
- breakpoint = breakpoint (default: 1024px) ← used for @media
740
-
741
- Design values must be scaled before clamp:
742
- PC Figma value × (pcTarget / designPc) = target PC value
743
- Mobile Figma value × (mobilePortrait / designMobile) = target mobile value
744
- ```
745
-
746
- **CSS Custom Properties (responsive):**
747
-
748
- ```css
749
- /* figma-tokens.css — Responsive tokens from Figma */
750
- /* clamp range: {mobileMinimum}px → {pcTarget}px */
751
- /* Breakpoint: {breakpoint}px (PC↔Mobile) */
752
- /* Design scale: PC {designPc}→{pcTarget}, Mobile {designMobile}→{mobilePortrait} */
753
-
754
- :root {
755
- /* === Shared (same across all viewports) === */
756
- --figma-primary: #3B82F6;
757
- --figma-font-family: 'Inter', system-ui, sans-serif;
758
- --figma-radius-md: 0.5rem;
759
- --figma-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
760
-
761
- /* === Fluid Typography (scales with viewport) === */
762
- /* Figma PC 96px → target 36px, Figma Mobile 48px → target 32px */
763
- --figma-text-h1: clamp(2rem, {intercept}rem + {slope}vw, 2.25rem);
764
- --figma-text-body: clamp(0.875rem, {intercept}rem + {slope}vw, 1rem);
765
-
766
- /* === Fluid Spacing (scales with viewport) === */
767
- --figma-space-section: clamp(1rem, {intercept}rem + {slope}vw, 3rem);
768
- --figma-space-content: clamp(0.75rem, {intercept}rem + {slope}vw, 1.5rem);
769
-
770
- /* === Breakpoint (from config, user-customizable) === */
771
- --figma-bp: 1024px;
772
- }
773
- ```
774
-
775
- **clamp() calculation formula:**
776
-
777
- ```
778
- Step 1: Scale Figma values to target viewport
779
- targetMobile = figmaMobileValue × (mobilePortrait / designMobile)
780
- targetPc = figmaPcValue × (pcTarget / designPc)
781
-
782
- Example (defaults): Figma PC h1=96px, Figma Mobile h1=48px
783
- targetPc = 96 × (1920 / 2560) = 72px
784
- targetMobile = 48 × (480 / 720) = 32px
785
-
786
- Step 2: Calculate clamp()
787
- minVw = mobileMinimum (360)
788
- maxVw = pcTarget (1920)
789
- min = targetMobile, max = targetPc
790
-
791
- slope = (max - min) / (maxVw - minVw)
792
- intercept = min - slope * minVw
793
- → clamp({min/16}rem, {intercept/16}rem + {slope*100}vw, {max/16}rem)
794
-
795
- Example:
796
- slope = (72 - 32) / (1920 - 360) = 0.02564
797
- intercept = 32 - 0.02564 × 360 = 22.77
798
- → clamp(2rem, 1.423rem + 2.564vw, 4.5rem)
799
- ```
800
-
801
- **Tailwind (responsive — if Tailwind detected):**
802
-
803
- Use Tailwind's responsive prefixes instead of clamp() for layout, clamp() for typography/spacing:
804
-
805
- ```js
806
- export const figmaTokens = {
807
- fontSize: {
808
- 'figma-h1': ['clamp(1.5rem, 1.076rem + 1.878vw, 3rem)', { lineHeight: '1.2' }],
809
- 'figma-body': ['clamp(0.875rem, 0.828rem + 0.188vw, 1rem)', { lineHeight: '1.5' }],
810
- },
811
- spacing: {
812
- 'figma-section': 'clamp(1rem, 0.248rem + 3.286vw, 3rem)',
813
- },
814
- };
815
- ```
816
-
817
- **SCSS (responsive):**
818
-
819
- ```scss
820
- // _figma-tokens.scss — figma-fluid() 함수로 자동 계산
821
- @use 'sass:math';
822
-
823
- $figma-bp: 1024px;
824
- $figma-bp-mobile-min: 360px;
825
- $figma-bp-pc-target: 1920px;
826
-
827
- @function figma-fluid($mobile, $desktop, $min-vw: $figma-bp-mobile-min, $max-vw: $figma-bp-pc-target) {
828
- $slope: math.div($desktop - $mobile, $max-vw - $min-vw);
829
- $intercept: $mobile - $slope * $min-vw;
830
- @return clamp(#{$mobile}, #{$intercept} + #{$slope * 100}vw, #{$desktop});
831
- }
832
-
833
- @mixin figma-pc { @media (min-width: $figma-bp) { @content; } }
834
-
835
- // Token 사용
836
- $figma-text-h1: figma-fluid(2rem, 4.5rem);
837
- $figma-text-body: figma-fluid(0.875rem, 1rem);
838
- $figma-space-section: figma-fluid(1rem, 3rem);
839
- ```
840
-
841
- ```scss
842
- // Component.module.scss — 사용 예시
843
- @use 'figma-tokens' as figma;
844
-
845
- .heroSection {
846
- padding: figma.$figma-space-section;
847
- }
848
-
849
- .heroTitle {
850
- font-size: figma.$figma-text-h1;
851
- }
852
-
853
- .cardGrid {
854
- display: grid;
855
- grid-template-columns: 1fr;
856
-
857
- @include figma.figma-pc {
858
- grid-template-columns: repeat(3, 1fr);
859
- }
860
- }
861
- ```
862
-
863
- ### 4-4. Class Naming Rules
864
-
865
- 클래스 이름은 **역할(role)**을 드러내야 하며, 구조나 스타일 속성을 이름에 넣지 않는다.
866
-
867
- #### 네이밍 원칙
868
-
869
- | 원칙 | 좋은 예 | 나쁜 예 |
870
- |------|--------|--------|
871
- | **역할 기반** | `.heroSection`, `.productCard`, `.navPrimary` | `.section1`, `.card`, `.nav` |
872
- | **용도 명시** | `.heroBg`, `.cardThumbnail`, `.avatarImg` | `.bg`, `.img`, `.image1` |
873
- | **상태 포함** | `.buttonPrimary`, `.inputError` | `.blueButton`, `.redBorder` |
874
- | **관계 표현** | `.heroTitle`, `.heroDescription` | `.title`, `.text` |
875
- | **축약 금지** | `.navigationMenu`, `.backgroundImage` | `.navMnu`, `.bgImg` |
876
-
877
- #### 구체적 규칙
878
-
879
- ```
880
- 1. 컴포넌트 루트: 섹션/컴포넌트 이름 그대로
881
- .loginForm, .heroSection, .productGrid
92
+ ## 코드 생성 규칙 (각 Step에서 필요 참조)
882
93
 
883
- 2. 자식 요소: 부모이름 + 역할
884
- .heroTitle, .heroDescription, .heroCta
885
- .loginFormInput, .loginFormSubmit
94
+ | Skill | 내용 | 참조 시점 |
95
+ |-------|------|----------|
96
+ | `vibe-figma-rules` | 분석/감지 (Model Routing, Phase 1-3) | Step A, B |
97
+ | `vibe-figma-style` | 스타일 (토큰, SCSS, 클래스 네이밍) | Step B, C |
98
+ | `vibe-figma-codegen` | 코드 생성 (마크업, 이미지, 반응형, 검증) | Step B, C |
886
99
 
887
- 3. 이미지 클래스: 반드시 용도를 명시
888
- .heroBg ← 히어로 배경 이미지
889
- .heroBgOverlay ← 배경 위 오버레이
890
- .productPhoto ← 상품 사진
891
- .brandLogo ← 브랜드 로고
100
+ ## Design Quality Pipeline
892
101
 
893
- 4. 상태 변형: variant/state 접미사
894
- .buttonPrimary, .buttonDisabled
895
- .cardHighlight, .cardCompact
896
- ```
897
-
898
- #### Anti-Patterns
899
-
900
- ```css
901
- /* WRONG: 의미 없는 이름 */
902
- .wrapper { }
903
- .inner { }
904
- .box { }
905
- .item { }
906
- .text1 { }
907
-
908
- /* CORRECT: 역할이 드러나는 이름 */
909
- .eventSection { }
910
- .eventContent { }
911
- .rewardCard { }
912
- .rewardItem { }
913
- .eventDescription { }
914
- ```
915
-
916
- **Component style file MUST reference global tokens:**
917
-
918
- ```css
919
- /* LoginForm.module.css */
920
- .loginForm {
921
- padding: var(--figma-space-6);
922
- background: var(--figma-surface);
923
- border-radius: var(--figma-radius-lg);
924
- box-shadow: var(--figma-shadow-md);
925
- }
926
-
927
- .loginFormTitle {
928
- font-size: var(--figma-text-xl);
929
- font-weight: 600;
930
- color: var(--figma-text-primary);
931
- line-height: var(--figma-leading-tight);
932
- }
933
-
934
- .loginFormSubmit {
935
- background: var(--figma-primary);
936
- color: var(--figma-surface);
937
- border-radius: var(--figma-radius-md);
938
- padding: var(--figma-space-2) var(--figma-space-4);
939
- transition: background 150ms ease;
940
- }
941
-
942
- .loginFormSubmit:hover {
943
- background: var(--figma-primary-hover);
944
- }
945
- ```
946
-
947
- ---
948
-
949
- ## Phase 5: Markup Quality Standards
950
-
951
- ### 5-1. Semantic HTML (Mandatory)
952
-
953
- Every element MUST use the most specific semantic tag available. `<div>` is a last resort.
954
-
955
- | Visual Element | Correct Tag | Wrong |
956
- |---------------|------------|-------|
957
- | Page section | `<section>`, `<article>`, `<aside>` | `<div>` |
958
- | Navigation | `<nav>` | `<div class="nav">` |
959
- | Page header | `<header>` | `<div class="header">` |
960
- | Page footer | `<footer>` | `<div class="footer">` |
961
- | Heading hierarchy | `<h1>`→`<h6>` (sequential, no skips) | `<div class="title">` |
962
- | Paragraph text | `<p>` | `<div>` or `<span>` |
963
- | List of items | `<ul>`/`<ol>` + `<li>` | `<div>` repeated |
964
- | Clickable action | `<button>` | `<div onClick>` |
965
- | Navigation link | `<a href>` | `<span onClick>` |
966
- | Form field | `<input>` + `<label>` | `<div contenteditable>` |
967
- | Image | `<img alt="descriptive">` or `<figure>` + `<figcaption>` | `<div style="background-image">` for content images |
968
- | Tabular data | `<table>` + `<thead>` + `<tbody>` | `<div>` grid |
969
- | Time/Date | `<time datetime>` | `<span>` |
970
- | Emphasized text | `<strong>`, `<em>` | `<span class="bold">` |
971
- | Grouped fields | `<fieldset>` + `<legend>` | `<div>` |
972
-
973
- ### 5-2. Accessibility Checklist
974
-
975
- Every generated component MUST pass:
976
-
977
- - [ ] All interactive elements keyboard-reachable (tab order)
978
- - [ ] `<button>` for actions, `<a>` for navigation — never reversed
979
- - [ ] `<img>` has descriptive `alt` (not "image", not filename)
980
- - [ ] Form `<input>` linked to `<label>` (via `htmlFor` / `id`)
981
- - [ ] Color contrast >= 4.5:1 (text), >= 3:1 (large text, UI controls)
982
- - [ ] Focus indicator visible on all interactive elements
983
- - [ ] `aria-label` on icon-only buttons
984
- - [ ] `role` attribute on custom interactive widgets
985
- - [ ] Heading hierarchy is sequential (no h1 → h3 skip)
986
- - [ ] `<ul>`/`<ol>` for any visually listed items
987
-
988
- ### 5-3. Wrapper Elimination (Fragment / template)
989
-
990
- **불필요한 래핑 태그를 제거**하고 프레임워크가 제공하는 투명 래퍼를 사용:
991
-
992
- | Stack | 투명 래퍼 | 사용 시점 |
993
- |-------|----------|----------|
994
- | React / Next.js | `<>...</>` 또는 `<React.Fragment>` | 형제 요소를 그룹핑할 때 (DOM에 노드 추가 안 함) |
995
- | Vue / Nuxt | `<template>` (컴포넌트 루트 이외) | `v-if`, `v-for` 로 여러 요소를 조건부 렌더링할 때 |
996
- | Svelte | `{#if}`, `{#each}` 블록 | 자체적으로 래핑 불필요 |
997
-
998
- ```tsx
999
- // WRONG: 불필요한 div 래핑
1000
- <div>
1001
- <Header />
1002
- <Main />
1003
- <Footer />
1004
- </div>
1005
-
1006
- // CORRECT: React Fragment
1007
- <>
1008
- <Header />
1009
- <Main />
1010
- <Footer />
1011
- </>
1012
- ```
1013
-
1014
- ```vue
1015
- <!-- WRONG: 불필요한 div 래핑 -->
1016
- <div v-if="showGroup">
1017
- <ItemA />
1018
- <ItemB />
1019
- </div>
1020
-
1021
- <!-- CORRECT: Vue template (DOM에 렌더링 안 됨) -->
1022
- <template v-if="showGroup">
1023
- <ItemA />
1024
- <ItemB />
1025
- </template>
1026
- ```
1027
-
1028
- **규칙**: 래핑 요소에 스타일이나 이벤트가 없으면 → Fragment/template 사용. 스타일이 있으면 → semantic 태그 사용.
1029
-
1030
- ### 5-4. Similar UI Consolidation (80% Rule)
1031
-
1032
- 디자인에서 **유사도 80% 이상**인 UI 패턴이 발견되면, 별도 컴포넌트로 분리하지 말고 **하나의 컴포넌트 + variant props/slots**으로 통합:
1033
-
1034
- ```
1035
- 유사도 판단 기준:
1036
- - 레이아웃 구조 동일
1037
- - 색상/크기/텍스트만 다름
1038
- → 하나의 컴포넌트 + props로 변형
1039
-
1040
- - 구조 자체가 다름 (요소 추가/제거, 레이아웃 방향 변경)
1041
- → 별도 컴포넌트
1042
- ```
1043
-
1044
- | Stack | 변형 방법 |
1045
- |-------|----------|
1046
- | React | `variant` prop + 조건부 className / style |
1047
- | Vue | `variant` prop + `<slot>` for 커스텀 영역 |
1048
- | Svelte | `variant` prop + `<slot>` |
1049
- | React Native | `variant` prop + StyleSheet 조건 선택 |
1050
-
1051
- ```tsx
1052
- // React — 하나의 Card 컴포넌트가 3가지 변형 처리
1053
- interface CardProps {
1054
- variant: 'default' | 'highlight' | 'compact';
1055
- title: string;
1056
- children: React.ReactNode;
1057
- }
1058
-
1059
- export function Card({ variant, title, children }: CardProps): JSX.Element {
1060
- return (
1061
- <article className={styles[variant]}>
1062
- <h3 className={styles.title}>{title}</h3>
1063
- {children}
1064
- </article>
1065
- );
1066
- }
1067
- ```
1068
-
1069
- ```vue
1070
- <!-- Vue — slot으로 커스텀 영역 제공 -->
1071
- <template>
1072
- <article :class="$style[variant]">
1073
- <h3 :class="$style.title">{{ title }}</h3>
1074
- <slot />
1075
- </article>
1076
- </template>
1077
-
1078
- <script setup lang="ts">
1079
- defineProps<{ variant: 'default' | 'highlight' | 'compact'; title: string }>();
1080
- </script>
1081
- ```
1082
-
1083
- ### 5-5. Component Structure Rules
1084
-
1085
- ```
1086
- Max nesting depth: 3 levels (container > group > element)
1087
- Max component length: 50 lines
1088
- Max props: 5 per component
1089
- ```
1090
-
1091
- **Split triggers:**
1092
-
1093
- | Signal | Action |
1094
- |--------|--------|
1095
- | Component > 50 lines | Split into sub-components |
1096
- | Repeated visual pattern (2+ times) | Extract shared component |
1097
- | **Similar pattern (80%+ match)** | **Single component + variant prop** |
1098
- | Distinct visual boundary (card, modal, form) | Own component + own style file |
1099
- | 3+ related props | Group into object prop or extract sub-component |
1100
-
1101
- ### 5-6. Markup Anti-Patterns (NEVER Generate)
1102
-
1103
- ```tsx
1104
- // WRONG: div soup
1105
- <div className="card">
1106
- <div className="card-header">
1107
- <div className="title">Login</div>
1108
- </div>
1109
- <div className="card-body">
1110
- <div className="input-group">
1111
- <div className="label">Email</div>
1112
- <div className="input"><input /></div>
1113
- </div>
1114
- </div>
1115
- </div>
1116
-
1117
- // CORRECT: semantic markup
1118
- <article className={styles.card}>
1119
- <header className={styles.header}>
1120
- <h2 className={styles.title}>Login</h2>
1121
- </header>
1122
- <form className={styles.body}>
1123
- <fieldset className={styles.fieldGroup}>
1124
- <label htmlFor="email" className={styles.label}>Email</label>
1125
- <input id="email" type="email" className={styles.input} />
1126
- </fieldset>
1127
- </form>
1128
- </article>
1129
- ```
1130
-
1131
- ```tsx
1132
- // WRONG: 불필요한 래핑
1133
- <div>
1134
- <ComponentA />
1135
- <ComponentB />
1136
- </div>
1137
-
1138
- // CORRECT: Fragment
1139
- <>
1140
- <ComponentA />
1141
- <ComponentB />
1142
- </>
1143
- ```
1144
-
1145
- ```tsx
1146
- // WRONG: 유사한 UI를 별도 컴포넌트로
1147
- <DefaultCard /> // 구조 동일, 색상만 다름
1148
- <HighlightCard /> // 구조 동일, 색상만 다름
1149
-
1150
- // CORRECT: 단일 컴포넌트 + variant
1151
- <Card variant="default" />
1152
- <Card variant="highlight" />
1153
- ```
1154
-
1155
- ---
1156
-
1157
- ## Phase 6: Code Generation
1158
-
1159
- ### 6-0. Apply Storyboard Spec + Design Context
1160
-
1161
- **스토리보드 스펙 (Phase 0-1)이 있으면 우선 적용:**
1162
-
1163
- | storyboardSpec | Effect on Code Generation |
1164
- |----------------|--------------------------|
1165
- | `interactions` | 호버/클릭/스크롤 이벤트 → CSS `:hover`/`:active`/`:focus` + JS 핸들러 |
1166
- | `animations` | 트랜지션/애니메이션 → `transition`, `@keyframes`, timing/easing 스펙대로 |
1167
- | `states` | 로딩/에러/성공/빈 상태 → 조건부 렌더링 + 상태별 UI 컴포넌트 |
1168
- | `breakpoints` | Phase 3-3에서 이미 적용됨 |
1169
- | `colorGuide` | 스토리보드 컬러 가이드 → 토큰 검증 |
1170
- | `typographyGuide` | 스토리보드 타이포 가이드 → 토큰 검증 |
1171
-
1172
- **design-context.json이 있으면 추가 적용:**
1173
-
1174
- | Context Field | Effect on Code Generation |
1175
- |---------------|--------------------------|
1176
- | `constraints.accessibility = "AAA"` | Use `aria-describedby` on all form fields, `prefers-reduced-motion` media query, `prefers-contrast` support |
1177
- | `constraints.devices = ["mobile"]` | Mobile-only layout, no desktop breakpoints, touch target ≥ 44px |
1178
- | `constraints.devices = ["mobile","desktop"]` | Mobile-first with `md:` breakpoint |
1179
- | `aesthetic.style = "minimal"` | Reduce visual weight — fewer shadows, thinner borders, more whitespace |
1180
- | `aesthetic.style = "bold"` | Stronger shadows, thicker borders, larger typography scale |
1181
- | `brand.personality` | Preserve brand-unique visual patterns (do NOT distill these away) |
1182
- | `detectedStack.fonts` | Use project's existing font stack instead of Figma's font family |
1183
-
1184
- ### 6-1. Stack-Specific Rules
1185
-
1186
- Generate code following these rules per stack:
1187
-
1188
- #### React / Next.js + TypeScript
1189
-
1190
- ```
1191
- - Functional components with explicit return types
1192
- - Props interface defined above component
1193
- - Named exports (not default)
1194
- - <></> Fragment for sibling grouping (no unnecessary wrapper div)
1195
- - CSS Modules: import styles from './Component.module.css'
1196
- - Tailwind: classes in JSX, extract repeated patterns to @apply
1197
- - Responsive: mobile-first breakpoints
1198
- - Variant pattern: discriminated union props for similar UI
1199
- - useMemo/useCallback only when measurably needed (not by default)
1200
- - Next.js: use 'use client' only when client interactivity needed
1201
- - Next.js: Image component for images, Link for navigation
1202
- ```
1203
-
1204
- #### Vue 3 / Nuxt
1205
-
1206
- ```
1207
- - <script setup lang="ts"> composition API
1208
- - defineProps with TypeScript interface (with defaults via withDefaults)
1209
- - <template> for invisible grouping (v-if, v-for on multiple elements)
1210
- - <style scoped> (or <style module>) with CSS custom property references
1211
- - v-bind in <style> for dynamic values: color: v-bind(themeColor)
1212
- - <slot> + named slots for composable variant patterns
1213
- - <Teleport> for modals/overlays
1214
- - Or Tailwind classes in template
1215
- - Nuxt: <NuxtLink> for navigation, <NuxtImg> for images
1216
- - Nuxt: definePageMeta for page-level config
1217
- - computed() for derived state (not methods for template expressions)
1218
- ```
1219
-
1220
- #### Svelte
1221
-
1222
- ```
1223
- - TypeScript in <script lang="ts">
1224
- - Export let for props with types
1225
- - {#if}/{#each}/{#await} blocks — inherently wrapper-free
1226
- - <slot> for composable patterns
1227
- - <style> block with CSS custom property references
1228
- - Or Tailwind classes in markup
1229
- - Reactive declarations ($:) for derived values
1230
- - transition: directive for animations
1231
- ```
1232
-
1233
- #### SCSS (any framework with SCSS)
1234
-
1235
- ```
1236
- - @use 'figma-tokens' as figma — namespaced import (not @import)
1237
- - $변수 for tokens: figma.$figma-primary, figma.$figma-space-4
1238
- - @include figma.figma-pc { } for breakpoint — @media 직접 사용 금지
1239
- - figma-fluid($mobile, $desktop) for responsive values — 수동 clamp() 금지
1240
- - Nesting max 3 levels: .section { .title { .icon { } } } ← 한계
1241
- - & for BEM-like modifiers: &--active, &__title
1242
- - @each for variant generation from map
1243
- - Partials: _figma-tokens.scss, _figma-mixins.scss
1244
- - Vue: <style lang="scss" scoped> with @use
1245
- ```
1246
-
1247
- ```scss
1248
- // Component usage example
1249
- @use 'figma-tokens' as figma;
1250
-
1251
- .heroSection {
1252
- position: relative;
1253
- padding: figma.figma-fluid(1rem, 3rem);
1254
-
1255
- &Title {
1256
- font-size: figma.figma-fluid(1.5rem, 3rem);
1257
- color: figma.$figma-text-primary;
1258
- }
1259
-
1260
- &Cta {
1261
- background: figma.$figma-primary;
1262
- border-radius: figma.$figma-radius-md;
1263
-
1264
- &:hover { background: figma.$figma-primary-hover; }
1265
- }
1266
- }
1267
-
1268
- .cardGrid {
1269
- display: grid;
1270
- grid-template-columns: 1fr;
1271
-
1272
- @include figma.figma-pc {
1273
- grid-template-columns: repeat(3, 1fr);
1274
- }
1275
- }
1276
- ```
1277
-
1278
- #### React Native
1279
-
1280
- ```
1281
- - StyleSheet.create at bottom of file
1282
- - <></> Fragment for sibling grouping
1283
- - Dimensions-aware responsive layout
1284
- - Platform.select / Platform.OS for platform-specific styles
1285
- - Extract shared style constants to styles/tokens.ts
1286
- ```
1287
-
1288
- #### Flutter (Dart)
1289
-
1290
- ```
1291
- - StatelessWidget or StatefulWidget as appropriate
1292
- - Theme.of(context) for design tokens
1293
- - Extract shared values to lib/theme/figma_tokens.dart
1294
- - Proper widget composition
1295
- ```
1296
-
1297
- ### 6-2. Image Code Patterns (from Phase 2-A classification)
1298
-
1299
- Phase 2-A에서 분류된 이미지 유형별 코드 생성 규칙:
1300
-
1301
- #### Background Image Class Separation (핵심 원칙)
1302
-
1303
- 배경 이미지 관련 요소는 **용도별로 별도 클래스**로 분리한다. 레이아웃과 이미지를 합치지 않는다.
1304
-
1305
- ##### 별도 클래스로 분리해야 하는 유형
1306
-
1307
- | 유형 | 클래스 예시 | 분리 이유 |
1308
- |------|-----------|----------|
1309
- | **섹션 전면 배경** | `.heroBg`, `.eventBg`, `.rewardsBg` | 이벤트 기간/시즌별 이미지 교체 |
1310
- | **오버레이** | `.heroBgOverlay`, `.eventBgOverlay` | 투명도/그라데이션 독립 조절 |
1311
- | **패턴/텍스처** | `.sectionPattern`, `.headerTexture` | `background-repeat`/`size` 별도 제어 |
1312
- | **파티클/장식 효과** | `.heroParticle`, `.sparkleEffect` | 애니메이션 on/off, 성능 이슈 시 제거 |
1313
- | **캐릭터/일러스트** | `.heroCharacter`, `.eventIllust` | 콜라보/캐릭터별 교체, position 조절 |
1314
- | **그라데이션** | `.sectionGradient`, `.fadeBottom` | 테마별 색상 변경 |
1315
- | **비디오 포스터** | `.videoPoster` | 비디오 로드 전 폴백, JS에서 교체 |
1316
-
1317
- ##### Multi-Layer Pattern (섹션 배경 기본 구조)
1318
-
1319
- Figma에서 배경 이미지가 있는 섹션은 다음 레이어 구조로 생성:
1320
-
1321
- ```
1322
- .{section}Section ← 레이아웃 (position, size, padding, overflow)
1323
- .{section}Bg ← 배경 이미지 (URL, size, position) — z-index: 0
1324
- .{section}BgOverlay ← 오버레이 (gradient, opacity) — z-index: 1
1325
- .{section}Character ← 캐릭터/일러스트 (선택) — z-index: 2
1326
- .{section}Pattern ← 패턴/텍스처 (선택) — z-index: 1
1327
- .{section}Content ← 텍스트/버튼/UI — z-index: 최상위
1328
- ```
1329
-
1330
- ```tsx
1331
- // 실제 마크업 예시 — 게임 이벤트 히어로 섹션
1332
- <section className={styles.heroSection}>
1333
- <div className={styles.heroBg} />
1334
- <div className={styles.heroBgOverlay} />
1335
- <div className={styles.heroCharacter} />
1336
- <div className={styles.heroContent}>
1337
- <h1 className={styles.heroTitle}>Stellar Blade × PUBG</h1>
1338
- <p className={styles.heroDescription}>기간한정 콜라보 이벤트</p>
1339
- <a href="#rewards" className={styles.heroCta}>보상 확인하기</a>
1340
- </div>
1341
- </section>
1342
- ```
1343
-
1344
- ```css
1345
- /* heroSection.module.css */
1346
- .heroSection { position: relative; overflow: hidden; min-height: 100vh; }
1347
-
1348
- .heroBg {
1349
- position: absolute; inset: 0; z-index: 0;
1350
- background-image: url('/assets/hero-bg.webp');
1351
- background-size: cover;
1352
- background-position: center;
1353
- }
1354
-
1355
- .heroBgOverlay {
1356
- position: absolute; inset: 0; z-index: 1;
1357
- background: linear-gradient(to bottom, rgba(0,0,0,0.3), rgba(0,0,0,0.7));
1358
- }
1359
-
1360
- .heroCharacter {
1361
- position: absolute; bottom: 0; right: 5%; z-index: 2;
1362
- width: 40%; height: 80%;
1363
- background-image: url('/assets/hero-character.webp');
1364
- background-size: contain;
1365
- background-position: bottom center;
1366
- background-repeat: no-repeat;
1367
- }
1368
-
1369
- .heroContent { position: relative; z-index: 3; }
1370
- ```
1371
-
1372
- **모든 배경 관련 이미지가 독립 클래스**이므로:
1373
- - JS에서 `.heroBg`만 교체하면 배경만 바뀜
1374
- - `.heroCharacter`만 교체하면 캐릭터만 바뀜
1375
- - `.heroBgOverlay`의 opacity를 조절하면 텍스트 가독성만 조절 가능
1376
- - 성능 이슈 시 `.heroParticle`만 `display: none`
1377
-
1378
- #### Responsive Background Image (반응형 배경 — PC/Mobile 분기)
1379
-
1380
- ```css
1381
- /* 배경 이미지 클래스 안에서만 반응형 처리 */
1382
- .heroBg {
1383
- background-image: url('/assets/hero-mobile.webp');
1384
- background-size: cover;
1385
- background-position: center;
1386
- position: absolute;
1387
- inset: 0;
1388
- z-index: 0;
1389
- }
1390
-
1391
- @media (min-width: 1024px) { /* {breakpoint}px */
1392
- .heroBg {
1393
- background-image: url('/assets/hero-pc.webp');
1394
- }
1395
- }
1396
- ```
1397
-
1398
- **섹션별 배경 이미지가 여러 개인 경우** — 각각 고유 클래스:
1399
-
1400
- ```css
1401
- .heroBg { background-image: url('/assets/hero-bg.webp'); /* ... */ }
1402
- .eventBg { background-image: url('/assets/event-bg.webp'); /* ... */ }
1403
- .rewardsBg { background-image: url('/assets/rewards-bg.webp'); /* ... */ }
1404
- ```
1405
-
1406
- **Figma 오버레이 감지**: 레이어에 `opacity < 1` 또는 `fills`에 반투명 색상이 IMAGE 위에 있으면 → `{section}BgOverlay` 클래스로 처리.
1407
-
1408
- #### Content Image (독립적인 이미지 콘텐츠)
1409
-
1410
- ```tsx
1411
- // React / Next.js
1412
- <img
1413
- src="/assets/product.webp"
1414
- alt="상품 설명" // Figma 레이어 이름 기반
1415
- width={600} // Figma 레이어 width (scaled)
1416
- height={400} // Figma 레이어 height (scaled)
1417
- loading="lazy" // 뷰포트 밖이면 lazy
1418
- />
1419
-
1420
- // Next.js — Image 컴포넌트 우선
1421
- import Image from 'next/image';
1422
- <Image
1423
- src="/assets/product.webp"
1424
- alt="상품 설명"
1425
- width={600}
1426
- height={400}
1427
- priority={false} // hero 이미지만 priority={true}
1428
- />
1429
- ```
1430
-
1431
- ```vue
1432
- <!-- Nuxt — NuxtImg 우선 -->
1433
- <NuxtImg
1434
- src="/assets/product.webp"
1435
- alt="상품 설명"
1436
- width="600"
1437
- height="400"
1438
- loading="lazy"
1439
- />
1440
- ```
1441
-
1442
- #### Responsive Content Image (뷰포트별 다른 이미지)
1443
-
1444
- ```html
1445
- <picture>
1446
- <source media="(min-width: 1024px)" srcset="/assets/hero-pc.webp" />
1447
- <img src="/assets/hero-mobile.webp" alt="히어로 이미지" loading="eager" />
1448
- </picture>
1449
- ```
1450
-
1451
- #### 이미지 공통 규칙
1452
-
1453
- | 규칙 | 설명 |
1454
- |------|------|
1455
- | **format** | `.webp` 우선 (fallback: `.png`/`.jpg`) |
1456
- | **alt** | Figma 레이어 이름에서 파생, 장식 이미지는 `alt=""` + `aria-hidden="true"` |
1457
- | **width/height** | 항상 명시 (CLS 방지), Figma 레이어 크기를 스케일 팩터 적용 후 사용 |
1458
- | **loading** | 뷰포트 상단(hero, header) → `eager`, 나머지 → `lazy` |
1459
- | **object-fit** | `cover` (배경/히어로), `contain` (로고/아이콘), `fill` 사용 금지 |
1460
- | **반응형 크기** | Content image는 `max-width: 100%; height: auto;` 기본 |
1461
- | **배경 이미지 + 텍스트** | 반드시 오버레이(`::before` 또는 gradient)로 텍스트 가독성 확보 |
1462
- | **장식 패턴** | `background-repeat: repeat` + `background-size` 조절 |
1463
-
1464
- #### Anti-Patterns
1465
-
1466
- ```tsx
1467
- // WRONG: 레이아웃 클래스에 background-image를 합침
1468
- <section className={styles.hero} /> /* .hero에 bg-image + padding + flex 등 다 섞임 */
1469
-
1470
- // CORRECT: 배경 이미지는 별도 클래스
1471
- <section className={styles.heroSection}>
1472
- <div className={styles.heroBg} />
1473
- <div className={styles.heroContent}>...</div>
1474
- </section>
1475
- ```
1476
-
1477
- ```tsx
1478
- // WRONG: inline style로 배경 이미지
1479
- <div style={{ backgroundImage: `url(${bg})` }} />
1480
-
1481
- // CORRECT: 전용 클래스에 이미지 URL
1482
- <div className={styles.heroBg} />
1483
- /* JS 동적 교체 시: element.style.backgroundImage = url(...) on .heroBg만 */
1484
- ```
1485
-
1486
- ```css
1487
- /* WRONG: 배경 + 텍스트, 오버레이 없음 */
1488
- .hero { background-image: url(...); color: white; }
1489
-
1490
- /* CORRECT: 3-layer 분리 (bg / overlay / content) */
1491
- .heroSection { position: relative; }
1492
- .heroBg { background-image: url(...); position: absolute; inset: 0; z-index: 0; }
1493
- .heroBgOverlay { position: absolute; inset: 0; background: rgba(0,0,0,0.5); z-index: 1; }
1494
- .heroContent { position: relative; z-index: 2; color: white; }
1495
- ```
1496
-
1497
- ```css
1498
- /* WRONG: 의미 없는 이름 */
1499
- .bg { }
1500
- .bgImg { }
1501
- .overlay { }
1502
-
1503
- /* CORRECT: 섹션별 명시적 이름 */
1504
- .heroBg { }
1505
- .heroBgOverlay { }
1506
- .eventBg { }
1507
- .rewardsBg { }
1508
- ```
1509
-
1510
- ### 6-3. Responsive Code Generation (responsive mode only)
1511
-
1512
- When `responsive.json` exists, apply these rules on top of stack-specific rules:
1513
-
1514
- #### Principle: Fluid First, Breakpoint Second
1515
-
1516
- ```
1517
- 1. Typography & Spacing → clamp() fluid tokens (no breakpoints needed)
1518
- 2. Layout direction changes → @media at project breakpoint (flex-direction, grid-template)
1519
- 3. Visibility toggles → @media at project breakpoint (display: none/block)
1520
- 4. Component swaps → conditional render with useMediaQuery or CSS
1521
- ```
1522
-
1523
- **Breakpoint selection**: Use `{breakpoints}` from Phase 3-3. Pick the breakpoint closest to where the layout structurally changes between Figma viewports. For example:
1524
- - Figma mobile=375px, desktop=1440px → use project's `md` (typically 768px) as primary breakpoint
1525
- - If project has `tablet: 1024px` → use that instead
1526
- - If 3 viewports (mobile/tablet/desktop) → use 2 breakpoints (e.g., `sm` and `lg`)
1527
-
1528
- #### CSS Modules (responsive example)
1529
-
1530
- ```css
1531
- /* Component.module.css */
1532
- /* Breakpoints from project: {breakpoints} */
1533
-
1534
- .container {
1535
- display: flex;
1536
- flex-direction: column; /* mobile-first */
1537
- gap: var(--figma-space-content); /* fluid: clamp() */
1538
- padding: var(--figma-space-section); /* fluid: clamp() */
1539
- }
1540
-
1541
- .title {
1542
- font-size: var(--figma-text-h1); /* fluid: clamp() */
1543
- line-height: 1.2;
1544
- }
1545
-
1546
- .cardGrid {
1547
- display: grid;
1548
- grid-template-columns: 1fr; /* mobile: single column */
1549
- gap: var(--figma-space-content);
1550
- }
1551
-
1552
- /* Layout breakpoint — use project's breakpoint, NOT hardcoded */
1553
- @media (min-width: 1024px) { /* {breakpoint}px from Phase 3-3 */ /* or project's md value */
1554
- .container {
1555
- flex-direction: row;
1556
- }
1557
- .cardGrid {
1558
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
1559
- }
1560
- }
1561
-
1562
- /* Visibility toggle — same breakpoint */
1563
- .mobileOnly { display: block; }
1564
- .desktopOnly { display: none; }
1565
-
1566
- @media (min-width: 1024px) { /* {breakpoint}px from Phase 3-3 */
1567
- .mobileOnly { display: none; }
1568
- .desktopOnly { display: block; }
1569
- }
1570
- ```
1571
-
1572
- #### Tailwind (responsive example)
1573
-
1574
- Use project's Tailwind screen prefixes (e.g., `sm:`, `md:`, `lg:`) — NOT hardcoded pixel values.
1575
-
1576
- ```tsx
1577
- {/* Uses project's Tailwind breakpoints — md: maps to project's screens.md */}
1578
- <div className="flex flex-col md:flex-row gap-[--figma-space-content] p-[--figma-space-section]">
1579
- <h1 className="text-[length:--figma-text-h1] leading-tight">Title</h1>
1580
- <div className="grid grid-cols-1 md:grid-cols-3 gap-[--figma-space-content]">
1581
- {/* cards */}
1582
- </div>
1583
- <nav className="md:hidden">Mobile Nav</nav>
1584
- <nav className="hidden md:flex">Desktop Nav</nav>
1585
- </div>
1586
- ```
1587
-
1588
- #### Anti-Patterns (NEVER do in responsive mode)
1589
-
1590
- | Wrong | Right |
1591
- |-------|-------|
1592
- | Separate mobile/desktop component files | Single component with responsive CSS |
1593
- | `@media` for font-size/spacing | `clamp()` via fluid tokens |
1594
- | Hardcoded `@media (min-width: 768px)` | Use project breakpoint from Phase 3-3 or Tailwind prefix |
1595
- | Pixel values in responsive styles | Token variables everywhere |
1596
- | Duplicated markup for each viewport | Visibility toggles or conditional sections |
1597
- | `window.innerWidth` checks in JS | CSS-only responsive (`@media`, `clamp()`, `auto-fit`) |
1598
- | Inventing new breakpoints | Use project's existing breakpoint system |
1599
-
1600
- ---
1601
-
1602
- ## Phase 7: Token Mapping (default mode)
1603
-
1604
- **Only in default (project integration) mode.** Map extracted Figma tokens to the project's existing token system.
1605
-
1606
- ### Token Source Priority
1607
-
1608
- ```
1609
- 1. MASTER.md (.claude/vibe/design-system/{project}/MASTER.md) ← 최우선
1610
- 2. design-context.json (.claude/vibe/design-context.json) ← 보조
1611
- 3. Project theme files (tailwind.config, CSS variables, etc.) ← 폴백
1612
- 4. Generate new figma-tokens ← 마지막 수단
1613
- ```
1614
-
1615
- ### Mapping Rules
1616
-
1617
- 1. **MASTER.md exists** → authoritative token source
1618
- - Figma color ≈ MASTER.md color (ΔE < 5) → use MASTER.md token name
1619
- - Figma spacing ≈ MASTER.md spacing (±2px) → use MASTER.md token name
1620
- - Figma font ≈ MASTER.md font → use MASTER.md token name
1621
- - Unmatched Figma values → add to `figma-tokens.css` as supplementary tokens
1622
-
1623
- 2. **No MASTER.md, but design-context.json exists** →
1624
- - Use `detectedStack` info for naming convention
1625
- - Use `aesthetic.colorMood` to validate token naming (e.g., warm palette → warm- prefix)
1626
- - Generate `figma-tokens.css` grouped by category
1627
-
1628
- 3. **No design system at all** →
1629
- - Generate `figma-tokens.css` (or Tailwind extend)
1630
- - Group tokens by category (color, typography, spacing, shadow)
1631
-
1632
- ### Output Mapping Comment
1633
-
1634
- Always output token mapping as a comment block at the top of the token file:
1635
-
1636
- ```
1637
- /* Figma Token Mapping:
1638
- * Figma "Primary/Default" → var(--figma-primary) = #3B82F6
1639
- * ✅ Matched: var(--color-blue-500) from MASTER.md
1640
- * Figma "Text/Body" → var(--figma-text-base) = 1rem / 1.5
1641
- * ✅ Matched: var(--text-base) from MASTER.md
1642
- * Figma "Accent/Glow" → var(--figma-accent-glow) = #7C3AED
1643
- * ⚠️ New token: no existing match
1644
- */
1645
- ```
1646
-
1647
- ---
1648
-
1649
- ## Phase 8: Correction Notes
1650
-
1651
- After generating code, output a brief correction report:
1652
-
1653
- ```markdown
1654
- ## Correction Notes
1655
-
1656
- ### Generation Mode
1657
- - Mode: default / --new / responsive
1658
- - Output directory: {path}
1659
- - Viewports: {list of viewport labels + widths, if responsive}
1660
-
1661
- ### Files Generated
1662
- | File | Type | Description |
1663
- |------|------|-------------|
1664
- | styles/tokens.css | Global tokens | {N} colors, {N} spacing, {N} typography |
1665
- | styles/global.css | Base styles | Reset + typography + layout |
1666
- | ComponentName/ComponentName.tsx | Component | Root component |
1667
- | ComponentName/ComponentName.module.css | Styles | Component-specific styles |
1668
-
1669
- ### Responsive Summary (responsive mode only)
1670
- | Token | Mobile | Desktop | Strategy |
1671
- |-------|--------|---------|----------|
1672
- | --figma-text-h1 | 24px | 48px | clamp() |
1673
- | --figma-space-section | 16px | 48px | clamp() |
1674
- | Card grid | 1col | 3col | @media grid |
1675
- | Sidebar | hidden | visible | @media display |
1676
-
1677
- - Fluid tokens generated: {N}
1678
- - Layout breakpoints used: {list}
1679
- - Component swaps: {list or "none"}
1680
-
1681
- ### Layer Issues Found
1682
- - [Layer name] was ambiguous → interpreted as [component] based on image
1683
- - [Layer structure] didn't match visual → used image-based layout
1684
-
1685
- ### Markup Quality
1686
- - Semantic tags used: {list}
1687
- - Accessibility: {pass/fail items}
1688
-
1689
- ### Recommendations for Figma File
1690
- - Use Auto Layout for [specific frame] to improve extraction accuracy
1691
- - Name layers semantically (e.g., "login-form" not "Frame 47")
1692
- - Use consistent spacing tokens
1693
- - (responsive) Keep same component names across mobile/desktop frames for easier mapping
1694
- ```
1695
-
1696
- ---
1697
-
1698
- ## Phase 9: Visual Verification Loop
1699
-
1700
- 코드 생성 완료 후, **Figma 원본 디자인과 생성된 UI를 비교**하여 완성도를 높이는 검증 루프.
1701
-
1702
- ### 9-1. 스크린샷 비교
1703
-
1704
- ```
1705
- 1. Figma 원본 스크린샷: Phase 0에서 get_screenshot으로 획득한 이미지
1706
- 2. 생성된 UI 스크린샷: /vibe.utils --preview 또는 브라우저 스크린샷
1707
- 3. 두 이미지를 side-by-side로 비교
1708
- ```
1709
-
1710
- ### 9-2. 차이점 검출
1711
-
1712
- 이미지를 비교하여 다음 항목 체크:
1713
-
1714
- | 검증 항목 | 비교 방법 |
1715
- |----------|----------|
1716
- | **레이아웃** | 요소 배치, 정렬, 간격이 원본과 일치하는가 |
1717
- | **타이포그래피** | 폰트 크기, 굵기, 줄간격이 원본과 일치하는가 |
1718
- | **색상** | 배경, 텍스트, 버튼 색상이 원본과 일치하는가 |
1719
- | **이미지** | 배경 이미지, 에셋이 올바르게 표시되는가 |
1720
- | **간격/여백** | padding, margin, gap이 원본과 일치하는가 |
1721
- | **반응형** | (responsive mode) 모바일/데스크탑 각각 원본과 일치하는가 |
1722
- | **누락 요소** | 원본에 있는데 생성 코드에 없는 요소가 있는가 |
1723
-
1724
- ### 9-3. Diff Report 출력
1725
-
1726
- ```markdown
1727
- ## Visual Diff Report
1728
-
1729
- ### Match Score: {N}% (목표: 95%+)
1730
-
1731
- ### 불일치 항목
1732
- | # | 섹션 | 항목 | Figma 원본 | 생성 코드 | 심각도 |
1733
- |---|------|------|-----------|----------|--------|
1734
- | 1 | Hero | 제목 font-size | 48px | 36px | P1 |
1735
- | 2 | Hero | 배경 이미지 | 표시됨 | 누락 | P1 |
1736
- | 3 | Card | border-radius | 12px | 8px | P2 |
1737
-
1738
- ### 수정 필요
1739
- - P1: {count}건 (반드시 수정)
1740
- - P2: {count}건 (권장 수정)
1741
- ```
1742
-
1743
- ### 9-4. Auto-Fix Loop
1744
-
1745
- ```
1746
- P1 불일치가 있으면:
1747
- 1. 해당 컴포넌트/스타일 파일을 수정
1748
- 2. 다시 스크린샷 비교
1749
- 3. P1이 0이 될 때까지 반복 (횟수 제한 없음)
1750
-
1751
- 수렴 감지:
1752
- - 이전 라운드와 동일한 P1 항목이 반복되면 → 접근 방식 변경
1753
- - 같은 항목이 3회 연속 미해결 → 해당 항목만 사용자에게 확인 요청 후 계속
1754
- ```
1755
-
1756
- ### 9-5. 검증 완료 조건
1757
-
1758
- ```
1759
- ✅ Match Score 95% 이상
1760
- ✅ P1 불일치 0건
1761
- ✅ 모든 이미지 에셋 표시 확인
1762
- ✅ (responsive) 모바일 + 데스크탑 각각 95% 이상
1763
- ```
1764
-
1765
- ---
1766
-
1767
- ## Refine Mode (`--refine`)
1768
-
1769
- 이전 `/vibe.figma` 실행으로 생성된 코드의 완성도가 부족할 때 사용.
1770
- **새로 만들지 않고, 기존 코드를 Figma 원본과 재비교하여 수정만 한다.**
1771
-
1772
- ### Refine 플로우
1773
-
1774
- ```
1775
- Step 1: URL 재입력
1776
- → 이전과 동일한 방식으로 스토리보드/디자인 URL 입력
1777
- → 또는 "이전과 동일" 입력 시 이전 URL 재사용
1778
-
1779
- Step 2: 기존 코드 스캔
1780
- → 프로젝트에서 이전에 생성된 파일 탐색 (pages/, components/ 내 피처 폴더)
1781
- → 생성된 컴포넌트 목록 파악
1782
-
1783
- Step 3: Figma 원본 재추출
1784
- → get_design_context + get_screenshot으로 최신 디자인 데이터 획득
1785
-
1786
- Step 4: Side-by-side 비교
1787
- → Figma 스크린샷 vs 기존 코드의 렌더링 결과
1788
- → Phase 9 검증 루프와 동일한 비교 항목:
1789
- 레이아웃, 타이포, 색상, 이미지, 간격, 누락 요소
1790
-
1791
- Step 5: Diff 기반 수정
1792
- → 변경이 필요한 파일만 Edit
1793
- → 새 파일 생성 최소화
1794
- → 수정 사유를 주석으로 남기지 않음 (코드만 수정)
1795
-
1796
- Step 6: 재검증
1797
- → Phase 9 검증 루프 실행 (P1=0 될 때까지)
1798
- ```
1799
-
1800
- ### Refine에서 수정하는 항목
1801
-
1802
- | 카테고리 | 수정 내용 |
1803
- |----------|----------|
1804
- | **누락 에셋** | 다운로드 안 된 이미지 재다운로드 + 경로 설정 |
1805
- | **레이아웃 불일치** | flex/grid 방향, 정렬, 간격 보정 |
1806
- | **타이포 불일치** | font-size, weight, line-height, letter-spacing 보정 |
1807
- | **색상 불일치** | 배경, 텍스트, 보더 색상 보정 |
1808
- | **누락 컴포넌트** | Figma에 있는데 코드에 없는 섹션 추가 |
1809
- | **인터랙션 누락** | 호버/포커스/액티브 상태 추가 |
1810
- | **반응형 누락** | 브레이크포인트 대응 미흡한 부분 보정 |
1811
- | **이미지 경로** | 임시 URL → 로컬 경로 교체, 누락 이미지 다운로드 |
1812
-
1813
- ### Refine에서 하지 않는 것
1814
-
1815
- ```
1816
- ❌ 파일 구조 변경 (이미 생성된 폴더/파일 구조 유지)
1817
- ❌ 컴포넌트 분리/통합 (구조적 리팩토링은 수동으로)
1818
- ❌ 토큰 파일 재생성 (기존 토큰에 누락분만 추가)
1819
- ❌ 전체 재작성 (diff 기반 최소 수정만)
1820
- ```
1821
-
1822
- ---
1823
-
1824
- ## Tool Usage Rules
1825
-
1826
- | Tool | When |
1827
- |------|------|
1828
- | **MCP: get_design_context** | Figma 추출 — 코드 + 스크린샷 + 메타데이터 (토큰 불필요) |
1829
- | **MCP: get_metadata** | 레이어 구조 XML (노드 ID, 크기, 위치) |
1830
- | **MCP: get_screenshot** | 프레임 이미지 렌더링 |
1831
- | Read | Project config, existing components, design-context.json |
1832
- | Glob | Find existing components, theme files, design tokens |
1833
- | Grep | Search for existing color/spacing/typography definitions |
1834
- | Write | Create new component files and style files |
1835
- | Edit | Update existing theme/token files to add new tokens (default mode) |
1836
-
1837
- ## Important
1838
-
1839
- - **Never guess colors** — extract from layers.json or image analysis
1840
- - **Never invent spacing** — use extracted values mapped to token scale
1841
- - **Never hardcode values** — all visual properties reference token variables
1842
- - **Preserve existing patterns** — match the project's existing component style (default mode)
1843
- - **Image is truth** — when layer structure is confusing, trust what the image shows
1844
- - **Ask before overwriting** — if a component file already exists, ask the user first
1845
- - **No console.log** — never include debug logging in generated code
1846
- - **No div soup** — every element uses the correct semantic tag
1847
- - **Component size limit** — split components exceeding 50 lines
1848
- - **Style separation** — global tokens file + per-component style files, always
1849
-
1850
- ## Next Steps: Design Quality Pipeline
1851
-
1852
- After generating code, present the following pipeline to the user:
1853
-
1854
- ### Quick (default recommendation)
1855
- ```
1856
- /design-normalize → /design-audit
1857
- ```
1858
- - Normalize: 하드코딩 값 → MASTER.md 토큰으로 치환
1859
- - Audit: A11Y + 성능 + 반응형 + AI Slop 검출
1860
-
1861
- ### Thorough (recommended for production)
1862
- ```
1863
- /design-normalize → /design-audit → /design-critique → /design-polish
1864
- ```
1865
- - + Critique: Nielsen 10 휴리스틱 + 5 페르소나 분석
1866
- - + Polish: 인터랙션 상태 보완 (hover/focus/loading/error)
1867
-
1868
- ### Pre-requisite check
1869
- If `.claude/vibe/design-context.json` does NOT exist:
1870
- ```
1871
- ⚠️ 디자인 컨텍스트가 없습니다. /design-teach 를 먼저 실행하면
1872
- 브랜드, 접근성, 타겟 디바이스에 맞춘 더 정확한 코드를 생성할 수 있습니다.
1873
- ```
1874
-
1875
- ### Output format
1876
- ```
1877
- ## 🎨 Design Quality Pipeline
1878
-
1879
- 생성된 파일: {file list}
1880
-
1881
- 추천 다음 단계:
1882
- 1. /design-normalize — 토큰 정렬 (하드코딩 제거)
1883
- 2. /design-audit — 기술 품질 검사
1884
- 3. /design-critique — UX 휴리스틱 리뷰
1885
- 4. /design-polish — 최종 인터랙션 보완
1886
-
1887
- 💡 /design-teach 가 아직 설정되지 않았다면 먼저 실행하세요.
1888
- ```
102
+ 코드 생성 완료 — Load skill `vibe-figma-pipeline`