@su-record/vibe 2.8.47 → 2.8.49
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/hooks/scripts/figma-extract.js +141 -16
- package/package.json +1 -1
- package/skills/vibe.figma/SKILL.md +111 -884
- package/skills/vibe.figma.convert/SKILL.md +146 -27
- package/skills/vibe.figma.convert/rubrics/conversion-rules.md +16 -0
- package/skills/vibe.figma.extract/SKILL.md +56 -7
- package/skills/vibe.figma.extract/rubrics/image-rules.md +8 -0
|
@@ -11,8 +11,15 @@ tier: standard
|
|
|
11
11
|
**Claude는 시맨틱 판단(태그 선택, 컴포넌트 분리, 인터랙션)만 담당한다.**
|
|
12
12
|
|
|
13
13
|
```
|
|
14
|
+
⛔ 불변 규칙 — 복잡한 UI도 반드시 HTML로 구현한다:
|
|
15
|
+
"복잡하니까 이미지로 처리하자" → 이 사고방식 자체가 금지.
|
|
16
|
+
카드 그리드, 보상 목록, 교환소, 가격 표시 → 전부 HTML+CSS.
|
|
17
|
+
<img>는 순수 이미지 에셋(아이콘, 썸네일, 벡터 글자)에만 사용.
|
|
18
|
+
BG는 CSS background-image만 사용. <img> 태그 금지.
|
|
19
|
+
|
|
14
20
|
❌ 스크린샷을 보고 CSS 추정 (범용 LLM의 약점)
|
|
15
21
|
❌ Figma 레이어를 무분별하게 div soup로 변환
|
|
22
|
+
❌ 복잡한 섹션을 screenshot으로 이미지화 (코드 생성 중 screenshot 호출 금지)
|
|
16
23
|
✅ Figma Auto Layout → CSS Flexbox 1:1 매핑 (기계적)
|
|
17
24
|
✅ Figma CSS 속성 → SCSS 직접 변환 (추정 없음)
|
|
18
25
|
✅ Claude → 시맨틱 태그 선택 + 컴포넌트 설계 + 인터랙션
|
|
@@ -55,11 +62,45 @@ component-index (/tmp/{feature}/component-index.json) 에서 매칭되는 컴포
|
|
|
55
62
|
### 1-1. 노드 → HTML 매핑 규칙 (기계적)
|
|
56
63
|
|
|
57
64
|
```
|
|
65
|
+
⛔ 코드 작성 전: 이미지 vs HTML 판별 테이블을 먼저 작성한다 (BLOCKING).
|
|
66
|
+
섹션의 모든 1~2depth 노드를 순회하여 각각의 처리 방법을 결정.
|
|
67
|
+
판별 없이 코드를 작성하면 안 된다.
|
|
68
|
+
|
|
69
|
+
판별 규칙 (순서대로 적용, 하나라도 YES → HTML):
|
|
70
|
+
Q1. 이 노드의 자식 트리에 TEXT 노드가 있는가?
|
|
71
|
+
YES → HTML로 구현 (이미지에 텍스트를 넣지 않는다)
|
|
72
|
+
※ "가격 1,000", "보상 교환하기", "이벤트 기간" 등이 있으면 무조건 HTML
|
|
73
|
+
Q2. 같은 부모 아래 동일 구조 INSTANCE가 2개 이상인가?
|
|
74
|
+
YES → HTML 반복 구조 (v-for/.map) — 내부 아이콘/썸네일만 <img>
|
|
75
|
+
※ 카드 4개 그리드를 이미지 1장으로 렌더링하면 절대 안 됨
|
|
76
|
+
Q3. 클릭/인터랙션이 필요한가? (btn, CTA, link, tab)
|
|
77
|
+
YES → HTML <button>/<a>
|
|
78
|
+
Q4. 동적으로 변경되는 데이터인가? (가격, 수량, 기간, 상태)
|
|
79
|
+
YES → HTML 텍스트
|
|
80
|
+
모두 NO → 이미지 렌더링 가능 (벡터 글자, 래스터 에셋, 합성 BG 등)
|
|
81
|
+
|
|
58
82
|
각 노드에 대해 아래 규칙을 순서대로 적용:
|
|
59
83
|
|
|
60
|
-
1.
|
|
84
|
+
1. 배경 레이어 (BG 프레임 — 가장 먼저 처리):
|
|
85
|
+
BG 프레임 (name에 "BG"/"bg" 또는 부모와 동일 크기)
|
|
86
|
+
|
|
87
|
+
❌ 절대 금지 패턴 (이렇게 쓰면 안 됨):
|
|
88
|
+
<img src="hero-bg.webp" class="bg-img" />
|
|
89
|
+
<div class="bg"><img src="bg.webp" /></div>
|
|
90
|
+
position: absolute; inset: 0; → 이미지 배치
|
|
91
|
+
|
|
92
|
+
✅ 유일하게 허용되는 패턴:
|
|
93
|
+
부모 요소의 SCSS에 background-image로 처리:
|
|
94
|
+
.heroSection {
|
|
95
|
+
background-image: url('/images/{feature}/hero-bg.webp');
|
|
96
|
+
background-size: cover;
|
|
97
|
+
background-position: center top;
|
|
98
|
+
}
|
|
99
|
+
HTML에는 BG 관련 요소를 아무것도 넣지 않음.
|
|
100
|
+
|
|
101
|
+
2. 타입별 기본 매핑:
|
|
61
102
|
TEXT 노드 → <span> (Claude가 <h1>~<h6>, <p>, <button> 등으로 승격)
|
|
62
|
-
IMAGE fill → <img src="다운로드된 경로" />
|
|
103
|
+
IMAGE fill → <img src="다운로드된 경로" /> (판별 통과한 순수 에셋만)
|
|
63
104
|
VECTOR/GROUP → 크기가 작으면(≤64px) 아이콘 후보 → <img> (렌더링 이미지)
|
|
64
105
|
크기가 크면 장식 요소 → <div> + background
|
|
65
106
|
FRAME/INSTANCE:
|
|
@@ -67,20 +108,21 @@ component-index (/tmp/{feature}/component-index.json) 에서 매칭되는 컴포
|
|
|
67
108
|
Auto Layout 없음 → <div> + position:relative (자식은 absolute)
|
|
68
109
|
children 없음 → 빈 div 또는 스킵
|
|
69
110
|
|
|
70
|
-
2. 배경 레이어 판별:
|
|
71
|
-
BG 프레임 (name에 "BG"/"bg" 또는 부모와 동일 크기)
|
|
72
|
-
❌ <img> 태그로 배경 처리 금지
|
|
73
|
-
❌ position:absolute + inset:0 으로 이미지 배치 금지
|
|
74
|
-
✅ 부모 요소에 CSS background-image로 처리:
|
|
75
|
-
background-image: url('/images/{feature}/{section}-bg.webp');
|
|
76
|
-
background-size: cover;
|
|
77
|
-
background-position: center top;
|
|
78
|
-
✅ BG 프레임은 HTML에 아무것도 렌더링하지 않음 (CSS만)
|
|
79
|
-
|
|
80
111
|
3. 반복 패턴 감지:
|
|
81
|
-
같은 부모 아래 동일 타입 + 유사 구조(children 수 동일)
|
|
112
|
+
같은 부모 아래 동일 타입 + 유사 구조(children 수 동일) INSTANCE 2개 이상
|
|
82
113
|
→ v-for (Vue) 또는 .map() (React)
|
|
83
114
|
→ 첫 번째 노드를 기준으로 템플릿 생성
|
|
115
|
+
→ 카드 내부의 이미지 에셋(아이콘, 썸네일)만 <img>
|
|
116
|
+
→ 카드 레이아웃, 텍스트, 버튼, 가격은 HTML로 구현
|
|
117
|
+
|
|
118
|
+
❌ 잘못된 예: <img src="exchange-section1.webp" /> (카드 4개가 한 이미지)
|
|
119
|
+
✅ 올바른 예:
|
|
120
|
+
<div v-for="card in weeklyCards" :key="card.id" class="card">
|
|
121
|
+
<img :src="card.icon" :alt="card.name" class="card__icon" />
|
|
122
|
+
<span class="card__name">{{ card.name }}</span>
|
|
123
|
+
<span class="card__price">{{ card.price }}</span>
|
|
124
|
+
<button class="card__btn">보상 교환하기</button>
|
|
125
|
+
</div>
|
|
84
126
|
|
|
85
127
|
4. 스킵 대상:
|
|
86
128
|
크기 0px 노드
|
|
@@ -99,14 +141,29 @@ tree.json의 css 객체를 SCSS로 직접 변환한다. 추정하지 않는다.
|
|
|
99
141
|
node.css.justifyContent → justify-content
|
|
100
142
|
node.css.alignItems → align-items
|
|
101
143
|
node.css.gap → gap (→ vw 변환)
|
|
144
|
+
node.css.flexGrow → flex-grow (값 그대로)
|
|
102
145
|
node.css.padding → padding (→ vw 변환)
|
|
103
146
|
node.css.width → width (→ vw 또는 % 변환)
|
|
104
147
|
node.css.height → height (→ vw 변환, 또는 auto)
|
|
105
148
|
node.css.overflow → overflow
|
|
106
149
|
node.css.position → position
|
|
150
|
+
node.css.top → top (→ vw 변환, absolute 노드만)
|
|
151
|
+
node.css.left → left (→ vw 변환, absolute 노드만)
|
|
152
|
+
node.css.transform → transform (rotate — 값 그대로)
|
|
153
|
+
|
|
154
|
+
layoutSizing 처리 (node.layoutSizingH / node.layoutSizingV):
|
|
155
|
+
HUG → css에 width/height 없음 (auto). SCSS에서 생략하면 auto 동작.
|
|
156
|
+
FILL → 부모 flex-direction 확인 후 결정:
|
|
157
|
+
부모 row + 자식 FILL-H → flex: 1 0 0 (또는 width: 100%)
|
|
158
|
+
부모 column + 자식 FILL-V → flex: 1 0 0 (또는 height: 100%)
|
|
159
|
+
부모 row + 자식 FILL-V → align-self: stretch
|
|
160
|
+
부모 column + 자식 FILL-H → align-self: stretch
|
|
161
|
+
FIXED → css.width/height의 px 값을 vw로 변환 (기존 방식)
|
|
107
162
|
|
|
108
163
|
비주얼 (components/ 파일):
|
|
109
164
|
node.css.backgroundColor → background-color
|
|
165
|
+
node.css.backgroundImage → background-image (gradient — 값 그대로)
|
|
166
|
+
node.css.backgroundBlendMode → background-blend-mode (값 그대로)
|
|
110
167
|
node.css.color → color
|
|
111
168
|
node.css.fontFamily → font-family
|
|
112
169
|
node.css.fontSize → font-size (→ clamp 변환)
|
|
@@ -115,20 +172,35 @@ tree.json의 css 객체를 SCSS로 직접 변환한다. 추정하지 않는다.
|
|
|
115
172
|
node.css.letterSpacing → letter-spacing (→ vw 변환)
|
|
116
173
|
node.css.textAlign → text-align
|
|
117
174
|
node.css.borderRadius → border-radius (→ vw 변환)
|
|
118
|
-
node.css.border → border (width → vw
|
|
175
|
+
node.css.border → border (width → vw 변환, strokeAlign=INSIDE)
|
|
176
|
+
node.css.outline → outline (width → vw 변환, strokeAlign=OUTSIDE)
|
|
177
|
+
node.css.boxSizing → box-sizing (INSIDE stroke → border-box)
|
|
119
178
|
node.css.boxShadow → box-shadow (px → vw 변환)
|
|
120
179
|
node.css.opacity → opacity
|
|
121
180
|
node.css.mixBlendMode → mix-blend-mode
|
|
122
|
-
node.css.filter → filter (px → vw
|
|
181
|
+
node.css.filter → filter (blur px → vw 변환, grayscale/saturate → 값 그대로)
|
|
123
182
|
node.css.backdropFilter → backdrop-filter (px → vw 변환)
|
|
124
183
|
|
|
184
|
+
이미지 처리 (node.imageScaleMode):
|
|
185
|
+
FILL → background-size: cover
|
|
186
|
+
FIT → background-size: contain
|
|
187
|
+
CROP → background-size: cover; background-position: center
|
|
188
|
+
TILE → background-size: auto; background-repeat: repeat
|
|
189
|
+
|
|
190
|
+
다중 fill (node.fills 배열, 2개 이상일 때):
|
|
191
|
+
예: [IMAGE(grayscale) + GRADIENT_LINEAR] → 겹침 배경
|
|
192
|
+
CSS: background: url('img.webp'), linear-gradient(...);
|
|
193
|
+
background-blend-mode: multiply;
|
|
194
|
+
filter: grayscale(100%);
|
|
195
|
+
converter는 fills 배열 순서대로 CSS background 레이어링
|
|
196
|
+
|
|
125
197
|
반응형 단위 변환 (scaleFactor 사용하지 않음):
|
|
126
198
|
스토리보드 CONFIG에서 확보:
|
|
127
199
|
designWidth: 디자인 너비 (예: 720px 모바일, 2560px PC)
|
|
128
200
|
minWidth: 최소 지원 너비 (예: 340px)
|
|
129
201
|
breakpoint: PC/모바일 분계 (예: 1025px)
|
|
130
202
|
|
|
131
|
-
UI 요소 (width, height, padding, gap, border-radius, shadow 등):
|
|
203
|
+
UI 요소 (width, height, padding, gap, border-radius, shadow, top, left 등):
|
|
132
204
|
→ vw 비례: vw값 = (Figma px / designWidth) × 100
|
|
133
205
|
→ 예: gap: 24px / 720 × 100 = 3.33vw
|
|
134
206
|
→ width: 부모 대비 %도 가능 (620/720 = 86%)
|
|
@@ -154,7 +226,9 @@ tree.json의 css 객체를 SCSS로 직접 변환한다. 추정하지 않는다.
|
|
|
154
226
|
|
|
155
227
|
변환하지 않는 속성:
|
|
156
228
|
color, opacity, font-weight, font-family, z-index,
|
|
157
|
-
line-height(단위 없을 때), text-align, mix-blend-mode
|
|
229
|
+
line-height(단위 없을 때), text-align, mix-blend-mode,
|
|
230
|
+
transform(rotate), background-blend-mode, flex-grow,
|
|
231
|
+
box-sizing, grayscale/saturate(filter 내)
|
|
158
232
|
|
|
159
233
|
값이 없으면:
|
|
160
234
|
→ 해당 속성 생략 (추정 금지)
|
|
@@ -434,7 +508,15 @@ function handleShare(): void {
|
|
|
434
508
|
조건: BG 프레임 (name에 BG/bg 또는 부모와 크기 동일)
|
|
435
509
|
❌ <img> 태그 금지
|
|
436
510
|
✅ CSS background-image로만 처리:
|
|
437
|
-
부모 { background-image: url('...');
|
|
511
|
+
부모 { background-image: url('...'); }
|
|
512
|
+
imageScaleMode에 따라:
|
|
513
|
+
FILL → background-size: cover (기본)
|
|
514
|
+
FIT → background-size: contain
|
|
515
|
+
CROP → background-size: cover; background-position: center
|
|
516
|
+
TILE → background-size: auto; background-repeat: repeat
|
|
517
|
+
다중 fill (node.fills 배열):
|
|
518
|
+
background: url('img.webp'), linear-gradient(...);
|
|
519
|
+
background-blend-mode: multiply;
|
|
438
520
|
|
|
439
521
|
콘텐츠 이미지:
|
|
440
522
|
조건: imageRef 있음 + 독립적 크기 + TEXT 형제 없음
|
|
@@ -454,21 +536,58 @@ function handleShare(): void {
|
|
|
454
536
|
|
|
455
537
|
---
|
|
456
538
|
|
|
457
|
-
## 5. 반응형 추가 (
|
|
539
|
+
## 5. 반응형 추가 (MO↔PC 매칭)
|
|
458
540
|
|
|
459
541
|
```
|
|
460
|
-
|
|
542
|
+
remapped.json의 pcDiff를 사용하여 @media 오버라이드 추가.
|
|
543
|
+
MO 기본 코드 삭제 금지. PC는 항상 @media 블록 안에만 작성.
|
|
544
|
+
|
|
545
|
+
SCSS 구조:
|
|
546
|
+
.heroSection {
|
|
547
|
+
// MO 기본 (vw = px / 720 × 100)
|
|
548
|
+
width: 100%;
|
|
549
|
+
height: 177.78vw;
|
|
550
|
+
background-image: url('/images/{feature}/hero-bg.webp');
|
|
551
|
+
|
|
552
|
+
@media (min-width: #{$bp-desktop}) {
|
|
553
|
+
// PC 오버라이드 (vw = px / 2560 × 100, 또는 고정 px)
|
|
554
|
+
height: 32.66vw; // 836 / 2560 × 100
|
|
555
|
+
background-image: url('/images/{feature}/hero-bg-pc.webp');
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
CSS diff 처리 규칙:
|
|
560
|
+
|
|
561
|
+
같은 값 → MO만 유지 (PC에 쓰지 않음)
|
|
562
|
+
|
|
563
|
+
다른 px 값:
|
|
564
|
+
→ @media { 속성: PC값/pcDesignWidth × 100 vw; }
|
|
565
|
+
→ 예: MO gap:24px → 3.33vw, PC gap:60px → @media { gap: 2.34vw; }
|
|
566
|
+
|
|
567
|
+
다른 레이아웃:
|
|
568
|
+
→ @media { flex-direction: row; }
|
|
569
|
+
→ @media { justify-content: space-between; }
|
|
570
|
+
|
|
571
|
+
다른 이미지:
|
|
572
|
+
→ @media { background-image: url('/images/{feature}/pc-xxx.webp'); }
|
|
573
|
+
|
|
574
|
+
layoutSizing diff:
|
|
575
|
+
MO: HUG → PC: FIXED → @media { width: Xvw; }
|
|
576
|
+
MO: FILL → PC: FIXED → @media { width: Xvw; flex: initial; }
|
|
577
|
+
MO: FIXED → PC: FILL → @media { flex: 1 0 0; width: auto; }
|
|
578
|
+
|
|
579
|
+
MO에만 있는 속성 → MO 기본값 유지
|
|
580
|
+
PC에만 있는 속성 → @media에서 추가
|
|
581
|
+
PC에서 사라진 속성 → @media { display: none; } 또는 생략
|
|
461
582
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
CSS 속성 차이만 @media 오버라이드로 추가
|
|
583
|
+
absolute 위치 diff:
|
|
584
|
+
→ @media { top: PC값/pcDesignWidth × 100 vw; left: ...; }
|
|
465
585
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
다른 레이아웃 → @include pc { flex-direction: row; }
|
|
469
|
-
다른 이미지 → @include pc { content: url(/images/{feature}/desktop-xxx.webp); }
|
|
586
|
+
rotation diff:
|
|
587
|
+
→ @media { transform: rotate(PC각도deg); }
|
|
470
588
|
|
|
471
589
|
기존 모바일 코드 삭제 금지.
|
|
590
|
+
PC 오버라이드는 항상 @media 블록 안에만.
|
|
472
591
|
```
|
|
473
592
|
|
|
474
593
|
---
|
|
@@ -53,6 +53,19 @@ Q4. 이 노드의 콘텐츠가 동적 데이터인가? (가격, 수량, 기간,
|
|
|
53
53
|
|
|
54
54
|
위 체크에서 하나라도 YES → HTML로 구현
|
|
55
55
|
모두 NO → 이미지(벡터 글자, BG, 래스터, 복잡 그래픽)로 렌더링
|
|
56
|
+
|
|
57
|
+
실제 테스트에서 발생한 잘못된 패턴:
|
|
58
|
+
|
|
59
|
+
❌ <img src="hero-bg.webp" class="bg-img" /> → BG를 img 태그로
|
|
60
|
+
❌ <img src="exchange-section1.webp" /> → 카드 4개를 이미지 1장으로
|
|
61
|
+
❌ <img src="daily-step2-list.webp" /> → 보상 목록을 통째 이미지로
|
|
62
|
+
❌ <img src="hero-period-bg-mo.webp" class="period-bg"> → Period BG를 img로
|
|
63
|
+
|
|
64
|
+
✅ .heroSection { background-image: url('hero-bg.webp'); background-size: cover; }
|
|
65
|
+
✅ <div v-for="card in cards" :key="card.id">
|
|
66
|
+
<img :src="card.icon" /> <span>{{ card.price }}</span>
|
|
67
|
+
<button>보상 교환하기</button>
|
|
68
|
+
</div>
|
|
56
69
|
```
|
|
57
70
|
|
|
58
71
|
## CSS 직접 매핑 규칙
|
|
@@ -107,6 +120,9 @@ BEM 패턴: `.sectionName__childName`
|
|
|
107
120
|
|
|
108
121
|
## 자가 검증 (코드 작성 후)
|
|
109
122
|
|
|
123
|
+
- [ ] ⛔ BG 프레임이 <img> 태그로 처리되지 않았는가? (CSS background-image만 허용)
|
|
124
|
+
- [ ] ⛔ TEXT 자식이 있는 프레임이 통째 이미지로 처리되지 않았는가?
|
|
125
|
+
- [ ] ⛔ INSTANCE 반복 패턴(카드/아이템)이 이미지 1장으로 처리되지 않았는가?
|
|
110
126
|
- [ ] template 클래스 ↔ SCSS 클래스 1:1 일치
|
|
111
127
|
- [ ] 모든 img src가 static/에 실제 존재
|
|
112
128
|
- [ ] Auto Layout 노드 → SCSS에 flex 속성 존재
|
|
@@ -9,6 +9,18 @@ tier: standard
|
|
|
9
9
|
|
|
10
10
|
Figma REST API(`src/infra/lib/figma/`)를 사용하여 **구조적 코드 생성에 필요한 모든 데이터**를 추출.
|
|
11
11
|
|
|
12
|
+
```
|
|
13
|
+
⛔ 불변 규칙 — screenshot 명령의 허용 범위:
|
|
14
|
+
✅ BG 프레임 렌더링 (TEXT 자식이 없는 순수 배경만)
|
|
15
|
+
✅ 벡터 글자 GROUP (웹폰트 없는 장식 타이틀)
|
|
16
|
+
✅ 개별 아이콘/썸네일 (50~300px 독립 에셋)
|
|
17
|
+
✅ 섹션별 전체 스크린샷 → sections/ (Phase 6 검증용)
|
|
18
|
+
❌ TEXT 자식이 있는 프레임 (가격, 수량, 설명 등)
|
|
19
|
+
❌ INSTANCE 반복 패턴 프레임 (카드 그리드, 보상 목록)
|
|
20
|
+
❌ 버튼/인터랙티브 요소가 있는 프레임
|
|
21
|
+
❌ "섹션 콘텐츠"를 통째로 렌더링
|
|
22
|
+
```
|
|
23
|
+
|
|
12
24
|
```
|
|
13
25
|
추출 우선순위:
|
|
14
26
|
1순위: 노드 트리 + CSS (코드 생성의 PRIMARY 소스)
|
|
@@ -53,27 +65,53 @@ Bash:
|
|
|
53
65
|
|
|
54
66
|
트리 추출 도구가 자동 변환하는 속성. **이 값들이 SCSS에 직접 매핑된다:**
|
|
55
67
|
|
|
68
|
+
**레이아웃:**
|
|
69
|
+
|
|
56
70
|
| Figma 속성 | CSS | vw 변환 |
|
|
57
|
-
|
|
71
|
+
|-----------|-----|---------|
|
|
58
72
|
| `layoutMode=VERTICAL` | `display:flex; flex-direction:column` | ❌ |
|
|
59
73
|
| `layoutMode=HORIZONTAL` | `display:flex; flex-direction:row` | ❌ |
|
|
60
74
|
| `primaryAxisAlignItems` | `justify-content` | ❌ |
|
|
61
75
|
| `counterAxisAlignItems` | `align-items` | ❌ |
|
|
62
76
|
| `itemSpacing` | `gap` | ✅ |
|
|
77
|
+
| `layoutGrow=1` | `flex-grow: 1` | ❌ |
|
|
63
78
|
| `padding*` | `padding` | ✅ |
|
|
64
79
|
| `absoluteBoundingBox.width/height` | `width/height` | ✅ |
|
|
65
|
-
| `layoutPositioning=ABSOLUTE` | `position: absolute` |
|
|
80
|
+
| `layoutPositioning=ABSOLUTE` | `position: absolute` + `top/left` (부모 상대 좌표) | ✅ |
|
|
81
|
+
| `layoutSizingHorizontal=HUG` | width 삭제 (auto) | — |
|
|
82
|
+
| `layoutSizingHorizontal=FILL` | 메타데이터 `layoutSizingH` (converter가 flex:1/100% 결정) | — |
|
|
83
|
+
| `layoutSizingVertical=HUG` | height 삭제 (auto) | — |
|
|
84
|
+
| `layoutSizingVertical=FILL` | 메타데이터 `layoutSizingV` (converter가 결정) | — |
|
|
66
85
|
| `clipsContent` | `overflow: hidden` | ❌ |
|
|
67
|
-
|
|
68
|
-
|
|
86
|
+
|
|
87
|
+
**비주얼:**
|
|
88
|
+
|
|
89
|
+
| Figma 속성 | CSS | vw 변환 |
|
|
90
|
+
|-----------|-----|---------|
|
|
91
|
+
| `fills[].SOLID` | `background-color` | ❌ |
|
|
92
|
+
| `fills[].IMAGE` | `imageRef` + `imageScaleMode` (FILL/FIT/CROP/TILE) | — |
|
|
93
|
+
| `fills[].GRADIENT_LINEAR` | `background-image: linear-gradient(...)` | ❌ |
|
|
94
|
+
| `fills[].GRADIENT_RADIAL` | `background-image: radial-gradient(...)` | ❌ |
|
|
95
|
+
| `fills[] (2개 이상)` | `fills` 배열 (type, color, imageRef, gradient, blendMode, filters) | — |
|
|
96
|
+
| `fills[].blendMode` | `background-blend-mode` | ❌ |
|
|
97
|
+
| `fills[].filters.saturation` | `filter: grayscale(X%)` / `saturate(X%)` | ❌ |
|
|
69
98
|
| `fills[].color` (TEXT) | `color` | ❌ |
|
|
70
|
-
| `strokes[]
|
|
99
|
+
| `strokes[] + strokeAlign=INSIDE` | `border` + `box-sizing: border-box` | ✅ (width만) |
|
|
100
|
+
| `strokes[] + strokeAlign=OUTSIDE` | `outline` | ✅ (width만) |
|
|
101
|
+
| `strokes[] + strokeAlign=CENTER` | `border` | ✅ (width만) |
|
|
71
102
|
| `effects[].DROP_SHADOW` | `box-shadow` | ✅ (px만) |
|
|
72
|
-
| `effects[].
|
|
103
|
+
| `effects[].INNER_SHADOW` | `box-shadow` (inset) | ✅ (px만) |
|
|
104
|
+
| `effects[].LAYER_BLUR` | `filter: blur()` (누적) | ✅ |
|
|
73
105
|
| `effects[].BACKGROUND_BLUR` | `backdrop-filter: blur()` | ✅ |
|
|
74
106
|
| `cornerRadius` | `border-radius` | ✅ |
|
|
75
107
|
| `opacity` | `opacity` | ❌ |
|
|
76
|
-
| `
|
|
108
|
+
| `rotation` | `transform: rotate(Xdeg)` | ❌ |
|
|
109
|
+
| `blendMode` (노드 레벨) | `mix-blend-mode` | ❌ |
|
|
110
|
+
|
|
111
|
+
**텍스트:**
|
|
112
|
+
|
|
113
|
+
| Figma 속성 | CSS | vw 변환 |
|
|
114
|
+
|-----------|-----|---------|
|
|
77
115
|
| `style.fontFamily` | `font-family` | ❌ |
|
|
78
116
|
| `style.fontSize` | `font-size` | ✅ |
|
|
79
117
|
| `style.fontWeight` | `font-weight` | ❌ |
|
|
@@ -82,6 +120,17 @@ Bash:
|
|
|
82
120
|
| `style.textAlignHorizontal` | `text-align` | ❌ |
|
|
83
121
|
| `characters` | 텍스트 내용 | — |
|
|
84
122
|
|
|
123
|
+
### FigmaNode 메타데이터 필드 (css 외)
|
|
124
|
+
|
|
125
|
+
converter가 의사결정에 사용하는 추가 필드:
|
|
126
|
+
|
|
127
|
+
| 필드 | 타입 | 용도 |
|
|
128
|
+
|------|------|------|
|
|
129
|
+
| `layoutSizingH` | `'FIXED'\|'HUG'\|'FILL'` | 부모 context로 width 결정 |
|
|
130
|
+
| `layoutSizingV` | `'FIXED'\|'HUG'\|'FILL'` | 부모 context로 height 결정 |
|
|
131
|
+
| `imageScaleMode` | `'FILL'\|'FIT'\|'CROP'\|'TILE'` | background-size 결정 |
|
|
132
|
+
| `fills` | `array` | 다중 fill 레이어 상세 (2개 이상일 때만) |
|
|
133
|
+
|
|
85
134
|
---
|
|
86
135
|
|
|
87
136
|
## 2. 이미지 에셋 — 노드 렌더링 기반 (imageRef 개별 다운로드 금지)
|
|
@@ -4,7 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
```
|
|
6
6
|
❌ imageRef 개별 다운로드 금지 (텍스처 fill 공유 문제)
|
|
7
|
+
❌ 섹션/그룹을 통째 이미지로 렌더링 금지 (내부 TEXT/INSTANCE 포함 시)
|
|
7
8
|
✅ 모든 이미지는 Figma screenshot API로 노드 렌더링
|
|
9
|
+
✅ 렌더링 전 반드시 2-1.5 판별 규칙 확인 → HTML 대상은 렌더링하지 않음
|
|
10
|
+
|
|
11
|
+
실제 테스트에서 발생한 잘못된 렌더링:
|
|
12
|
+
❌ exchange-section1.webp (카드 4개 그리드를 이미지 1장으로)
|
|
13
|
+
❌ daily-step2-list.webp (보상 목록을 텍스트 포함하여 통째로)
|
|
14
|
+
❌ prize-section1.webp (응모 아이템을 텍스트 포함하여 통째로)
|
|
15
|
+
✅ 올바른 접근: 카드 내부의 아이콘/썸네일만 개별 렌더링
|
|
8
16
|
```
|
|
9
17
|
|
|
10
18
|
## Render Methods
|