@su-record/vibe 2.8.38 → 2.8.40

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@su-record/vibe",
3
- "version": "2.8.38",
3
+ "version": "2.8.40",
4
4
  "description": "AI Coding Framework for Claude Code — 56 agents, 45 skills, multi-LLM orchestration",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",
@@ -37,11 +37,43 @@ Figma 트리가 코드의 원천이다. 스크린샷은 검증용이다.
37
37
  ```
38
38
  /vibe.figma
39
39
  → Phase 0: Setup (스택 감지, 디렉토리 생성, 기존 자산 인덱싱)
40
- → Phase 1: Storyboard (스토리보드 → 레이아웃 + 컴포넌트 + 기능 정의)
41
- → Phase 2: 재료 확보 (디자인 URL → 트리 + 이미지 + 스크린샷)
40
+ → Phase 1: Storyboard (스토리보드 → 기능 스펙 문서 작성, 파일 생성 없음)
41
+ → Phase 2: 재료 확보 (디자인 URL → 트리 + 노드 렌더링 이미지 + 스크린샷)
42
42
  → Phase 3: 구조적 코드 생성 (트리 → HTML+SCSS 매핑 + 시맨틱 보강)
43
43
  → Phase 3.5: 컴파일 게이트 (tsc → build → dev 확인)
44
44
  → Phase 4: 시각 검증 루프 (렌더링 vs 스크린샷 비교 → 수정)
45
+ ─── 추가 브레이크포인트가 있으면 Phase 2~4 반복 ───
46
+ → Phase 5: 공통화 (브레이크포인트별 작업 병합 → 최종 프로젝트 구조)
47
+
48
+ 브레이크포인트별 작업 구조:
49
+ 각 Figma URL의 ROOT name에서 폴더명 추출 (kebab-case):
50
+ "MO_Main ..." → /tmp/{feature}/mo-main/
51
+ "PC_Main ..." → /tmp/{feature}/pc-main/
52
+
53
+ /tmp/{feature}/
54
+ ├── mo-main/ ← 첫 번째 URL (모바일)
55
+ │ ├── tree.json
56
+ │ ├── bg/
57
+ │ ├── content/
58
+ │ └── sections/
59
+ ├── pc-main/ ← 두 번째 URL (데스크탑)
60
+ │ ├── tree.json
61
+ │ ├── bg/
62
+ │ ├── content/
63
+ │ └── sections/
64
+ └── final/ ← Phase 5 공통화 결과
65
+ ├── components/{feature}/
66
+ └── styles/{feature}/
67
+
68
+ Phase 5 공통화:
69
+ 1. 각 브레이크포인트의 컴포넌트 diff
70
+ → 구조 동일: 1개로 병합
71
+ → 구조 다름: props로 분기 또는 별도 유지
72
+ 2. SCSS diff
73
+ → 같은 값: 기본 스타일
74
+ → 다른 값: @media 오버라이드
75
+ 3. 토큰 diff → 합집합 정리
76
+ 4. /tmp/ 임시 폴더 → 프로젝트 디렉토리에 최종 배치
45
77
  ```
46
78
 
47
79
  ---
@@ -186,7 +218,7 @@ URL에서 fileKey, nodeId 추출
186
218
 
187
219
  2단계: name 패턴으로 프레임 분류
188
220
  SPEC — "기능 정의서", "정책" → depth 높여서 텍스트 추출
189
- CONFIG — "해상도", "브라우저" → 스케일 팩터 계산
221
+ CONFIG — "해상도", "브라우저" → designWidth, minWidth, breakpoint 확보
190
222
  SHARED — "공통", "GNB", "Footer", "Popup" → 공통 컴포넌트 파악
191
223
  PAGE — "화면설계", "메인 -" → 섹션 목록 + 인터랙션 스펙
192
224
 
@@ -202,86 +234,56 @@ URL에서 fileKey, nodeId 추출
202
234
  → 또는 depth 높여서 하위 분할 조회
