@su-record/vibe 2.8.25 → 2.8.27
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/package.json +1 -1
- package/skills/vibe-figma/SKILL.md +136 -191
- package/skills/vibe-figma-convert/SKILL.md +13 -175
package/package.json
CHANGED
|
@@ -13,22 +13,11 @@ tier: standard
|
|
|
13
13
|
❌ CSS로 이미지 재현 (삼각형/원/gradient로 나무/눈사람/배경 그리기)
|
|
14
14
|
❌ 이미지 다운로드 없이 코드 생성 진행
|
|
15
15
|
❌ placeholder / 빈 template / 빈 src="" 남기기
|
|
16
|
-
❌ CSS 값을 추정 (참조
|
|
16
|
+
❌ CSS 값을 추정 (참조 코드의 Tailwind 클래스에 정확한 값이 있음)
|
|
17
17
|
❌ 브라우저 기본 스타일(검은색 16px)로 보이는 텍스트
|
|
18
|
-
❌ 핵심 에셋만 다운로드 (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
### 스타일 배치 규칙 (모드별)
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
일반 모드:
|
|
25
|
-
❌ 컴포넌트 파일 안에 <style> 블록 / 인라인 style=""
|
|
26
|
-
✅ 외부 SCSS 파일에만 스타일 작성
|
|
27
|
-
|
|
28
|
-
직역 모드:
|
|
29
|
-
✅ <style scoped> 블록 허용 (Tailwind→CSS 1:1 변환)
|
|
30
|
-
✅ 인라인 :style="" 허용 (maskImage 등 동적 값)
|
|
31
|
-
❌ 외부 SCSS 파일에 추상화된 스타일 작성 (원본 좌표 손실)
|
|
18
|
+
❌ 핵심 에셋만 다운로드 (const img... 전부 다운로드)
|
|
19
|
+
❌ 컴포넌트 파일 안에 <style> 블록 / 인라인 style=""
|
|
20
|
+
✅ 외부 SCSS 파일에만 스타일 작성
|
|
32
21
|
```
|
|
33
22
|
|
|
34
23
|
## 전체 플로우
|
|
@@ -62,7 +51,7 @@ tier: standard
|
|
|
62
51
|
4. 디렉토리 생성:
|
|
63
52
|
- components/{feature}/
|
|
64
53
|
- public/images/{feature}/ (또는 static/images/{feature}/)
|
|
65
|
-
- styles/{feature}/ (layout/, components/ 하위)
|
|
54
|
+
- styles/{feature}/ (layout/, components/ 하위)
|
|
66
55
|
```
|
|
67
56
|
|
|
68
57
|
---
|
|
@@ -189,225 +178,193 @@ Phase 1 완료 조건:
|
|
|
189
178
|
|
|
190
179
|
---
|
|
191
180
|
|
|
192
|
-
## Phase 2: Design
|
|
193
|
-
|
|
194
|
-
Phase 1에서 컴포넌트가 이미 존재 (레이아웃 + 기능 주석 + 목 데이터).
|
|
195
|
-
Phase 2에서는 이 컴포넌트에 **디자인(이미지 + 스타일)**을 입힌다.
|
|
181
|
+
## Phase 2: Design
|
|
196
182
|
|
|
197
|
-
|
|
183
|
+
Phase 1 컴포넌트에 **이미지 + 스타일**을 입힌다.
|
|
184
|
+
모바일 퍼스트. base = 최소 뷰포트, @media (min-width:)로 확장.
|
|
198
185
|
|
|
199
186
|
사용자에게 질문한다:
|
|
200
187
|
- question: "베이스 디자인(모바일) Figma URL을 입력해주세요."
|
|
201
188
|
- options 제공 금지 — 자유 텍스트 입력만 허용
|
|
202
189
|
|
|
203
|
-
→
|
|
190
|
+
→ 2-1 → 2-2 섹션 루프 실행.
|
|
204
191
|
|
|
205
|
-
|
|
192
|
+
완료 후 다시 질문:
|
|
206
193
|
- question: "다음 브레이크포인트 디자인 URL을 입력해주세요. (없으면 '없음')"
|
|
207
|
-
-
|
|
208
|
-
|
|
209
|
-
→ URL 입력 시: @media (min-width:) 레이어 추가 후 다시 질문
|
|
194
|
+
→ URL 입력 시: 2-3 반응형 추가 후 다시 질문
|
|
210
195
|
→ "없음" 응답 시: Phase 3으로
|
|
211
196
|
|
|
212
|
-
|
|
213
|
-
예: 720px → 480px(스케일), 2560px → 1920px(스케일) → @media (min-width: 1024px)
|
|
214
|
-
|
|
215
|
-
### 2-1. 스타일 파일 내용 작성 (첫 섹션 전)
|
|
197
|
+
### 2-1. SCSS Setup + 등록 (첫 섹션 전)
|
|
216
198
|
|
|
217
199
|
```
|
|
218
|
-
|
|
219
|
-
styles/{feature}/index.scss ← @
|
|
220
|
-
styles/{feature}/_tokens.scss ← 빈 파일 (
|
|
200
|
+
SCSS 파일 기본 내용 Write:
|
|
201
|
+
styles/{feature}/index.scss ← @import 진입점
|
|
202
|
+
styles/{feature}/_tokens.scss ← 빈 파일 (섹션마다 채움)
|
|
221
203
|
styles/{feature}/_mixins.scss ← breakpoint mixin
|
|
222
|
-
styles/{feature}/_base.scss ←
|
|
204
|
+
styles/{feature}/_base.scss ← 루트 클래스 (.winterPcbang 등)
|
|
223
205
|
styles/{feature}/layout/ ← 디렉토리
|
|
224
206
|
styles/{feature}/components/ ← 디렉토리
|
|
225
|
-
```
|
|
226
207
|
|
|
227
|
-
|
|
208
|
+
스타일 등록 (BLOCKING — 미등록 시 섹션 루프 진행 금지):
|
|
209
|
+
Grep "{feature}/index.scss" → 이미 등록되어 있으면 건너뜀.
|
|
228
210
|
|
|
211
|
+
■ 신규 프로젝트 (--new):
|
|
212
|
+
루트 페이지 파일에서 직접 로드:
|
|
213
|
+
pages/{feature}.vue → <style lang="scss" src="~/assets/scss/{feature}/index.scss" />
|
|
214
|
+
app/{feature}/page.tsx → import '~/styles/{feature}/index.scss'
|
|
215
|
+
|
|
216
|
+
■ 기존 프로젝트 (업데이트):
|
|
217
|
+
Grep "\.scss\|\.css" in nuxt.config.*/next.config.*/vite.config.*/main.ts
|
|
218
|
+
→ 기존 방식과 동일하게 등록
|
|
219
|
+
|
|
220
|
+
검증: Grep "{feature}/index.scss" in 프로젝트 전체 → 0건이면 실패
|
|
229
221
|
```
|
|
230
|
-
각 섹션의 get_design_context 응답을 받을 때마다 개별 판정.
|
|
231
|
-
한 페이지 내에서 섹션마다 모드가 다를 수 있음.
|
|
232
222
|
|
|
233
|
-
|
|
234
|
-
□ 에셋 URL 15개 이상
|
|
235
|
-
□ 소수점 좌표 사용 (left-[117.13px], top-[373.65px])
|
|
236
|
-
□ mix-blend-mode 사용 (mix-blend-lighten, mix-blend-multiply, mix-blend-hue)
|
|
237
|
-
□ rotate/scale 변환 사용 (rotate-[149.7deg], -scale-y-100)
|
|
238
|
-
□ mask-image 사용
|
|
239
|
-
□ blur 필터 사용 (blur-[3.5px])
|
|
240
|
-
□ 2560px 이상 원본 해상도에서 트리밍된 BG 구조
|
|
223
|
+
### 2-2. 섹션 루프
|
|
241
224
|
|
|
242
|
-
|
|
243
|
-
□ flex/grid 기반 정형 레이아웃
|
|
244
|
-
□ 에셋 URL 10개 미만
|
|
245
|
-
□ absolute 좌표 없거나 정수값만
|
|
246
|
-
□ mix-blend/rotate/mask/blur 미사용
|
|
225
|
+
**각 섹션을 순서대로, 한 섹션을 완전히 완료한 후 다음으로.**
|
|
247
226
|
|
|
248
|
-
|
|
249
|
-
┌──────────┬──────────┐
|
|
250
|
-
│ 섹션 │ 모드 │
|
|
251
|
-
├──────────┼──────────┤
|
|
252
|
-
│ Hero │ 직역 │
|
|
253
|
-
│ KID │ 직역 │
|
|
254
|
-
│ Daily │ 직역 │
|
|
255
|
-
│ Caution │ 일반 │
|
|
256
|
-
│ ... │ ... │
|
|
257
|
-
└──────────┴──────────┘
|
|
227
|
+
#### a. 참조 코드 획득
|
|
258
228
|
|
|
259
|
-
혼합 섹션 (배경=비정형, 콘텐츠=정형):
|
|
260
|
-
→ 직역 모드 적용 (비정형이 하나라도 있으면 직역)
|
|
261
|
-
→ 콘텐츠 영역의 반복 패턴(v-for 등)은 직역 내에서 유지
|
|
262
229
|
```
|
|
230
|
+
get_design_context(fileKey, 섹션.nodeId)
|
|
263
231
|
|
|
264
|
-
|
|
232
|
+
반환:
|
|
233
|
+
- const img변수명 = 'URL' (이미지 에셋)
|
|
234
|
+
- React+Tailwind JSX (HTML 구조 + CSS 값)
|
|
235
|
+
- 스크린샷 (시각 기준점)
|
|
236
|
+
- data-name 속성 (레이어 이름: "BG", "Title" 등)
|
|
265
237
|
|
|
238
|
+
큰 섹션 (높이 1500px+):
|
|
239
|
+
get_metadata로 하위 노드 목록 → 하위 단위로 get_design_context 분할 호출
|
|
240
|
+
타임아웃 시: 1회 재시도 (excludeScreenshot: true) → 실패 시 분할
|
|
266
241
|
```
|
|
267
|
-
get_design_context 타임아웃 방지:
|
|
268
242
|
|
|
269
|
-
|
|
270
|
-
사전 분할: get_design_context 호출 전에 먼저 분할
|
|
271
|
-
1. get_metadata(섹션 nodeId)로 하위 노드 목록 확보
|
|
272
|
-
2. 하위 노드별로 get_design_context 호출 (분할)
|
|
273
|
-
3. 결과를 합쳐서 하나의 섹션으로 처리
|
|
243
|
+
#### b. 이미지 다운로드 (BLOCKING)
|
|
274
244
|
|
|
275
|
-
타임아웃 발생 시 (분할 없이 호출한 경우):
|
|
276
|
-
1회 재시도 (excludeScreenshot: true)
|
|
277
|
-
→ 실패 시 즉시 분할 전략으로 전환 (3회 반복 금지)
|
|
278
|
-
→ 분할도 불가하면 get_screenshot + 스크린샷 기반 구현
|
|
279
|
-
(이 경우 CSS 값은 스크린샷에서 추정 — 품질 하락 감수)
|
|
280
|
-
|
|
281
|
-
실전 데이터 (PUBG 겨울 PC방 기준):
|
|
282
|
-
정상 응답: ~900px 이하 (KID 238px, Caution 880px, Anchor 652px)
|
|
283
|
-
타임아웃: 2000px+ (Daily 2372~3604, PlayTime 2000~2613, Exchange 2832~4342)
|
|
284
|
-
파일 저장: Hero 1280px (92K~130K chars — 에셋 40개로 크기 큼)
|
|
285
245
|
```
|
|
246
|
+
참조 코드의 모든 const img... URL을 추출 → 다운로드 → 검증.
|
|
286
247
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
248
|
+
변수명 → 파일명: imgSnowParticle12 → snow-particle-12.webp
|
|
249
|
+
다운로드: curl -sL "{url}" -o images/{feature}/{파일명}
|
|
250
|
+
검증: ls -la → 모든 파일 존재 + 0byte 아닌지
|
|
290
251
|
|
|
291
|
-
|
|
252
|
+
이미지 매핑 생성:
|
|
253
|
+
imageMap = { imgTitle: '/images/{feature}/title.webp', ... }
|
|
292
254
|
|
|
255
|
+
전부 완료해야 c 단계로 진행. 하나라도 실패 → 코드 생성 금지.
|
|
293
256
|
```
|
|
294
|
-
get_design_context(fileKey, 섹션.nodeId)
|
|
295
257
|
|
|
296
|
-
|
|
297
|
-
- const img변수명 = 'https://figma.com/api/mcp/asset/...' (이미지 에셋 URL들)
|
|
298
|
-
- React+Tailwind JSX 코드 (HTML 구조 + CSS 값)
|
|
299
|
-
- 스크린샷 (원본 디자인 이미지)
|
|
300
|
-
- data-name 속성 (레이어 이름: "BG", "Title", "Period" 등)
|
|
258
|
+
#### c. 클래스 매핑 테이블 생성
|
|
301
259
|
|
|
302
|
-
이 참조 코드가 모든 작업의 기반.
|
|
303
260
|
```
|
|
261
|
+
SCSS 작성 전에 반드시 매핑 테이블을 먼저 출력한다.
|
|
262
|
+
이 테이블 없이 SCSS를 작성하지 않는다.
|
|
304
263
|
|
|
305
|
-
|
|
264
|
+
1. Phase 1 컴포넌트의 클래스 목록을 Read로 수집
|
|
265
|
+
2. 참조 코드의 data-name + HTML 구조를 분석
|
|
266
|
+
3. 매핑 테이블 출력:
|
|
306
267
|
|
|
307
|
-
|
|
308
|
-
|
|
268
|
+
┌─────────────────────┬──────────────────┬────────────────────────────┐
|
|
269
|
+
│ Phase 1 클래스 │ 참조 data-name │ 핵심 Tailwind 값 │
|
|
270
|
+
├─────────────────────┼──────────────────┼────────────────────────────┤
|
|
271
|
+
│ .kidSection │ (root) │ flex flex-col gap-[32px] │
|
|
272
|
+
│ .kidBg │ BG │ absolute mix-blend-multiply│
|
|
273
|
+
│ .kidLoginBtn │ Btn_Login │ border shadow h-[120px] │
|
|
274
|
+
│ .kidLoginBtnText │ (텍스트 노드) │ text-[36px] text-white │
|
|
275
|
+
│ .kidDivider │ Divider │ h-px w-full │
|
|
276
|
+
│ .kidSteamLink │ steam_account │ text-[24px] font-semibold │
|
|
277
|
+
│ .kidSteamNote │ (하위 텍스트) │ text-[20px] #dadce3 │
|
|
278
|
+
└─────────────────────┴──────────────────┴────────────────────────────┘
|
|
309
279
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
280
|
+
매핑 기준:
|
|
281
|
+
data-name 일치 → 직접 매핑
|
|
282
|
+
data-name 없음 → HTML 위치/텍스트 내용으로 판단
|
|
283
|
+
Phase 1에 없는 요소 → 클래스 신규 추가 (template에도 반영)
|
|
284
|
+
참조 코드에 없는 클래스 → 스타일 없이 유지
|
|
314
285
|
```
|
|
315
286
|
|
|
316
|
-
####
|
|
287
|
+
#### d. SCSS 작성
|
|
317
288
|
|
|
318
289
|
```
|
|
319
|
-
|
|
320
|
-
|
|
290
|
+
매핑 테이블의 각 행을 순서대로 CSS로 변환하여 SCSS에 작성.
|
|
291
|
+
vibe-figma-extract의 Tailwind→CSS 변환표 참조.
|
|
292
|
+
|
|
293
|
+
CSS 변수 패턴 처리:
|
|
294
|
+
font-[family-name:var(--font/family/pretendard,...)] → fallback 값 사용
|
|
295
|
+
text-[length:var(--font/size/heading/24,24px)] → 24px
|
|
296
|
+
text-[color:var(--color/grayscale/300,#dadce3)] → #dadce3
|
|
321
297
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
- src={변수} → 로컬 이미지 경로
|
|
326
|
-
- style={{ maskImage: ... }} → :style="{ maskImage: ... }"
|
|
327
|
-
- 소수점 좌표, rotate, mix-blend-mode 전부 보존
|
|
328
|
-
- scaleFactor 적용: px 값만 스케일링, 나머지(색상, opacity, blend) 유지
|
|
329
|
-
|
|
330
|
-
외부 SCSS 파일 생성하지 않음.
|
|
331
|
-
컴포넌트에 <style scoped> 블록으로 스타일 포함.
|
|
298
|
+
scaleFactor 적용:
|
|
299
|
+
px 값 → × scaleFactor (font-size, padding, margin, gap, width, height, border-radius)
|
|
300
|
+
적용 안 함 → color, opacity, font-weight, z-index, line-height(단위 없음), % 값
|
|
332
301
|
|
|
333
|
-
|
|
334
|
-
|
|
302
|
+
출력 파일:
|
|
303
|
+
styles/{feature}/layout/_{section}.scss
|
|
304
|
+
→ position, display, flex, width, height, padding, overflow, z-index, background-image
|
|
305
|
+
styles/{feature}/components/_{section}.scss
|
|
306
|
+
→ font-size, font-weight, color, line-height, letter-spacing, text-align,
|
|
307
|
+
border, border-radius, box-shadow, opacity
|
|
308
|
+
styles/{feature}/_tokens.scss
|
|
309
|
+
→ 새로 발견된 색상/폰트/스페이싱 토큰 추가
|
|
335
310
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
bg-[#0A1628] → background-color: #0A1628
|
|
340
|
-
pt-[120px] → padding-top: 90px (120 × 0.75)
|
|
311
|
+
BG 레이어 패턴 (참조 코드에서 absolute + inset-0 + object-cover):
|
|
312
|
+
.{section}Bg → position: absolute; inset: 0; z-index: 0;
|
|
313
|
+
.{section}Content → position: relative; z-index: 1;
|
|
341
314
|
|
|
342
|
-
|
|
343
|
-
styles/{feature}/layout/_{section}.scss ← 배치/구조/배경이미지
|
|
344
|
-
styles/{feature}/components/_{section}.scss ← 텍스트/버튼/카드 스타일
|
|
345
|
-
styles/{feature}/_tokens.scss ← 새 토큰 추가
|
|
315
|
+
index.scss에 새 섹션 @import 추가.
|
|
346
316
|
```
|
|
347
317
|
|
|
348
|
-
####
|
|
318
|
+
#### e. template 업데이트
|
|
349
319
|
|
|
350
320
|
```
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
script(JSDoc, 인터페이스, 목 데이터, 핸들러)는 보존.
|
|
354
|
-
|
|
355
|
-
변환 핵심:
|
|
356
|
-
- 참조 코드의 div/img 구조를 거의 그대로 유지
|
|
357
|
-
- 모든 이미지 경로를 로컬 경로로 교체
|
|
358
|
-
- Phase 1의 기능 요소(v-for, @click, v-if)를 적절한 위치에 재배치
|
|
359
|
-
- <style scoped>에 Tailwind→CSS 변환 결과 포함
|
|
360
|
-
- 장식 이미지에 alt="" aria-hidden="true"
|
|
321
|
+
Phase 1 컴포넌트의 template을 참조 코드 기반으로 리팩토링.
|
|
322
|
+
script(JSDoc, 인터페이스, 목 데이터, 핸들러)는 보존.
|
|
361
323
|
|
|
362
|
-
|
|
363
|
-
|
|
324
|
+
1. 참조 코드의 HTML 구조를 프로젝트 스택으로 변환:
|
|
325
|
+
className → class, onClick → @click, {조건 && <X/>} → v-if 등
|
|
364
326
|
|
|
365
|
-
|
|
366
|
-
|
|
327
|
+
2. 이미지 경로를 imageMap으로 교체:
|
|
328
|
+
src={imgTitle} → src="/images/{feature}/title.webp"
|
|
367
329
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
- 이미지 경로를 다운로드된 로컬 경로로 설정
|
|
371
|
-
- 배경 이미지 섹션은 Multi-Layer 구조 (.{section}Bg + .{section}Content)
|
|
372
|
-
- 클래스명을 외부 스타일 파일의 셀렉터와 매칭
|
|
373
|
-
- Phase 1의 기능 요소(v-for, @click, v-if)를 새 구조에 재배치
|
|
374
|
-
|
|
375
|
-
컴포넌트에 <style> 블록 없음. 스타일은 전부 외부 파일.
|
|
376
|
-
```
|
|
330
|
+
3. BG 레이어 구조 적용:
|
|
331
|
+
.{section}Bg div (배경) + .{section}Content div (콘텐츠)
|
|
377
332
|
|
|
378
|
-
|
|
333
|
+
4. Phase 1 기능 요소 재배치:
|
|
334
|
+
v-for, @click, v-if, $emit 등을 새 구조의 적절한 위치에 배치
|
|
379
335
|
|
|
336
|
+
5. 접근성:
|
|
337
|
+
장식 이미지 → alt="" aria-hidden="true"
|
|
338
|
+
콘텐츠 이미지 → alt="설명적 텍스트"
|
|
339
|
+
|
|
340
|
+
컴포넌트에 <style> 블록 없음. 스타일은 전부 외부 SCSS.
|
|
380
341
|
```
|
|
381
|
-
공통 체크:
|
|
382
|
-
□ Grep: "figma.com/api" in 생성 파일 → 0건
|
|
383
|
-
□ Grep: "placeholder" in 컴포넌트 파일 → 0건
|
|
384
|
-
□ Grep: 'src=""' in 컴포넌트 파일 → 0건
|
|
385
|
-
□ Glob: images/{feature}/*.webp → 이미지 파일 존재
|
|
386
|
-
□ Read: 컴포넌트 template에 실제 HTML 태그 존재 (빈 template 아님)
|
|
387
342
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
343
|
+
#### f. 섹션 검증
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
Grep 체크:
|
|
347
|
+
□ "figma.com/api" in 생성 파일 → 0건
|
|
348
|
+
□ 'src=""' in 컴포넌트 파일 → 0건
|
|
349
|
+
□ "<style" in 컴포넌트 파일 → 0건
|
|
392
350
|
|
|
393
|
-
|
|
394
|
-
□
|
|
395
|
-
□
|
|
351
|
+
Read 체크:
|
|
352
|
+
□ 외부 SCSS 파일에 font-size, color 존재 (브라우저 기본 스타일 방지)
|
|
353
|
+
□ 이미지 파일 수 = const img... 수 (누락 0)
|
|
396
354
|
|
|
397
|
-
실패
|
|
355
|
+
실패 → 수정 → 재검증
|
|
398
356
|
```
|
|
399
357
|
|
|
400
|
-
### 2-3. 두 번째 URL
|
|
358
|
+
### 2-3. 반응형 (두 번째 URL부터)
|
|
401
359
|
|
|
402
360
|
```
|
|
403
|
-
두 번째 이후 URL
|
|
404
|
-
기존 스타일은 유지하고 반응형만 추가.
|
|
361
|
+
두 번째 이후 URL: 기존 스타일 유지 + @media 추가만.
|
|
405
362
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
363
|
+
같은 값 → 유지
|
|
364
|
+
다른 px 값 → @media (min-width: $bp-desktop) 오버라이드
|
|
365
|
+
다른 레이아웃 → @media 블록 추가
|
|
366
|
+
다른 배경 이미지 → @media 이미지 분기
|
|
367
|
+
기존 코드/스타일 삭제 금지
|
|
411
368
|
```
|
|
412
369
|
|
|
413
370
|
---
|
|
@@ -415,27 +372,15 @@ vibe-figma-extract 스킬 참조.
|
|
|
415
372
|
## Phase 3: Verification
|
|
416
373
|
|
|
417
374
|
```
|
|
418
|
-
|
|
419
|
-
□
|
|
420
|
-
□
|
|
421
|
-
□
|
|
422
|
-
□
|
|
423
|
-
□ Grep: 'src=""' in components/{feature}/ → 0건
|
|
424
|
-
□ Glob: images/{feature}/*.webp → 이미지 파일 존재
|
|
375
|
+
Grep 체크:
|
|
376
|
+
□ "figma.com/api" in 모든 생성 파일 → 0건
|
|
377
|
+
□ "<style" in components/{feature}/ → 0건
|
|
378
|
+
□ 'src=""' in components/{feature}/ → 0건
|
|
379
|
+
□ Glob: images/{feature}/ → 이미지 파일 존재
|
|
425
380
|
|
|
426
381
|
시각 검증:
|
|
427
|
-
각
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
P1 (필수 수정): 이미지 누락, 레이아웃 구조 다름, 텍스트 스타일 미적용
|
|
433
|
-
P2 (권장 수정): 미세 간격 차이, 미세 색상 차이
|
|
434
|
-
|
|
435
|
-
P1 → 수정 → 재검증 (P1=0 될 때까지, 제한 없음)
|
|
436
|
-
|
|
437
|
-
완료 후:
|
|
438
|
-
Design Quality Pipeline 안내:
|
|
439
|
-
/design-normalize → /design-audit (quick)
|
|
440
|
-
+ /design-critique → /design-polish (thorough)
|
|
382
|
+
각 섹션: get_screenshot(nodeId) vs dev 서버/preview 비교
|
|
383
|
+
P1 (필수): 이미지 누락, 레이아웃 구조 다름, 텍스트 스타일 미적용
|
|
384
|
+
P2 (권장): 미세 간격, 미세 색상 차이
|
|
385
|
+
→ P1 수정 → 재검증 (P1=0 될 때까지)
|
|
441
386
|
```
|
|
@@ -8,181 +8,7 @@ tier: standard
|
|
|
8
8
|
# vibe-figma-convert — 코드 변환
|
|
9
9
|
|
|
10
10
|
`get_design_context` 참조 코드를 프로젝트 스택 코드로 변환.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 0. 직역 모드 (비정형 레이어)
|
|
16
|
-
|
|
17
|
-
비정형 레이어 감지 시 사용. 참조 코드의 구조와 좌표를 **거의 그대로** 보존.
|
|
18
|
-
|
|
19
|
-
### 핵심 원칙
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
1. 참조 코드의 HTML 중첩 구조를 1:1로 유지
|
|
23
|
-
2. absolute 좌표, rotate, mix-blend-mode, blur 등 시각 속성 전부 보존
|
|
24
|
-
3. scaleFactor는 px 값에만 적용 (색상, opacity, blend-mode, z-index 미적용)
|
|
25
|
-
4. 에셋 URL → 로컬 경로로만 교체
|
|
26
|
-
5. 의미론적 재구성(BG+Content 분리) 시도하지 않음
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### Tailwind → CSS 클래스 직역
|
|
30
|
-
|
|
31
|
-
```
|
|
32
|
-
참조 코드의 각 요소에 고유 클래스명을 부여하고,
|
|
33
|
-
Tailwind 클래스를 CSS로 1:1 변환하여 <style scoped>에 작성.
|
|
34
|
-
|
|
35
|
-
클래스명 규칙: data-name 또는 data-node-id 기반
|
|
36
|
-
data-name="BG" → .bg
|
|
37
|
-
data-name="Period" → .period
|
|
38
|
-
data-name="Title" → .title
|
|
39
|
-
data-name="Light" → .light
|
|
40
|
-
이름 없으면 → .node-{nodeId} (콜론→하이픈)
|
|
41
|
-
|
|
42
|
-
변환 예시:
|
|
43
|
-
className="absolute h-[1280px] left-0 overflow-clip top-0 w-[720px]"
|
|
44
|
-
→
|
|
45
|
-
.bg {
|
|
46
|
-
position: absolute;
|
|
47
|
-
height: 853px; /* 1280 × 0.667 */
|
|
48
|
-
left: 0;
|
|
49
|
-
overflow: clip;
|
|
50
|
-
top: 0;
|
|
51
|
-
width: 100%; /* 720px = 뷰포트 전체폭 → 100% */
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
className="-translate-x-1/2 absolute h-[174px] left-1/2 top-1/2 w-[620px]"
|
|
55
|
-
→
|
|
56
|
-
.title-img {
|
|
57
|
-
position: absolute;
|
|
58
|
-
height: 116px; /* 174 × 0.667 */
|
|
59
|
-
left: 50%;
|
|
60
|
-
top: 50%;
|
|
61
|
-
transform: translateX(-50%);
|
|
62
|
-
width: 413px; /* 620 × 0.667 */
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### 특수 패턴 직역
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
mix-blend:
|
|
70
|
-
className="mix-blend-lighten"
|
|
71
|
-
→ mix-blend-mode: lighten;
|
|
72
|
-
|
|
73
|
-
rotate + scale:
|
|
74
|
-
className="-scale-y-100 rotate-[149.7deg]"
|
|
75
|
-
→ transform: scaleY(-1) rotate(149.7deg);
|
|
76
|
-
|
|
77
|
-
mask-image:
|
|
78
|
-
style={{ maskImage: `url('${imgVar}')` }}
|
|
79
|
-
→ :style="{ maskImage: `url('/images/{feature}/파일.webp')` }"
|
|
80
|
-
(Vue 동적 바인딩으로 변환)
|
|
81
|
-
|
|
82
|
-
blur:
|
|
83
|
-
className="blur-[3.5px]"
|
|
84
|
-
→ filter: blur(3.5px);
|
|
85
|
-
|
|
86
|
-
소수점 좌표:
|
|
87
|
-
className="absolute h-[141.67px] left-[380.52px] top-[528.95px]"
|
|
88
|
-
→ position: absolute;
|
|
89
|
-
height: 94px; /* 141.67 × 0.667 반올림 */
|
|
90
|
-
left: 254px; /* 380.52 × 0.667 반올림 */
|
|
91
|
-
top: 353px; /* 528.95 × 0.667 반올림 */
|
|
92
|
-
|
|
93
|
-
inset-[-18.13%]:
|
|
94
|
-
→ inset: -18.13%; (% 값은 스케일링 안 함)
|
|
95
|
-
|
|
96
|
-
overflow-hidden + 스프라이트:
|
|
97
|
-
className="absolute h-full left-[-129.09%] max-w-none top-0 w-[229.09%]"
|
|
98
|
-
→ position: absolute;
|
|
99
|
-
height: 100%;
|
|
100
|
-
left: -129.09%; /* % 값 그대로 */
|
|
101
|
-
max-width: none;
|
|
102
|
-
top: 0;
|
|
103
|
-
width: 229.09%; /* % 값 그대로 */
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Vue SFC 출력 형태
|
|
107
|
-
|
|
108
|
-
```vue
|
|
109
|
-
<template>
|
|
110
|
-
<section class="heroSection">
|
|
111
|
-
<!-- 참조 코드의 HTML 구조를 1:1 유지 -->
|
|
112
|
-
<div class="bg">
|
|
113
|
-
<div class="bgInner">
|
|
114
|
-
<div class="bgImage1">
|
|
115
|
-
<img src="/images/{feature}/bg.webp" alt="" aria-hidden="true" />
|
|
116
|
-
</div>
|
|
117
|
-
<div class="tree4">
|
|
118
|
-
<img src="/images/{feature}/tree-4.webp" alt="" aria-hidden="true" />
|
|
119
|
-
</div>
|
|
120
|
-
<!-- ... 모든 서브 레이어 유지 -->
|
|
121
|
-
</div>
|
|
122
|
-
<!-- ... -->
|
|
123
|
-
</div>
|
|
124
|
-
|
|
125
|
-
<!-- 콘텐츠 영역 (참조 코드의 Title, Period 등) -->
|
|
126
|
-
<div class="titleArea">
|
|
127
|
-
<div class="titleImg">
|
|
128
|
-
<img src="/images/{feature}/title.webp" alt="추운 겨울, 따뜻한 보상이 펑펑" />
|
|
129
|
-
</div>
|
|
130
|
-
</div>
|
|
131
|
-
</section>
|
|
132
|
-
</template>
|
|
133
|
-
|
|
134
|
-
<script setup lang="ts">
|
|
135
|
-
// Phase 1의 script 보존
|
|
136
|
-
</script>
|
|
137
|
-
|
|
138
|
-
<style scoped>
|
|
139
|
-
.heroSection {
|
|
140
|
-
position: relative;
|
|
141
|
-
width: 100%;
|
|
142
|
-
height: 853px; /* 1280 × 0.667 */
|
|
143
|
-
overflow: hidden;
|
|
144
|
-
}
|
|
145
|
-
.bg {
|
|
146
|
-
position: absolute;
|
|
147
|
-
height: 853px;
|
|
148
|
-
left: 0;
|
|
149
|
-
overflow: clip;
|
|
150
|
-
top: 0;
|
|
151
|
-
width: 100%;
|
|
152
|
-
}
|
|
153
|
-
.tree4 {
|
|
154
|
-
position: absolute;
|
|
155
|
-
bottom: 215px;
|
|
156
|
-
height: 453px;
|
|
157
|
-
left: 50%;
|
|
158
|
-
transform: translateX(calc(-50% + 599px));
|
|
159
|
-
opacity: 0.4;
|
|
160
|
-
width: 1727px;
|
|
161
|
-
}
|
|
162
|
-
.tree4 img {
|
|
163
|
-
position: absolute;
|
|
164
|
-
inset: 0;
|
|
165
|
-
max-width: none;
|
|
166
|
-
object-fit: cover;
|
|
167
|
-
pointer-events: none;
|
|
168
|
-
width: 100%;
|
|
169
|
-
height: 100%;
|
|
170
|
-
}
|
|
171
|
-
/* ... 모든 레이어의 CSS */
|
|
172
|
-
</style>
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### 직역 모드에서의 반응형
|
|
176
|
-
|
|
177
|
-
```
|
|
178
|
-
첫 번째 URL → <style scoped>에 base 스타일
|
|
179
|
-
두 번째 URL → 같은 <style scoped> 안에 @media (min-width:) 추가
|
|
180
|
-
기존 스타일 삭제 금지
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
---
|
|
184
|
-
|
|
185
|
-
## 이하: 일반 모드 (정형 레이어)
|
|
11
|
+
항상 외부 SCSS 파일에 스타일 작성.
|
|
186
12
|
|
|
187
13
|
---
|
|
188
14
|
|
|
@@ -296,6 +122,18 @@ $space-content: 18px; // 24 × 0.75
|
|
|
296
122
|
$bp-pc: 1024px;
|
|
297
123
|
```
|
|
298
124
|
|
|
125
|
+
### CSS 변수 패턴 처리
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
참조 코드에 Figma 디자인 토큰이 CSS 변수로 포함될 수 있음:
|
|
129
|
+
font-[family-name:var(--font/family/pretendard,...)]
|
|
130
|
+
text-[length:var(--font/size/heading/24,24px)]
|
|
131
|
+
text-[color:var(--color/grayscale/950,#171716)]
|
|
132
|
+
|
|
133
|
+
→ var() 안의 fallback 값(24px, #171716)을 사용.
|
|
134
|
+
→ CSS 변수명은 프로젝트 토큰 네이밍에 참고.
|
|
135
|
+
```
|
|
136
|
+
|
|
299
137
|
---
|
|
300
138
|
|
|
301
139
|
## 2. 컴포넌트 파일 변환
|