@su-record/vibe 2.8.17 → 2.8.19
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.
- package/dist/infra/lib/config/GlobalConfigManager.d.ts +8 -0
- package/dist/infra/lib/config/GlobalConfigManager.d.ts.map +1 -1
- package/dist/infra/lib/config/GlobalConfigManager.js +37 -0
- package/dist/infra/lib/config/GlobalConfigManager.js.map +1 -1
- package/hooks/scripts/context-save.js +88 -1
- package/hooks/scripts/pre-tool-guard.js +30 -5
- package/hooks/scripts/sentinel-guard.js +28 -4
- package/hooks/scripts/utils.js +48 -0
- package/package.json +1 -1
- package/skills/vibe-figma/SKILL.md +25 -66
- package/skills/vibe-figma-analyze/SKILL.md +95 -318
- package/skills/vibe-figma-codegen/SKILL.md +112 -782
- package/skills/vibe-figma-consolidate/SKILL.md +53 -90
- package/skills/vibe-figma-frame/SKILL.md +454 -181
- package/skills/vibe-figma-pipeline/SKILL.md +4 -45
- package/skills/vibe-figma-rules/SKILL.md +363 -159
- package/skills/vibe-figma-style/SKILL.md +107 -476
|
@@ -1,28 +1,94 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vibe-figma-rules
|
|
3
|
-
description: Figma to Code
|
|
3
|
+
description: Figma to Code 공통 규칙 — Model Routing, 스택 감지, 브레이크포인트, 이미지 분류, 컴포넌트 통합
|
|
4
4
|
triggers: []
|
|
5
5
|
tier: standard
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# Skill: vibe-figma-rules —
|
|
8
|
+
# Skill: vibe-figma-rules — 공통 규칙 (Single Source of Truth)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
모든 vibe-figma-* 스킬이 참조하는 공통 규칙 정의.
|
|
11
|
+
중복 방지를 위해 **이 파일에만 정의**하고, 다른 스킬에서는 "vibe-figma-rules R-N 참조"로 사용.
|
|
11
12
|
|
|
12
13
|
---
|
|
13
14
|
|
|
14
|
-
##
|
|
15
|
+
## R-0. 설계 철학
|
|
16
|
+
|
|
17
|
+
> **비정형 레이어에서도 원본 디자인 수준의 완성도를 달성한다.**
|
|
18
|
+
|
|
19
|
+
실무 Figma 파일의 현실:
|
|
20
|
+
```
|
|
21
|
+
"Frame 633372" ← 의미 없는 자동 이름
|
|
22
|
+
"Frame 633371" ← 중첩된 의미 없는 프레임
|
|
23
|
+
"Frame 633370" ← AI가 구조를 파악할 수 없음
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
레이어명이 "Frame 47", "Group 12"이고, Auto Layout 없이 절대 배치된 프레임이 대부분.
|
|
27
|
+
그러나 `get_design_context`는 레이어가 비정형이어도 **스타일 토큰 값(색상, 폰트, 간격)과
|
|
28
|
+
에셋 URL은 정확하게 반환**한다. 이 값들은 디자이너가 Figma UI에서 보는 것과 동일한 값이다.
|
|
29
|
+
|
|
30
|
+
### 역할 분담
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
스크린샷이 답하는 것 (구조):
|
|
34
|
+
— WHAT goes WHERE: 레이아웃 구조, 섹션 경계, 이미지 배치, z-index, 겹침 관계
|
|
35
|
+
— 어떤 요소가 배경인지 콘텐츠인지, 어디에 오버레이가 있는지
|
|
36
|
+
— 참조 코드의 구조가 스크린샷과 다르면 → 스크린샷이 맞다
|
|
37
|
+
|
|
38
|
+
참조 코드가 답하는 것 (정확한 값):
|
|
39
|
+
— EXACT VALUES: hex 색상, font-size(px), font-weight, line-height,
|
|
40
|
+
padding, gap, margin, border-radius, shadow, 에셋 URL
|
|
41
|
+
— 이 값들은 Figma 디자인 토큰에서 직접 추출된 것이므로 정확하다
|
|
42
|
+
— 스크린샷에서 추정할 필요 없이 참조 코드의 값을 그대로 사용한다
|
|
43
|
+
— 단, px 값에는 스케일 팩터를 적용해야 한다 (R-3)
|
|
44
|
+
|
|
45
|
+
스크린샷이 검증하는 것 (최종 확인):
|
|
46
|
+
— 생성 코드의 렌더링 결과가 원본 스크린샷과 시각적으로 일치하는지
|
|
47
|
+
— Match Score 95%+, P1=0이 완료 기준
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 핵심 원칙
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
1. 참조 코드의 스타일 값을 반드시 사용한다
|
|
54
|
+
— get_design_context가 반환하는 색상/폰트/간격 값은 Figma 토큰에서 온 정확한 값
|
|
55
|
+
— 스크린샷에서 색상을 추정하거나 폰트 크기를 눈대중으로 읽지 않는다
|
|
56
|
+
— 참조 코드의 값 + 스케일 팩터 적용 = 코드에 쓸 값
|
|
57
|
+
|
|
58
|
+
2. 스크린샷으로 구조를 결정한다
|
|
59
|
+
— 레이어 구조("Frame 633372")가 무의미해도 스크린샷에서 섹션 경계를 파악
|
|
60
|
+
— 참조 코드의 HTML 구조가 스크린샷과 다르면 → 스크린샷 기준으로 재구성
|
|
61
|
+
— 이미지 배치(배경/콘텐츠/오버레이)도 스크린샷에서 판단
|
|
62
|
+
|
|
63
|
+
3. 스크린샷으로 누락을 잡는다
|
|
64
|
+
— 참조 코드에 없는 시각 요소가 스크린샷에 보이면 → 추가 구현
|
|
65
|
+
— 이미지 인벤토리는 스크린샷에서 작성 (참조 코드에 없는 이미지도 잡기 위해)
|
|
66
|
+
|
|
67
|
+
4. 검증은 항상 시각적 비교로
|
|
68
|
+
— 완료 기준: 원본 스크린샷 vs 생성 코드의 Match Score 95%+, P1=0
|
|
69
|
+
— 코드의 "정확성"이 아니라 "보이는 결과"가 기준이다
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### MCP 도구별 역할
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
get_design_context → 정확한 스타일 값 + 에셋 URL (코드 작성의 데이터 소스)
|
|
76
|
+
get_screenshot → 구조 파악 + 누락 검출 + 최종 시각 검증 (구조의 진실)
|
|
77
|
+
get_metadata → 프레임 목록 + nodeId 확보 (탐색 용도)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## R-1. Model Routing
|
|
15
83
|
|
|
16
84
|
| Step | Claude | GPT (Codex) | 이유 |
|
|
17
85
|
|------|--------|-------------|------|
|
|
18
86
|
| Step A (스토리보드 + 구조) | **Haiku → Sonnet** | — | MCP 추출 → 구조 설계 |
|
|
19
|
-
| Step B (
|
|
20
|
-
| Step B (
|
|
21
|
-
| Step B (
|
|
22
|
-
| Step C (
|
|
23
|
-
| Step
|
|
24
|
-
| Step C (PC 검증) | **Sonnet** | — | 양쪽 뷰포트 비교 |
|
|
25
|
-
| Step D (공통화) | **Sonnet** | — | 리팩토링 + 최종 검증 |
|
|
87
|
+
| Step B (디자인 추출) | **Haiku** | — | MCP + 이미지 다운로드 |
|
|
88
|
+
| Step B (코드 생성) | **Sonnet** | **gpt-5.3-codex-spark** (병렬) | 섹션별 컴포넌트 생성 |
|
|
89
|
+
| Step B (검증) | **Sonnet** | — | 이미지 비교 + auto-fix |
|
|
90
|
+
| Step C (추가 뷰포트) | **Haiku → Sonnet** | — | 반응형 레이어 추가 |
|
|
91
|
+
| Step D (공통화 + 검증) | **Sonnet** | — | 리팩토링 + 최종 검증 |
|
|
26
92
|
| Post (코드 리뷰) | — | **gpt-5.3-codex** | 전체 코드 품질 검증 |
|
|
27
93
|
|
|
28
94
|
### GPT 모델 선택 기준
|
|
@@ -33,227 +99,365 @@ tier: standard
|
|
|
33
99
|
| `gpt-5.3-codex` | `models.gptCodex` | 코드 리뷰, 분석 (정확도 우선) |
|
|
34
100
|
| `gpt-5.3-codex-spark` | `models.gptCodexSpark` | 코드 생성 (속도 우선) |
|
|
35
101
|
|
|
36
|
-
`~/.vibe/config.json`의 `models
|
|
102
|
+
`~/.vibe/config.json`의 `models`에서 오버라이드 가능.
|
|
37
103
|
|
|
38
104
|
---
|
|
39
105
|
|
|
40
|
-
##
|
|
106
|
+
## R-2. 프로젝트 스택 감지
|
|
41
107
|
|
|
42
|
-
###
|
|
108
|
+
### 2-1. Detect Stack
|
|
43
109
|
|
|
44
|
-
Read
|
|
110
|
+
1. Read `.claude/vibe/config.json` → `stacks` 필드
|
|
111
|
+
2. config 없으면 프로젝트 파일에서 감지:
|
|
45
112
|
|
|
46
|
-
|
|
|
47
|
-
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
113
|
+
| 감지 대상 | 파일 패턴 |
|
|
114
|
+
|-----------|----------|
|
|
115
|
+
| React | `package.json` → `react` in deps |
|
|
116
|
+
| Vue | `package.json` → `vue` in deps |
|
|
117
|
+
| Next.js | `next.config.*` |
|
|
118
|
+
| Nuxt | `nuxt.config.*` |
|
|
119
|
+
| Svelte | `svelte.config.*` |
|
|
120
|
+
| Tailwind | `tailwind.config.*` |
|
|
121
|
+
| CSS Modules | `*.module.css` 패턴 |
|
|
122
|
+
| SCSS | `*.scss` 또는 `sass` in deps |
|
|
123
|
+
| CSS-in-JS | `styled-components` / `@emotion` in deps |
|
|
55
124
|
|
|
56
|
-
###
|
|
125
|
+
### 2-2. 디렉토리 감지
|
|
57
126
|
|
|
58
|
-
|
|
127
|
+
| 용도 | Next.js | Nuxt | React | Vue |
|
|
128
|
+
|------|---------|------|-------|-----|
|
|
129
|
+
| 페이지 | `pages/` or `app/` | `pages/` | `src/pages/` or `src/views/` | `src/views/` |
|
|
130
|
+
| 컴포넌트 | `components/` or `src/components/` | `components/` | `src/components/` | `src/components/` |
|
|
131
|
+
| 스타일 | SCSS: `assets/scss/` or `src/styles/`, CSS: `src/styles/`, Tailwind: `tailwind.config.*` |
|
|
59
132
|
|
|
60
|
-
|
|
61
|
-
|--------|-----------------|
|
|
62
|
-
| Layout shift | Which elements reflow? (e.g., horizontal → vertical stack) |
|
|
63
|
-
| Visibility | Which elements hide/show per viewport? |
|
|
64
|
-
| Typography scale | Font size ratio between viewports |
|
|
65
|
-
| Spacing scale | Padding/gap ratio between viewports |
|
|
66
|
-
| Component shape | Does the component change form? (e.g., drawer → sidebar) |
|
|
67
|
-
| Navigation | Does nav change? (e.g., hamburger ↔ full nav bar) |
|
|
133
|
+
### 2-3. Design Context 로드
|
|
68
134
|
|
|
69
|
-
|
|
135
|
+
순서대로 읽음 (나중 소스가 우선):
|
|
70
136
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
| Hero title | 24px | 48px | fluid: clamp() |
|
|
76
|
-
| Card grid | 1 column | 3 columns | grid auto-fit |
|
|
77
|
-
| Sidebar | hidden | visible | display toggle |
|
|
78
|
-
| Body text | 14px | 16px | fluid: clamp() |
|
|
79
|
-
| Padding | 16px | 48px | fluid: clamp() |
|
|
80
|
-
```
|
|
137
|
+
1. **`.claude/vibe/design-context.json`** — 브랜드, 접근성, 디바이스 제약
|
|
138
|
+
2. **`.claude/vibe/design-system/{project}/MASTER.md`** — 토큰 정의
|
|
139
|
+
|
|
140
|
+
**결정 규칙**: Figma 토큰 ≈ 기존 MASTER.md 토큰 (색상 ΔE < 5 또는 간격 ±2px) → 기존 토큰 사용, 중복 생성 금지.
|
|
81
141
|
|
|
82
142
|
---
|
|
83
143
|
|
|
84
|
-
##
|
|
144
|
+
## R-3. 브레이크포인트 + 스케일 팩터
|
|
85
145
|
|
|
86
|
-
###
|
|
146
|
+
### 소스 우선순위
|
|
87
147
|
|
|
88
|
-
|
|
148
|
+
| 순위 | 소스 | 방법 |
|
|
149
|
+
|------|------|------|
|
|
150
|
+
| 1 | **스토리보드** | Step A에서 추출한 `storyboardSpec.breakpoints` |
|
|
151
|
+
| 2 | **`~/.vibe/config.json`** | `figma.breakpoints` (CLI로 커스터마이즈) |
|
|
152
|
+
| 3 | **프로젝트 CSS/Tailwind** | `tailwind.config.*` → `theme.screens`, 또는 `@media` 패턴 |
|
|
153
|
+
| 4 | **기본값** | 아래 테이블 |
|
|
89
154
|
|
|
90
|
-
|
|
91
|
-
2. **Design tokens** — Colors (fill, stroke), font properties, spacing values, border radius, shadows
|
|
92
|
-
3. **Auto-layout** — Direction, gap, padding (maps directly to flex/grid)
|
|
93
|
-
4. **Constraints** — Fixed vs fluid sizing
|
|
94
|
-
5. **Component instances** — Identify reusable patterns
|
|
95
|
-
6. **Image fills** — Identify layers with `type: "IMAGE"` fills (see Phase 2-A)
|
|
155
|
+
### 기본 브레이크포인트
|
|
96
156
|
|
|
97
|
-
|
|
157
|
+
```
|
|
158
|
+
breakpoint: 1024px ← PC↔Mobile 경계 (@media min-width)
|
|
159
|
+
pcTarget: 1920px ← PC 메인 타겟
|
|
160
|
+
mobilePortrait: 480px ← Mobile portrait max
|
|
161
|
+
mobileMinimum: 360px ← Mobile 최소 지원
|
|
162
|
+
designPc: 2560px ← Figma PC 아트보드 (디자인 2x 스케일)
|
|
163
|
+
designMobile: 720px ← Figma Mobile 아트보드 (디자인 2x 스케일)
|
|
164
|
+
```
|
|
98
165
|
|
|
99
|
-
|
|
166
|
+
### 스케일 팩터 계산
|
|
100
167
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"desktop": { "h1": 48, "body": 16, "padding": 48 } }
|
|
105
|
-
```
|
|
106
|
-
2. **Layout differences** — Auto-layout direction changes (e.g., HORIZONTAL → VERTICAL)
|
|
107
|
-
3. **Visibility map** — Which layers exist in one viewport but not the other
|
|
108
|
-
4. **Shared tokens** — Values identical across all viewports (colors, border-radius, shadows are usually shared)
|
|
168
|
+
```
|
|
169
|
+
PC: scaleFactor.pc = pcTarget / designPc (기본: 1920/2560 = 0.75)
|
|
170
|
+
Mobile: scaleFactor.mobile = mobilePortrait / designMobile (기본: 480/720 = 0.667)
|
|
109
171
|
|
|
110
|
-
|
|
172
|
+
적용: 코드 값 = Figma 추출 값 × scaleFactor
|
|
173
|
+
- 색상 hex → 스케일 불필요
|
|
174
|
+
- 타이포 (font-size, line-height) → 스케일 적용
|
|
175
|
+
- 간격 (padding, gap, margin) → 스케일 적용
|
|
176
|
+
```
|
|
111
177
|
|
|
112
|
-
|
|
178
|
+
### clamp() 계산 공식
|
|
113
179
|
|
|
114
|
-
|
|
180
|
+
```
|
|
181
|
+
Step 1: Figma 값 → 타겟 값
|
|
182
|
+
targetMobile = figmaMobileValue × scaleFactor.mobile
|
|
183
|
+
targetPc = figmaPcValue × scaleFactor.pc
|
|
184
|
+
|
|
185
|
+
Step 2: clamp() 생성
|
|
186
|
+
minVw = mobileMinimum (360px)
|
|
187
|
+
maxVw = pcTarget (1920px)
|
|
188
|
+
slope = (targetPc - targetMobile) / (maxVw - minVw)
|
|
189
|
+
intercept = targetMobile - slope × minVw
|
|
190
|
+
→ clamp({targetMobile/16}rem, {intercept/16}rem + {slope×100}vw, {targetPc/16}rem)
|
|
191
|
+
```
|
|
115
192
|
|
|
116
|
-
|
|
193
|
+
### CLI 커스터마이즈
|
|
117
194
|
|
|
118
|
-
```
|
|
119
|
-
|
|
195
|
+
```bash
|
|
196
|
+
vibe figma breakpoints # 현재 값 확인
|
|
197
|
+
vibe figma breakpoints --set breakpoint=768 # PC↔Mobile 경계 변경
|
|
120
198
|
```
|
|
121
199
|
|
|
122
|
-
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## R-4. 이미지 분류 + Multi-Layer 패턴
|
|
203
|
+
|
|
204
|
+
### 분류 기준
|
|
123
205
|
|
|
124
206
|
| 판별 조건 | 분류 | 코드 패턴 |
|
|
125
207
|
|----------|------|----------|
|
|
126
|
-
|
|
|
127
|
-
|
|
|
128
|
-
|
|
|
129
|
-
|
|
|
130
|
-
|
|
|
208
|
+
| 프레임 배경이고 위에 텍스트/UI 겹침 | **Background** | `background-image` + `background-size` |
|
|
209
|
+
| 독립 레이어, 겹치는 요소 없음 | **Content** | `<img>` 또는 `<picture>` |
|
|
210
|
+
| 이름에 `icon`/`logo`/`avatar` | **Inline Asset** | `<img>` (작은 크기) |
|
|
211
|
+
| `scaleMode: "TILE"` 반복 패턴 | **Pattern** | `background-repeat` |
|
|
212
|
+
| 전체 덮음 + opacity < 1 또는 blendMode | **Overlay** | `background-image` + overlay |
|
|
131
213
|
|
|
132
|
-
|
|
214
|
+
### Multi-Layer 패턴 (배경 이미지 섹션 필수 구조)
|
|
133
215
|
|
|
134
216
|
```
|
|
135
|
-
|
|
136
|
-
|
|
217
|
+
.{section}Section ← 레이아웃 (position: relative, overflow: hidden)
|
|
218
|
+
.{section}Bg ← 배경 이미지 — z-index: 0
|
|
219
|
+
.{section}BgOverlay ← 오버레이 (선택) — z-index: 1
|
|
220
|
+
.{section}Character ← 캐릭터/일러스트 (선택) — z-index: 2
|
|
221
|
+
.{section}Content ← 텍스트/버튼/UI — z-index: 최상위
|
|
222
|
+
```
|
|
137
223
|
|
|
138
|
-
|
|
139
|
-
|
|
224
|
+
```css
|
|
225
|
+
.{section}Section { position: relative; overflow: hidden; }
|
|
226
|
+
.{section}Bg {
|
|
227
|
+
position: absolute; inset: 0; z-index: 0;
|
|
228
|
+
background-image: url('/images/{feature}/{name}.webp');
|
|
229
|
+
background-size: cover; background-position: center;
|
|
230
|
+
}
|
|
231
|
+
.{section}BgOverlay {
|
|
232
|
+
position: absolute; inset: 0; z-index: 1;
|
|
233
|
+
background: linear-gradient(to bottom, rgba(0,0,0,0.3), rgba(0,0,0,0.7));
|
|
234
|
+
}
|
|
235
|
+
.{section}Content { position: relative; z-index: 2; }
|
|
140
236
|
```
|
|
141
237
|
|
|
142
|
-
|
|
238
|
+
### 배경 이미지 클래스 분리 원칙
|
|
143
239
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
240
|
+
| 유형 | 클래스 예시 | 분리 이유 |
|
|
241
|
+
|------|-----------|----------|
|
|
242
|
+
| 섹션 전면 배경 | `.heroBg` | 시즌/이벤트별 이미지 교체 |
|
|
243
|
+
| 오버레이 | `.heroBgOverlay` | 투명도/그라데이션 독립 조절 |
|
|
244
|
+
| 패턴/텍스처 | `.sectionPattern` | repeat/size 별도 제어 |
|
|
245
|
+
| 캐릭터/일러스트 | `.heroCharacter` | 콜라보별 교체, position 조절 |
|
|
246
|
+
| 그라데이션 | `.sectionGradient` | 테마별 색상 변경 |
|
|
147
247
|
|
|
148
|
-
###
|
|
248
|
+
### 반응형 배경 이미지
|
|
149
249
|
|
|
150
|
-
|
|
250
|
+
```css
|
|
251
|
+
.heroBg { background-image: url('/images/hero-mobile.webp'); }
|
|
252
|
+
@media (min-width: var(--breakpoint)) {
|
|
253
|
+
.heroBg { background-image: url('/images/hero-pc.webp'); }
|
|
254
|
+
}
|
|
255
|
+
```
|
|
151
256
|
|
|
152
|
-
|
|
257
|
+
### 이미지 공통 규칙
|
|
153
258
|
|
|
154
|
-
|
|
259
|
+
| 규칙 | 설명 |
|
|
260
|
+
|------|------|
|
|
261
|
+
| format | `.webp` 우선 (fallback: `.png`/`.jpg`) |
|
|
262
|
+
| alt | 레이어 이름에서 파생, 장식은 `alt=""` + `aria-hidden="true"` |
|
|
263
|
+
| width/height | 항상 명시 (CLS 방지), 스케일 팩터 적용 |
|
|
264
|
+
| loading | 뷰포트 상단 → `eager`, 나머지 → `lazy` |
|
|
265
|
+
| object-fit | `cover` (배경), `contain` (로고/아이콘), `fill` 금지 |
|
|
155
266
|
|
|
156
267
|
---
|
|
157
268
|
|
|
158
|
-
##
|
|
269
|
+
## R-5. 컴포넌트 통합 규칙 (80% Rule)
|
|
159
270
|
|
|
160
|
-
###
|
|
271
|
+
### 유사도 판단
|
|
161
272
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
- `tailwind.config.*` → Tailwind CSS
|
|
166
|
-
- `next.config.*` → Next.js
|
|
167
|
-
- `nuxt.config.*` → Nuxt
|
|
168
|
-
- `*.module.css` → CSS Modules pattern
|
|
169
|
-
- `*.scss` / `sass` in deps → SCSS
|
|
170
|
-
- `styled-components` / `@emotion` in deps → CSS-in-JS
|
|
273
|
+
```
|
|
274
|
+
구조 80%+ 동일 (색상/크기/텍스트만 다름)
|
|
275
|
+
→ 하나의 컴포넌트 + variant prop으로 통합
|
|
171
276
|
|
|
172
|
-
|
|
277
|
+
구조 자체가 다름 (요소 추가/제거, 레이아웃 방향 변경)
|
|
278
|
+
→ 별도 컴포넌트 유지
|
|
279
|
+
```
|
|
173
280
|
|
|
174
|
-
|
|
281
|
+
### 스택별 variant 구현
|
|
175
282
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
2. **`.claude/vibe/design-system/{project}/MASTER.md`** — authoritative token definitions
|
|
183
|
-
- If MASTER.md exists: **map Figma tokens to MASTER.md tokens first**, only create new tokens for values with no match
|
|
184
|
-
- If no MASTER.md: generate `figma-tokens.css` as standalone token source
|
|
283
|
+
| Stack | 방법 |
|
|
284
|
+
|-------|------|
|
|
285
|
+
| React / Next.js | `variant` prop + 조건부 className |
|
|
286
|
+
| Vue / Nuxt | `variant` prop + `<slot>` |
|
|
287
|
+
| Svelte | `variant` prop + `<slot>` |
|
|
288
|
+
| React Native | `variant` prop + StyleSheet 조건 |
|
|
185
289
|
|
|
186
|
-
|
|
290
|
+
### Fragment / 래퍼 제거
|
|
187
291
|
|
|
188
|
-
|
|
292
|
+
스타일이나 이벤트가 없는 래핑 요소 → 프레임워크 제공 투명 래퍼 사용:
|
|
189
293
|
|
|
190
|
-
|
|
294
|
+
| Stack | 투명 래퍼 |
|
|
295
|
+
|-------|----------|
|
|
296
|
+
| React / Next.js | `<>...</>` |
|
|
297
|
+
| Vue / Nuxt | `<template>` (컴포넌트 루트 이외) |
|
|
298
|
+
| Svelte | `{#if}`, `{#each}` 블록 |
|
|
191
299
|
|
|
192
|
-
|
|
300
|
+
---
|
|
193
301
|
|
|
194
|
-
|
|
195
|
-
|----------|--------|-----|
|
|
196
|
-
| 1 | **Storyboard** | Phase 0-1에서 추출한 `storyboardSpec.breakpoints` |
|
|
197
|
-
| 2 | **`~/.vibe/config.json`** | `figma.breakpoints` — user-customized via `vibe figma breakpoints --set` |
|
|
198
|
-
| 3 | **Project CSS/Tailwind** | Grep `tailwind.config.*` → `theme.screens`, or `@media.*min-width` patterns in codebase |
|
|
199
|
-
| 4 | **Defaults** | Built-in values (breakpoint: 1024px, etc.) |
|
|
302
|
+
## R-6. 검증 루프 공통 규칙
|
|
200
303
|
|
|
201
|
-
|
|
304
|
+
모든 Step에서 사용하는 스크린샷 기반 검증 프로세스.
|
|
202
305
|
|
|
203
|
-
|
|
306
|
+
### 6-1. 원본 스크린샷 확보
|
|
204
307
|
|
|
205
308
|
```
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
designPc: 2560px ← Figma PC artboard width (design is 2x scale)
|
|
211
|
-
designMobile: 720px ← Figma Mobile artboard width (design is 2x scale)
|
|
309
|
+
Step A에서 get_screenshot으로 확보한 섹션별 원본 이미지를 기준으로 사용.
|
|
310
|
+
없으면 해당 섹션의 nodeId로 get_screenshot 재호출.
|
|
311
|
+
|
|
312
|
+
원본 = Figma 디자인 스크린샷 (디자이너 의도)
|
|
212
313
|
```
|
|
213
314
|
|
|
214
|
-
|
|
315
|
+
### 6-2. 생성 코드 렌더링 확보
|
|
215
316
|
|
|
216
317
|
```
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
318
|
+
방법 (우선순위):
|
|
319
|
+
1. /vibe.utils --preview → 브라우저 렌더링 스크린샷 자동 생성
|
|
320
|
+
2. dev 서버 실행 중이면 → 해당 페이지 스크린샷 (Playwright/Puppeteer)
|
|
321
|
+
3. 둘 다 불가능하면 → 생성된 코드를 직접 읽고 시각적 결과를 추론
|
|
220
322
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
- Values scale linearly between these bounds
|
|
323
|
+
⚠️ 방법 3(코드 읽기 추론)은 최후 수단.
|
|
324
|
+
가능한 한 실제 렌더링 결과를 확보해야 정확한 비교 가능.
|
|
325
|
+
```
|
|
225
326
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
327
|
+
### 6-3. 섹션별 비교 (전체 페이지가 아닌 섹션 단위)
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
각 섹션 컴포넌트를 개별로 비교:
|
|
331
|
+
원본: Figma 섹션 스크린샷 (get_screenshot per section)
|
|
332
|
+
생성: 렌더링된 섹션 스크린샷 (또는 코드 추론)
|
|
333
|
+
|
|
334
|
+
섹션별로 비교해야 하는 이유:
|
|
335
|
+
- 전체 페이지 비교는 스크롤 길이 차이로 오차가 큼
|
|
336
|
+
- 섹션 단위가 수정 범위를 좁힐 수 있음
|
|
337
|
+
- P1 발견 시 해당 섹션만 수정 → 재비교
|
|
230
338
|
```
|
|
231
339
|
|
|
232
|
-
|
|
340
|
+
### 6-4. 비교 항목 + P1/P2 분류
|
|
233
341
|
|
|
234
|
-
|
|
342
|
+
| 비교 항목 | P1 (필수 수정) | P2 (권장 수정) |
|
|
343
|
+
|----------|---------------|---------------|
|
|
344
|
+
| **이미지 인벤토리** | 인벤토리에 있는 이미지가 코드에 없음 | — |
|
|
345
|
+
| **배경 이미지** | 누락, Multi-Layer 미적용, 경로 깨짐 | 크기/위치 미세 차이 |
|
|
346
|
+
| **이미지 다운로드** | 파일 미존재, 0byte, Figma URL 잔존 | — |
|
|
347
|
+
| **레이아웃** | 요소 배치 방향 다름, 섹션 순서 다름 | 미세 정렬 차이 |
|
|
348
|
+
| **색상** | 배경/텍스트색 완전히 다름 | 미세한 톤 차이 (ΔE < 10) |
|
|
349
|
+
| **타이포** | 제목/본문 크기 비율 다름, 굵기 다름 | ±2px 차이 |
|
|
350
|
+
| **간격** | 섹션 간 간격 크게 다름, 요소 겹침 | ±4px 차이 |
|
|
351
|
+
| **누락 요소** | 스크린샷에 보이는 요소가 코드에 없음 | — |
|
|
352
|
+
| **오버레이** | 배경 위 텍스트 가독성 확보 안 됨 | 투명도 미세 차이 |
|
|
235
353
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
354
|
+
### 6-5. Diff Report 출력
|
|
355
|
+
|
|
356
|
+
```markdown
|
|
357
|
+
## Visual Diff Report — {섹션명}
|
|
358
|
+
|
|
359
|
+
원본: Figma {섹션} 스크린샷
|
|
360
|
+
생성: 렌더링 결과 (또는 코드 추론)
|
|
361
|
+
|
|
362
|
+
### Match Score: {N}%
|
|
363
|
+
|
|
364
|
+
| # | 항목 | 원본 | 생성 코드 | 심각도 |
|
|
365
|
+
|---|------|------|----------|--------|
|
|
366
|
+
| 1 | 배경 이미지 | 눈 테마 + 그라데이션 | 누락 | P1 |
|
|
367
|
+
| 2 | 제목 font-size | ~36px (스케일 후) | 24px | P1 |
|
|
368
|
+
| 3 | 카드 border-radius | ~12px | 8px | P2 |
|
|
240
369
|
```
|
|
241
370
|
|
|
242
|
-
|
|
371
|
+
### 6-6. Auto-Fix Loop
|
|
243
372
|
|
|
244
|
-
|
|
373
|
+
```
|
|
374
|
+
P1이 있으면:
|
|
375
|
+
1. 해당 섹션의 컴포넌트/스타일 파일 수정
|
|
376
|
+
2. 수정 후 렌더링 재확보 (가능하면)
|
|
377
|
+
3. 원본 스크린샷과 재비교
|
|
378
|
+
4. P1=0 될 때까지 반복 — 횟수 제한 없음
|
|
379
|
+
|
|
380
|
+
같은 P1이 반복될 때:
|
|
381
|
+
- 이전과 동일한 수정을 반복하지 않는다
|
|
382
|
+
- 접근 방식을 바꿔서 시도한다 (다른 CSS 속성, 다른 레이아웃 전략)
|
|
383
|
+
- 접근을 바꿔도 해결 안 되면 → 사용자에게 해당 항목을 보여주고 방향 확인
|
|
384
|
+
- 사용자 확인 후에도 루프는 계속된다 — P1=0이 될 때까지 멈추지 않음
|
|
385
|
+
```
|
|
245
386
|
|
|
246
|
-
|
|
387
|
+
### 6-7. 완료 조건
|
|
247
388
|
|
|
248
389
|
```
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
390
|
+
✅ 모든 섹션 Match Score 95%+
|
|
391
|
+
✅ P1 = 0 (전 섹션)
|
|
392
|
+
✅ 모든 이미지 에셋 표시 + Figma 임시 URL 잔존 0건
|
|
393
|
+
✅ (반응형) 각 뷰포트 독립 검증 통과
|
|
394
|
+
✅ placeholder 0건 — "placeholder", "Key Visual Image", 빈 dashed box,
|
|
395
|
+
alt="placeholder", src="" 등이 코드에 남아있으면 미완성
|
|
396
|
+
✅ 단색/gradient 대체 0건 — 원본에 이미지 배경인 곳이 CSS 단색으로 처리되면 미완성
|
|
397
|
+
✅ 텍스트 스타일 전수 적용 — 브라우저 기본 스타일(검은색 16px)로 보이는 텍스트 0건
|
|
398
|
+
✅ 스타일 외부 파일 — 컴포넌트 내 <style> 블록 0건, 인라인 style="" 0건
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## R-7. 섹션별 분석 프로세스
|
|
254
404
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
405
|
+
### Step 1: get_design_context (스타일 값 + 에셋)
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
get_design_context(fileKey, 섹션 nodeId)
|
|
409
|
+
→ 참조 코드 반환 (React+Tailwind 형태)
|
|
410
|
+
|
|
411
|
+
참조 코드에서 추출하는 것 (정확한 값 — Figma 토큰에서 직접 추출됨):
|
|
412
|
+
✅ 색상: hex 값 (text-[#1B3A1D], bg-[#0A1628] 등)
|
|
413
|
+
✅ 폰트: font-size(px), font-weight, line-height, font-family
|
|
414
|
+
✅ 간격: padding, margin, gap (px)
|
|
415
|
+
✅ 장식: border-radius, box-shadow, opacity
|
|
416
|
+
✅ 에셋 URL: const xxxImage = 'https://figma.com/api/mcp/asset/...'
|
|
417
|
+
|
|
418
|
+
이 값들은 디자이너가 Figma UI 우측 패널에서 보는 것과 동일.
|
|
419
|
+
스크린샷에서 추정할 필요 없이 이 값을 그대로 사용한다.
|
|
420
|
+
단, px 값에는 스케일 팩터를 적용한다 (R-3).
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Step 2: get_screenshot (구조 + 이미지 인벤토리)
|
|
424
|
+
|
|
425
|
+
```
|
|
426
|
+
get_screenshot(fileKey, 섹션 nodeId)
|
|
427
|
+
→ 원본 디자인 이미지 확보
|
|
428
|
+
|
|
429
|
+
스크린샷에서 판단하는 것 (구조 — 참조 코드가 틀릴 수 있는 영역):
|
|
430
|
+
✅ 레이아웃 구조: 섹션 경계, flex/grid 방향, 요소 배치 순서
|
|
431
|
+
✅ 이미지 배치: 배경/콘텐츠/오버레이 분류, z-index 관계
|
|
432
|
+
✅ 이미지 인벤토리: 보이는 모든 이미지 목록 (참조 코드에 없는 것도 잡음)
|
|
433
|
+
✅ 겹침 구조: 텍스트-배경 관계, 오버레이 유무
|
|
434
|
+
✅ 누락 검출: 참조 코드에 없는 시각 요소 발견
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Step 3: 코드 생성
|
|
438
|
+
|
|
439
|
+
```
|
|
440
|
+
a. 참조 코드의 스타일 값을 그대로 사용하여 외부 스타일 파일 작성:
|
|
441
|
+
- 색상 hex → 토큰 변수로 정의
|
|
442
|
+
- font-size × scaleFactor → 토큰 변수로 정의
|
|
443
|
+
- 간격 × scaleFactor → 토큰 변수로 정의
|
|
444
|
+
|
|
445
|
+
b. 스크린샷의 구조로 HTML/template 결정:
|
|
446
|
+
- 레이아웃 방향, 요소 순서
|
|
447
|
+
- Multi-Layer 배경 구조 (배경 이미지가 있으면)
|
|
448
|
+
- 참조 코드의 구조가 스크린샷과 다르면 → 스크린샷 기준
|
|
449
|
+
|
|
450
|
+
c. 에셋 URL → 다운로드 → 로컬 경로로 교체
|
|
451
|
+
|
|
452
|
+
d. 스크린샷에 보이지만 참조 코드에 없는 요소 → 추가 구현
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### 참조 코드의 구조가 틀릴 때
|
|
456
|
+
|
|
457
|
+
```
|
|
458
|
+
레이어가 "Frame 633372"면 참조 코드의 HTML 구조가 부정확할 수 있다.
|
|
459
|
+
이때:
|
|
460
|
+
- 스타일 값(색상, 폰트, 간격)은 여전히 정확 → 그대로 사용
|
|
461
|
+
- HTML 구조(어떤 요소가 어디에)만 스크린샷 기준으로 재배치
|
|
462
|
+
- 에셋 URL도 여전히 유효 → 그대로 다운로드
|
|
259
463
|
```
|