203
235
  ```
204
236
 
205
- ### 1-2. 레이아웃 + 컴포넌트 구성 (코드 생성)
206
-
207
- ```
208
- 스토리보드에서 파악한 섹션 구조로 실제 파일을 생성한다.
209
-
210
- 1. 루트 페이지 파일 생성 (Write):
211
- pages/{feature}.vue (또는 app/{feature}/page.tsx)
212
- 모든 섹션 컴포넌트 import + 순서대로 배치
213
- → 팝업/모달 조건부 렌더링
214
-
215
- 2. 섹션 컴포넌트 파일 생성 (Write):
216
- components/{feature}/HeroSection.vue
217
- components/{feature}/DailyCheckInSection.vue
218
- components/{feature}/PlayTimeMissionSection.vue
219
- ...PAGE 프레임 수만큼
220
-
221
- 컴포넌트에 반드시 포함:
222
-
223
- <template>:
224
- - 섹션 제목 <h2> (스토리보드에서 추출한 실제 텍스트)
225
- - 설명 텍스트 <p>
226
- - 리스트 렌더링 (v-for + 데이터)
227
- - 버튼/CTA (실제 라벨 + @click 핸들러)
228
- - 조건부 렌더링 (상태에 따른 v-if)
229
- 빈 template 금지. 브라우저에서 텍스트가 보여야 함.
230
-
231
- <script setup>:
232
- - JSDoc 주석으로 기능 요구사항 작성:
233
- /**
234
- * 일일 출석 미션 섹션
235
- *
236
- * [기능 정의]
237
- * - 매일 출석 스노우 토큰 즉시 지급
238
- * - 누적 3/5/7일 달성 시 추가 보상
239
- *
240
- * [인터랙션]
241
- * 출석하기 클릭 API호출 토큰지급 표시
242
- * ② 누적 보상 클릭 → 보상 수령
243
- *
244
- * [상태] default, checked, reward-claimed
245
- */
246
- - TypeScript 인터페이스
247
- - 목 데이터 (빈 배열 금지, 3~7개 아이템)
248
- - 목 데이터의 image 필드: 실제 다운로드할 이미지 경로 사용 금지
249
- → Phase 1에서는 이미지 경로를 '' (빈 문자열) 또는 placeholder 텍스트로 설정
250
- → Phase 2에서 실제 이미지 다운로드 후 경로를 업데이트
251
- → ❌ '/images/{feature}/token-100.png' (존재하지 않는 파일 참조 금지)
252
- - 이벤트 핸들러 stub (body는 // TODO:)
253
-
254
- ❌ <client-only> 래핑 금지 — SSR hydration 실패 위험
255
- (<client-only>는 window/document 직접 접근하는 컴포넌트에만 사용)
256
- <style> 블록 없음 — 스타일은 Phase 3에서 외부 파일로.
257
-
258
- 3. 공통 컴포넌트 (SHARED에서 파악):
259
- → 프로젝트에 이미 있으면 import 재사용
260
- → 없으면 새로 생성 (GNB, Footer, Popup)
261
-
262
- 4. 스타일 디렉토리 구조 생성 (빈 파일):
263
- styles/{feature}/index.scss
264
- styles/{feature}/_tokens.scss
265
- styles/{feature}/_mixins.scss
266
- styles/{feature}/_base.scss
267
- styles/{feature}/layout/
268
- styles/{feature}/components/
237
+ ### 1-2. 기능 스펙 문서 작성 (파일 생성 없음)
238
+
239
+ ```
240
+ Phase 1에서 코드 파일을 생성하지 않는다.
241
+ → Phase 1 HTML 구조와 Phase 3 트리 매핑이 충돌하면 이중 작업
242
+ → Phase 3에서 tree.json 기반으로 코드를 생성
243
+
244
+ Phase 1의 출력물은 기능 스펙 문서 (텍스트):
245
+
246
+ 1. 섹션 목록:
247
+ | # | 섹션 이름 | Figma 프레임 name | 높이 | 설명 |
248
+ |---|----------|------------------|------|------|
249
+ | 1 | Hero | Hero | 1280px | 키비주얼 + 이벤트 정보 |
250
+ | 2 | DailyCheckIn | Daily | 3604px | 출석 미션 |
251
+ | 3 | PlayTime | Frame 633371 | 11363px | 플레이타임 미션 |
252
+
253
+ 2. 섹션의 기능 정의:
254
+ /**
255
+ * 일일 출석 미션 섹션
256
+ *
257
+ * [기능 정의]
258
+ * - 매일 출석 스노우 토큰 즉시 지급
259
+ * - 누적 3/5/7일 달성 추가 보상
260
+ *
261
+ * [인터랙션]
262
+ * ① 출석하기 클릭 → API호출 → 토큰지급 표시
263
+ * ② 누적 보상 클릭 → 보상 수령
264
+ *
265
+ * [상태] default, checked, reward-claimed
266
+ */
267
+
268
+ 3. 공통 컴포넌트 목록:
269
+ 프로젝트에 이미 있는 컴포넌트 (GNB, Footer 등)
270
+ 새로 만들 공통 컴포넌트
271
+
272
+ 4. TypeScript 인터페이스 초안:
273
+ interface RewardItem { id: string; name: string; tokenAmount: number; status: 'locked'|'available'|'claimed' }
269
274
  ```
270
275
 
271
276
  ### 1-3. 검증
272
277
 
273
278
  ```
274
279
  Phase 1 완료 조건:
275
- 브라우저에서 열면 섹션의 텍스트/리스트/버튼이 보인다
276
- 클릭하면 핸들러가 실행된다
277
- 모든 컴포넌트에 [기능 정의] + [인터랙션] + [상태] JSDoc
278
- 배열 0개, 빈 template 0개, <style> 블록 0개
279
- <client-only> 전체 래핑 0개
280
- □ 목 데이터의 image 필드에 존재하지 않는 파일 경로 0개
281
- □ 빌드 성공
280
+ 모든 섹션이 목록에 포함되어 있다
281
+ 섹션에 [기능 정의] + [인터랙션] + [상태] 가 정의되어 있다
282
+ TypeScript 인터페이스 초안이 작성되어 있다
283
+ 공통 컴포넌트가 식별되어 있다
284
+ 파일을 하나도 생성하지 않았다
282
285
 
283
- 화면 = Phase 1 미완성. Phase 2로 넘어가지 않는다.
284
- 스타일/이미지는 없어도 됨 — Phase 3에서 채움.
286
+ Phase 3에서 스펙 + tree.json을 합쳐서 코드를 생성한다.
285
287
  ```
286
288
 
287
289
  ---
@@ -399,15 +401,26 @@ Phase 2 완료 시 /tmp/{feature}/ 에 다음이 준비되어야 함:
399
401
  - 간격: padding, gap, margin 사용 빈도 높은 값
400
402
  ```
401
403
 
402
- ### 스케일 팩터
404
+ ### 반응형 단위 (scaleFactor 사용하지 않음)
403
405
 
404
406
  ```
405
- 스토리보드 CONFIG 또는 기본값에서:
406
- 모바일: scaleFactor = 480 / 720 = 0.667 (또는 targetMobile / designMobile)
407
- PC: scaleFactor = 1920 / 2560 = 0.75 (또는 targetPc / designPc)
407
+ 스토리보드 CONFIG에서 확보:
408
+ designWidth: 디자인 너비 (예: 모바일 720px, PC 2560px)
409
+ minWidth: 최소 지원 너비 (예: 340px)
410
+ breakpoint: PC/모바일 분계 (예: 1025px)
411
+
412
+ UI 요소 → vw 비례:
413
+ vw값 = (Figma px / designWidth) × 100
414
+ 예: gap: 24px → 24/720 × 100 = 3.33vw
415
+ 예: width: 620px → 620/720 = 86.11% (부모 대비)
416
+
417
+ 폰트 → clamp(최소, vw, 최대):
418
+ 최소값 = 역할별 가독성 기준 (h1:16px, 본문:12px, 캡션:10px)
419
+ vw값 = (Figma px / designWidth) × 100
420
+ 최대값 = Figma 원본 px
421
+ 예: 24px 본문 → clamp(12px, 3.33vw, 24px)
408
422
 
409
- 적용 대상: font-size, padding, margin, gap, border-radius, width, height
410
- 적용 안 함: color, opacity, font-weight, z-index, line-height(단위 없을 때)
423
+ 변환 함: color, opacity, font-weight, z-index, line-height(단위 없을 때)
411
424
  ```
412
425
 
413
426
  ---
@@ -582,7 +595,7 @@ Phase 1에서 생성한 빈 SCSS 파일에 기본 내용 Write:
582
595
  - 반복 패턴 (동일 구조 3+) → v-for
583
596
  c. CSS 직접 매핑:
584
597
  - node.css의 모든 속성을 SCSS에 1:1 매핑
585
- - scaleFactor 적용 (px 값만)
598
+ - vw/clamp 반응형 단위 변환 (vibe.figma.convert 참조)
586
599
  - tree.json에 없는 CSS 값은 작성하지 않음
587
600
  d. Phase 1의 JSDoc, 인터페이스, 핸들러 보존
588
601
 
@@ -619,7 +632,7 @@ Phase 1에서 생성한 빈 SCSS 파일에 기본 내용 Write:
619
632
  SCSS (vibe.figma.convert 참조):
620
633
  layout/ → position, display, flex, width, height, padding, gap
621
634
  components/ → font, color, border, shadow, opacity
622
- 모든 수치는 재료함의 정확한 값 × scaleFactor.
635
+ 모든 수치는 tree.json의 정확한 값 vw/clamp 변환 (vibe.figma.convert 참조).
623
636
  추정 금지 — 값이 없으면 tree.json에서 다시 찾는다.
624
637
  ```
625
638
 
@@ -810,7 +823,7 @@ import { getComputedStyles, compareStyles, diffsToIssues } from 'src/infra/lib/b
810
823
  ])
811
824
 
812
825
  2. Figma 재료함의 기대값과 비교:
813
- // tree.json에서 해당 노드의 CSS 수치 (scaleFactor 적용 후)
826
+ // tree.json에서 해당 노드의 CSS 수치 (vw/clamp 변환 후)
814
827
  const expected = { 'font-size': '16px', 'color': '#ffffff', 'width': '465px' }
815
828
  const diffs = compareStyles(expected, actual)
816
829
 
@@ -9,14 +9,15 @@
9
9
 
10
10
  ## Design Specs
11
11
 
12
- ### Viewport / Scale
12
+ ### Viewport / Responsive
13
13
 
14
- | Breakpoint | Design Width | Target Width | Scale Factor |
15
- |-----------|-------------|-------------|-------------|
16
- | Mobile (base) | {{DESIGN_MOBILE_PX}}px | {{TARGET_MOBILE_PX}}px | {{SCALE_MOBILE}} |
17
- | Desktop | {{DESIGN_PC_PX}}px | {{TARGET_PC_PX}}px | {{SCALE_PC}} |
14
+ | Breakpoint | Design Width | Min Width | CSS 단위 |
15
+ |-----------|-------------|-----------|---------|
16
+ | Mobile (base) | {{DESIGN_MOBILE_PX}}px | {{MIN_WIDTH}}px | vw + clamp |
17
+ | Desktop | {{DESIGN_PC_PX}}px | | vw + clamp |
18
18
 
19
19
  Breakpoint threshold: `@media (min-width: {{BP_PC}}px)`
20
+ ROOT folder: `{{MO_FOLDER}}/`, `{{PC_FOLDER}}/`
20
21
 
21
22
  ### Color Tokens
22
23
 
@@ -26,15 +27,15 @@ Breakpoint threshold: `@media (min-width: {{BP_PC}}px)`
26
27
 
27
28
  ### Typography Tokens
28
29
 
29
- | Token | Figma px | Scaled px | Weight | Role |
30
- |-------|---------|----------|--------|------|
31
- | `$text-{{ROLE}}` | {{FIGMA_PX}}px | {{SCALED_PX}}px | {{WEIGHT}} | {{ROLE}} |
30
+ | Token | Figma px | vw | clamp 최소 | Role |
31
+ |-------|---------|-----|----------|------|
32
+ | `$text-{{ROLE}}` | {{FIGMA_PX}}px | {{VW}}vw | {{MIN_PX}}px | {{ROLE}} |
32
33
 
33
34
  ### Spacing Tokens
34
35
 
35
- | Token | Figma px | Scaled px | Usage |
36
- |-------|---------|----------|-------|
37
- | `$space-{{NAME}}` | {{FIGMA_PX}}px | {{SCALED_PX}}px | {{USAGE}} |
36
+ | Token | Figma px | vw | Usage |
37
+ |-------|---------|-----|-------|
38
+ | `$space-{{NAME}}` | {{FIGMA_PX}}px | {{VW}}vw | {{USAGE}} |
38
39
 
39
40
  ---
40
41
 
@@ -44,38 +45,40 @@ Breakpoint threshold: `@media (min-width: {{BP_PC}}px)`
44
45
  |---|-------------|---------------|-----------|----------------|
45
46
  | 1 | {{SECTION_NAME}} | `components/{{FEATURE_KEY}}/{{ComponentName}}.vue` | {{NODE_COUNT}} | {{HEIGHT}}px |
46
47
 
47
- **Generation:** tree.json → HTML+SCSS 구조적 매핑 (external SCSS)
48
+ **Generation:** tree.json → HTML+SCSS 구조적 매핑 (vw/clamp 반응형)
48
49
 
49
50
  ---
50
51
 
51
52
  ## Asset Manifest
52
53
 
53
- | Variable | Local Path | Type | Alt Text |
54
- |----------|-----------|------|----------|
55
- | `{{IMG_VAR}}` | `/images/{{FEATURE_KEY}}/{{FILE_NAME}}.webp` | {{bg\|content\|decorative}} | {{ALT}} |
54
+ | Image | Local Path | Type | Render Method |
55
+ |-------|-----------|------|--------------|
56
+ | {{NAME}} | `/images/{{FEATURE_KEY}}/{{FILE_NAME}}.png` | {{bg\|content\|vector-text}} | {{frame-render\|node-render\|group-render}} |
56
57
 
57
- Total assets: {{ASSET_COUNT}}
58
+ Total assets: {{ASSET_COUNT}} (BG 렌더링 {{BG_COUNT}}장 + 콘텐츠 {{CONTENT_COUNT}}장)
58
59
 
59
60
  ---
60
61
 
61
62
  ## File Structure
62
63
 
63
64
  ```
64
- components/{{FEATURE_KEY}}/
65
- {{ComponentName}}.vue
66
-
67
- public/images/{{FEATURE_KEY}}/
68
- {{FILE_NAME}}.webp
69
-
70
- styles/{{FEATURE_KEY}}/
71
- index.scss
72
- _tokens.scss
73
- _mixins.scss
74
- _base.scss
75
- layout/
76
- _{{section}}.scss
77
- components/
78
- _{{section}}.scss
65
+ /tmp/{{FEATURE_KEY}}/
66
+ {{MO_FOLDER}}/ ← 모바일 작업
67
+ tree.json
68
+ bg/
69
+ content/
70
+ sections/
71
+ {{PC_FOLDER}}/ ← 데스크탑 작업
72
+ ...
73
+ final/ ← Phase 5 공통화 결과
74
+ components/{{FEATURE_KEY}}/
75
+ styles/{{FEATURE_KEY}}/
76
+ index.scss
77
+ _tokens.scss
78
+ _mixins.scss
79
+ _base.scss
80
+ layout/
81
+ components/
79
82
  ```
80
83
 
81
84
  ---
@@ -84,8 +87,9 @@ styles/{{FEATURE_KEY}}/
84
87
 
85
88
  - [ ] No `figma.com/api` URLs in any generated file
86
89
  - [ ] No `placeholder` or empty `src=""` in components
87
- - [ ] No `<style>` blocks in components (normal mode)
88
- - [ ] All assets downloaded and non-zero bytes
90
+ - [ ] No `<style>` blocks in components
91
+ - [ ] All images are node-rendered (no imageRef direct download)
92
+ - [ ] No image file > 5MB (텍스처 fill 의심)
89
93
  - [ ] Build passes without errors
90
94
  - [ ] Screenshot comparison: P1 issues = 0
91
95
 
@@ -44,7 +44,8 @@ component-index (/tmp/{feature}/component-index.json) 에서 매칭되는 컴포
44
44
  - tree.json (노드 트리 + CSS 속성 — 코드 생성의 PRIMARY 소스)
45
45
  - /tmp/{feature}/images/ (다운로드된 이미지 에셋)
46
46
  - 섹션 스크린샷 (검증용 — 생성에 사용하지 않음)
47
- - scaleFactor (모바일: 480/720=0.667, PC: 1920/2560=0.75)
47
+ - designWidth (스토리보드 CONFIG: 모바일 720px, PC 2560px)
48
+ - minWidth (최소 지원: 340px)
48
49
 
49
50
  출력:
50
51
  - 컴포넌트 파일 (Vue SFC / React TSX)
@@ -92,10 +93,10 @@ tree.json의 css 객체를 SCSS로 직접 변환한다. 추정하지 않는다.
92
93
  node.css.flexDirection → flex-direction
93
94
  node.css.justifyContent → justify-content
94
95
  node.css.alignItems → align-items
95
- node.css.gap → gap (× scaleFactor)
96
- node.css.padding → padding (× scaleFactor)
97
- node.css.width → width (× scaleFactor)
98
- node.css.height → height (× scaleFactor)
96
+ node.css.gap → gap ( vw 변환)
97
+ node.css.padding → padding ( vw 변환)
98
+ node.css.width → width ( vw 또는 % 변환)
99
+ node.css.height → height ( vw 변환, 또는 auto)
99
100
  node.css.overflow → overflow
100
101
  node.css.position → position
101
102
 
@@ -103,24 +104,52 @@ tree.json의 css 객체를 SCSS로 직접 변환한다. 추정하지 않는다.
103
104
  node.css.backgroundColor → background-color
104
105
  node.css.color → color
105
106
  node.css.fontFamily → font-family
106
- node.css.fontSize → font-size (× scaleFactor)
107
+ node.css.fontSize → font-size ( clamp 변환)
107
108
  node.css.fontWeight → font-weight
108
109
  node.css.lineHeight → line-height
109
- node.css.letterSpacing → letter-spacing (× scaleFactor)
110
+ node.css.letterSpacing → letter-spacing ( vw 변환)
110
111
  node.css.textAlign → text-align
111
- node.css.borderRadius → border-radius (× scaleFactor)
112
- node.css.border → border (width × scaleFactor)
113
- node.css.boxShadow → box-shadow (px값만 × scaleFactor)
112
+ node.css.borderRadius → border-radius ( vw 변환)
113
+ node.css.border → border (width vw 변환)
114
+ node.css.boxShadow → box-shadow (px vw 변환)
114
115
  node.css.opacity → opacity
115
116
  node.css.mixBlendMode → mix-blend-mode
116
- node.css.filter → filter (px값만 × scaleFactor)
117
- node.css.backdropFilter → backdrop-filter (px값만 × scaleFactor)
118
-
119
- scaleFactor 적용:
120
- 적용: width, height, padding, gap, margin, font-size, letter-spacing,
121
- border-radius, border-width, box-shadow px, filter px
122
- 미적용: color, opacity, font-weight, font-family, z-index,
123
- line-height(단위 없을 때), text-align, mix-blend-mode
117
+ node.css.filter → filter (px vw 변환)
118
+ node.css.backdropFilter → backdrop-filter (px vw 변환)
119
+
120
+ 반응형 단위 변환 (scaleFactor 사용하지 않음):
121
+ 스토리보드 CONFIG에서 확보:
122
+ designWidth: 디자인 너비 (예: 720px 모바일, 2560px PC)
123
+ minWidth: 최소 지원 너비 (예: 340px)
124
+ breakpoint: PC/모바일 분계 (예: 1025px)
125
+
126
+ UI 요소 (width, height, padding, gap, border-radius, shadow 등):
127
+ → vw 비례: vw값 = (Figma px / designWidth) × 100
128
+ → 예: gap: 24px / 720 × 100 = 3.33vw
129
+ → width: 부모 대비 %도 가능 (620/720 = 86%)
130
+
131
+ 폰트 (font-size):
132
+ → clamp(최소, vw, 최대): 가독성 최소값 보장
133
+ → vw값 = (Figma px / designWidth) × 100
134
+ → 최소값 = 역할(role)에 따라 결정 (Claude 시맨틱 판단)
135
+ → 최대값 = Figma 원본 px
136
+
137
+ | 역할 | 최소 | 판단 기준 |
138
+ |------|------|----------|
139
+ | h1~h2 제목 | 16px | name에 "title", 가장 큰 fontSize |
140
+ | h3~h4 소제목 | 14px | 중간 크기 fontSize |
141
+ | 본문 p | 12px | TEXT 노드, 긴 텍스트 |
142
+ | 캡션/라벨 | 10px | 작은 fontSize, 짧은 텍스트 |
143
+ | 버튼 | 12px | name에 "btn" |
144
+
145
+ 예시:
146
+ 디자인 24px, 본문 → font-size: clamp(12px, 3.33vw, 24px);
147
+ 디자인 48px, 제목 → font-size: clamp(16px, 6.67vw, 48px);
148
+ 디자인 16px, 캡션 → font-size: clamp(10px, 2.22vw, 16px);
149
+
150
+ 변환하지 않는 속성:
151
+ color, opacity, font-weight, font-family, z-index,
152
+ line-height(단위 없을 때), text-align, mix-blend-mode
124
153
 
125
154
  값이 없으면:
126
155
  → 해당 속성 생략 (추정 금지)
@@ -173,21 +202,21 @@ components/ → font-size, font-weight, color, line-height, letter-spacing,
173
202
  background-color, background-image, mix-blend-mode, filter
174
203
  ```
175
204
 
176
- ### layout 예시 (트리 기반 — 추정 없음)
205
+ ### layout 예시 (트리 기반 — vw 반응형)
177
206
 
178
207
  ```scss
179
208
  // tree.json 데이터:
180
209
  // Hero: { width:720, height:1280 }
181
210
  // Title: { display:flex, flexDirection:column, alignItems:center, gap:24px, width:620, height:230 }
182
211
  // Period: { display:flex, flexDirection:column, gap:10px, padding:"22px 14px", width:600, height:220 }
183
- // scaleFactor = 0.667
212
+ // designWidth = 720px → vw = (px / 720) × 100
184
213
 
185
214
  @use '../tokens' as t;
186
215
 
187
216
  .heroSection {
188
217
  position: relative;
189
218
  width: 100%;
190
- height: 854px; // 1280 × 0.667
219
+ height: 177.78vw; // 1280 / 720 × 100
191
220
  overflow: hidden; // tree: overflow:hidden
192
221
  }
193
222
 
@@ -195,30 +224,32 @@ components/ → font-size, font-weight, color, line-height, letter-spacing,
195
224
  display: flex; // tree: display:flex
196
225
  flex-direction: column; // tree: flexDirection:column
197
226
  align-items: center; // tree: alignItems:center
198
- gap: 16px; // tree: 24 × 0.667
199
- width: 414px; // tree: 620 × 0.667
227
+ gap: 3.33vw; // tree: 24 / 720 × 100
228
+ width: 86.11%; // tree: 620 / 720 (부모 대비 %)
200
229
  }
201
230
 
202
231
  .heroPeriod {
203
232
  display: flex; // tree: display:flex
204
233
  flex-direction: column; // tree: flexDirection:column
205
- gap: 7px; // tree: 10 × 0.667
206
- padding: 15px 9px; // tree: "22px 14px" × 0.667
207
- width: 400px; // tree: 600 × 0.667
234
+ gap: 1.39vw; // tree: 10 / 720 × 100
235
+ padding: 3.06vw 1.94vw; // tree: "22px 14px" / 720 × 100
236
+ width: 83.33%; // tree: 600 / 720
208
237
  }
209
238
  ```
210
239
 
211
- ### components 예시 (트리 기반)
240
+ ### components 예시 (트리 기반 — clamp 폰트)
212
241
 
213
242
  ```scss
214
243
  // tree.json 데이터:
215
244
  // TEXT "참여 대상": { fontSize:24px, fontWeight:600, color:#ffffff, fontFamily:Pretendard }
216
245
  // BTN_Share: { borderRadius:500px, backgroundColor:rgba(13,40,61,0.5), border:"1px solid #ffffff" }
246
+ // designWidth = 720px, minWidth = 340px
217
247
 
218
248
  @use '../tokens' as t;
219
249
 
220
250
  .heroTarget {
221
- font-size: 16px; // tree: 24 × 0.667
251
+ // 본문 역할 최소 12px
252
+ font-size: clamp(12px, 3.33vw, 24px); // tree: 24 / 720 × 100 = 3.33vw
222
253
  font-weight: 600; // tree: fontWeight:600
223
254
  color: #ffffff; // tree: color:#ffffff
224
255
  font-family: t.$font-pretendard;
@@ -226,11 +257,11 @@ components/ → font-size, font-weight, color, line-height, letter-spacing,
226
257
  }
227
258
 
228
259
  .heroShareBtn {
229
- border-radius: 500px; // tree: borderRadius:500px (비율이므로 scaleFactor 미적용)
260
+ border-radius: 69.44vw; // tree: 500 / 720 × 100 (원형 유지)
230
261
  background-color: rgba(13, 40, 61, 0.5); // tree 그대로
231
- border: 1px solid #ffffff; // tree 그대로
232
- width: 48px; // tree: 72 × 0.667
233
- height: 48px; // tree: 72 × 0.667
262
+ border: 0.14vw solid #ffffff; // tree: 1 / 720 × 100
263
+ width: 10vw; // tree: 72 / 720 × 100
264
+ height: 10vw; // 정사각형 유지
234
265
  display: flex; // tree: display:flex
235
266
  justify-content: center; // tree: justifyContent:center
236
267
  align-items: center; // tree: alignItems:center
@@ -382,7 +413,7 @@ function handleShare(): void {
382
413
 
383
414
  콘텐츠 이미지:
384
415
  조건: imageRef 있음 + 독립적 크기 + TEXT 형제 없음
385
- 매핑: width/height tree × scaleFactor
416
+ 매핑: width/height vw 변환
386
417
  태그: <img alt="가장 가까운 TEXT 노드의 characters" />
387
418
 
388
419
  장식 이미지:
@@ -392,7 +423,7 @@ function handleShare(): void {
392
423
 
393
424
  아이콘:
394
425
  조건: VECTOR/GROUP + 크기 ≤ 64px
395
- 매핑: width/height × scaleFactor
426
+ 매핑: width/height vw 변환
396
427
  태그: <img alt="기능 설명" /> 또는 인라인 SVG
397
428
  ```
398
429
 
@@ -42,13 +42,22 @@ line-height, letter-spacing, text-align, border, border-radius,
42
42
  box-shadow, opacity, mix-blend-mode, filter, backdrop-filter
43
43
  ```
44
44
 
45
- ## Scale Factor 적용
45
+ ## 반응형 단위 변환 (scaleFactor 사용하지 않음)
46
46
 
47
- **적용 (px 값):**
48
- font-size, padding, margin, gap, width, height, border-radius,
49
- border-width, box-shadow px, filter px, letter-spacing
47
+ 스토리보드 CONFIG에서 designWidth, minWidth 확보.
50
48
 
51
- **미적용:**
49
+ **UI 요소 → vw 비례:**
50
+ vw값 = (Figma px / designWidth) × 100
51
+ 적용: width, height, padding, gap, margin, border-radius,
52
+ border-width, box-shadow px, filter px, letter-spacing
53
+
54
+ **폰트 → clamp(최소, vw, 최대):**
55
+ vw값 = (Figma px / designWidth) × 100
56
+ 최소값 = 역할에 따라 결정:
57
+ h1~h2: 16px, h3~h4: 14px, 본문: 12px, 캡션: 10px, 버튼: 12px
58
+ 최대값 = Figma 원본 px
59
+
60
+ **변환하지 않는 속성:**
52
61
  color, opacity, font-weight, font-family, z-index,
53
62
  line-height(단위 없을 때), text-align, mix-blend-mode,
54
63
  rotate, % 값
@@ -75,48 +75,46 @@ defineProps<{
75
75
 
76
76
  ---
77
77
 
78
- ## SCSS Layout (tree.json 직접 매핑)
78
+ ## SCSS Layout (tree.json vw 반응형)
79
79
 
80
80
  ```scss
81
81
  // tree.json 데이터:
82
82
  // {{SECTION_NAME}}: { width:{{WIDTH}}, height:{{HEIGHT}}, overflow:hidden }
83
83
  // {{CHILD_1}}: { display:flex, flexDirection:column, gap:{{GAP}}px, padding:"{{PADDING}}" }
84
- // scaleFactor = {{SCALE_FACTOR}}
84
+ // designWidth = {{DESIGN_WIDTH}}px → vw = (px / designWidth) × 100
85
85
 
86
86
  .{{sectionName}} {
87
87
  position: relative;
88
88
  width: 100%;
89
- height: {{HEIGHT_SCALED}}px; // tree: {{HEIGHT}} × {{SCALE_FACTOR}}
89
+ height: {{HEIGHT_VW}}vw; // tree: {{HEIGHT}} / {{DESIGN_WIDTH}} × 100
90
90
  overflow: hidden; // tree: overflow:hidden
91
- }
92
-
93
- .{{sectionName}}__bg {
94
- position: absolute;
95
- inset: 0;
96
- z-index: 0;
97
- img { width: 100%; height: 100%; object-fit: cover; }
91
+ background-image: url('/images/{{FEATURE_KEY}}/{{section}}-bg.png');
92
+ background-size: cover;
93
+ background-position: center top;
98
94
  }
99
95
 
100
96
  .{{sectionName}}__{{child1Name}} {
101
97
  display: flex; // tree: display:flex
102
98
  flex-direction: column; // tree: flexDirection:column
103
- gap: {{GAP_SCALED}}px; // tree: {{GAP}} × {{SCALE_FACTOR}}
104
- padding: {{PADDING_SCALED}}; // tree: "{{PADDING}}" × {{SCALE_FACTOR}}
99
+ gap: {{GAP_VW}}vw; // tree: {{GAP}} / {{DESIGN_WIDTH}} × 100
100
+ padding: {{PADDING_VW}}; // tree: "{{PADDING}}" / {{DESIGN_WIDTH}} × 100
105
101
  }
106
102
  ```
107
103
 
108
104
  ---
109
105
 
110
- ## SCSS Components (tree.json 직접 매핑)
106
+ ## SCSS Components (tree.json clamp 폰트)
111
107
 
112
108
  ```scss
113
109
  // tree.json 데이터:
114
110
  // TEXT "{{TEXT_1}}": { fontSize:{{FONT_SIZE}}px, fontWeight:{{FONT_WEIGHT}}, color:{{COLOR}} }
111
+ // designWidth = {{DESIGN_WIDTH}}px
115
112
 
116
113
  .{{sectionName}}__title {
117
- font-size: {{FONT_SIZE_SCALED}}px; // tree: {{FONT_SIZE}} × {{SCALE_FACTOR}}
118
- font-weight: {{FONT_WEIGHT}}; // tree: 직접 (scaleFactor 미적용)
119
- color: {{COLOR}}; // tree: 직접 (scaleFactor 미적용)
114
+ // 역할: 제목 최소 16px
115
+ font-size: clamp(16px, {{FONT_SIZE_VW}}vw, {{FONT_SIZE}}px); // tree: {{FONT_SIZE}} / {{DESIGN_WIDTH}} × 100
116
+ font-weight: {{FONT_WEIGHT}}; // tree: 직접 (변환 안 함)
117
+ color: {{COLOR}}; // tree: 직접 (변환 안 함)
120
118
  }
121
119
  ```
122
120