@su-record/vibe 2.8.12 → 2.8.14

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.
@@ -0,0 +1,583 @@
1
+ ---
2
+ name: vibe-figma-style
3
+ description: Style architecture — tokens, SCSS, class naming, responsive tokens
4
+ triggers: []
5
+ tier: standard
6
+ ---
7
+
8
+ # Skill: vibe-figma-style — 스타일 아키텍처
9
+
10
+ 토큰 포맷, SCSS 규칙, 클래스 네이밍, 반응형 토큰 계산.
11
+
12
+ ---
13
+
14
+ ## Phase 4: Style Architecture
15
+
16
+ ### 4-1. Global Styles File
17
+
18
+ **Token resolution priority** (default mode):
19
+
20
+ 1. **MASTER.md tokens** — if `.claude/vibe/design-system/{project}/MASTER.md` exists, map Figma values to these tokens
21
+ 2. **design-context.json tokens** — if `detectedStack.fonts`, `aesthetic.colorMood` exist, align with these
22
+ 3. **New figma-tokens** — only for values that have no existing match
23
+
24
+ **--new mode**: Generate self-contained token file (no MASTER.md dependency), in project's standard directories.
25
+
26
+ #### Output Structure (both modes — project structure 준수)
27
+
28
+ 프로젝트의 기존 디렉토리 구조를 감지하여 올바른 위치에 파일 생성.
29
+
30
+ **Step 1: 디렉토리 감지**
31
+
32
+ ```
33
+ 페이지 디렉토리:
34
+ Next.js → pages/ or app/
35
+ Nuxt → pages/
36
+ React → src/pages/ or src/views/
37
+ Vue → src/views/
38
+
39
+ 컴포넌트 디렉토리:
40
+ Next.js → components/ or src/components/
41
+ Nuxt → components/
42
+ React → src/components/
43
+ Vue → src/components/
44
+
45
+ 스타일 디렉토리:
46
+ SCSS → assets/scss/ or src/scss/ or src/styles/
47
+ CSS → src/styles/ or styles/
48
+ Tailwind → tailwind.config.* (extend)
49
+ ```
50
+
51
+ **Step 2: 스타일 구조 — 모드별 분리**
52
+
53
+ Figma 파일명에서 피처명 자동 추출 → kebab-case 변환.
54
+
55
+ ### 기본 모드 (기존 프로젝트에 추가)
56
+
57
+ ```
58
+ ⚠️ 기존 스타일 구조를 먼저 분석하고 그대로 따른다.
59
+ 1. Glob/Grep으로 기존 스타일 파일 패턴 탐색:
60
+ - 디렉토리 구조 (styles/, scss/, css/)
61
+ - 파일 네이밍 (BEM, camelCase, kebab-case)
62
+ - import 방식 (@use, @import, CSS Modules)
63
+ - 기존 변수/mixin 파일 위치
64
+ 2. 기존 토큰/변수/mixin을 최대한 재사용
65
+ 3. 새 컴포넌트 스타일만 기존 패턴대로 추가
66
+ 4. 기존 스타일 파일을 수정하지 않음 (사이드이펙트 방지)
67
+
68
+ 예: 기존 프로젝트가 assets/scss/ 구조를 쓰면:
69
+ assets/scss/_variables.scss ← 기존 (수정 안 함)
70
+ assets/scss/_mixins.scss ← 기존 (재사용)
71
+ assets/scss/pages/
72
+ _winter-pcbang.scss ← 새 피처 스타일 (기존 패턴 따름)
73
+
74
+ 예: 기존 프로젝트가 CSS Modules를 쓰면:
75
+ components/winter-pcbang/
76
+ HeroSection.module.scss ← 기존 패턴대로
77
+ DailyCheckIn.module.scss
78
+ ```
79
+
80
+ ### --new 모드 (새 피처, 자체 완결)
81
+
82
+ ```
83
+ 피처 전용 스타일 폴더를 생성하고, 글로벌 + 컴포넌트별 2-tier 구조:
84
+
85
+ styles/{feature-name}/
86
+ index.scss ← 진입점
87
+
88
+ // ── foundation (토큰, mixin, 기반) ──
89
+ _tokens.scss ← 피처 전용 토큰
90
+ _mixins.scss ← 피처 전용 mixin
91
+ _base.scss ← 피처 공통 reset/폰트
92
+
93
+ // ── layout (섹션 배치, 구조) ──
94
+ layout/
95
+ _page.scss ← 페이지 전체 레이아웃
96
+ _hero.scss ← HeroSection 배치/구조
97
+ _daily-checkin.scss ← DailyCheckInSection 배치/구조
98
+ _play-time-mission.scss ← PlayTimeMissionSection 배치/구조
99
+ _token-exchange.scss ← TokenExchangeSection 배치/구조
100
+ _token-raffle.scss ← TokenRaffleSection 배치/구조
101
+ _caution.scss ← CautionSection 배치/구조
102
+
103
+ // ── components (재사용 UI 디자인) ──
104
+ components/
105
+ _card.scss ← 카드 디자인 (보상, 상품, 아이템)
106
+ _button.scss ← 버튼 디자인 (CTA, 출석, 교환)
107
+ _badge.scss ← 배지 디자인 (완료, 진행중, 잠금)
108
+ _progress.scss ← 프로그레스 바, 게이지
109
+ _popups.scss ← 팝업/모달 디자인
110
+ _tooltip.scss ← 툴팁, 안내 말풍선
111
+ _form.scss ← 입력 폼 (이메일, 검색)
112
+
113
+ index.scss 내용:
114
+ // foundation
115
+ @use 'tokens';
116
+ @use 'mixins';
117
+ @use 'base';
118
+
119
+ // layout
120
+ @use 'layout/page';
121
+ @use 'layout/hero';
122
+ @use 'layout/daily-checkin';
123
+ @use 'layout/play-time-mission';
124
+ @use 'layout/token-exchange';
125
+ @use 'layout/token-raffle';
126
+ @use 'layout/caution';
127
+
128
+ // components
129
+ @use 'components/card';
130
+ @use 'components/button';
131
+ @use 'components/badge';
132
+ @use 'components/progress';
133
+ @use 'components/popups';
134
+ @use 'components/tooltip';
135
+ @use 'components/form';
136
+
137
+ layout vs components 구분 기준:
138
+ layout/ → 섹션의 위치, 크기, 배치, 간격, 배경
139
+ (position, display, flex/grid, padding, margin, background-image)
140
+ components/ → UI 요소의 모양, 색상, 타이포, 인터랙션 상태
141
+ (color, font, border, border-radius, shadow, hover, focus)
142
+
143
+ 각 컴포넌트 스타일 파일 규칙:
144
+ - @use '../tokens' as t; 로 토큰 참조
145
+ - @use '../mixins' as m; 로 mixin 참조
146
+ - 해당 섹션의 클래스만 정의 (다른 섹션 스타일 금지)
147
+ - 역할 기반 클래스 네이밍 (Phase 4-4 규칙 적용)
148
+ - 배경 이미지는 별도 클래스 (Multi-Layer 패턴)
149
+
150
+ _tokens.scss 내용:
151
+ $feature-primary: #xxx;
152
+ $feature-text: #xxx;
153
+ $feature-bp: 1024px;
154
+ @function fluid($min, $max) { ... }
155
+ // 자체 완결 — 외부 의존 없음
156
+
157
+ 이 폴더째 다른 프로젝트에 복사 가능.
158
+ ```
159
+
160
+ ### 컴포넌트 ↔ 스타일 파일 매핑 규칙
161
+
162
+ ```
163
+ 두 종류의 스타일 파일이 필요:
164
+
165
+ 1. 섹션 스타일 — 페이지의 각 영역
166
+ | 컴포넌트 | --new 모드 | 기본 모드 |
167
+ |---------|-----------|---------|
168
+ | HeroSection.vue | _hero.scss | 기존 패턴 따름 |
169
+ | DailyCheckIn.vue | _daily-checkin.scss | 기존 패턴 따름 |
170
+ | CautionSection.vue | _caution.scss | 기존 패턴 따름 |
171
+
172
+ 2. 재사용 컴포넌트 스타일 — 여러 섹션에서 공통 사용
173
+ | 패턴 | --new 모드 | 용도 |
174
+ |------|-----------|------|
175
+ | 카드 (보상, 상품, 아이템) | _card.scss | 그리드 아이템, 리스트 아이템 |
176
+ | 버튼 (CTA, 출석, 교환) | _button.scss | 액션 트리거 |
177
+ | 배지 (상태 표시) | _badge.scss | 완료/진행중/잠금 표시 |
178
+ | 프로그레스 | _progress.scss | 게이지, 달성도 |
179
+ | 팝업/모달 | _popups.scss | 확인, 상세, 입력 |
180
+
181
+ 재사용 컴포넌트 감지 방법:
182
+ → 디자인 프레임에서 2회 이상 반복되는 시각 패턴
183
+ → 같은 구조 + 다른 데이터 → 하나의 스타일 파일 + variant
184
+
185
+ vibe-figma-frame에서 프레임별 추출 시:
186
+ → 섹션 스타일 파일에 해당 섹션 스타일 작성
187
+ → 반복 패턴 발견 시 재사용 컴포넌트 스타일 파일로 분리
188
+ → 토큰에 새 값 추가 (중복 시 기존 토큰 재사용)
189
+ ```
190
+
191
+ ### 4-2. Token File Format
192
+
193
+ **CSS Custom Properties (default):**
194
+
195
+ ```css
196
+ /* figma-tokens.css — Auto-generated from Figma. Do not edit manually. */
197
+ /* Source: https://www.figma.com/design/{fileKey} */
198
+
199
+ :root {
200
+ /* Colors */
201
+ --figma-primary: #3B82F6;
202
+ --figma-primary-hover: #2563EB;
203
+ --figma-surface: #FFFFFF;
204
+ --figma-surface-secondary: #F9FAFB;
205
+ --figma-text-primary: #111827;
206
+ --figma-text-secondary: #6B7280;
207
+ --figma-border: #E5E7EB;
208
+
209
+ /* Typography */
210
+ --figma-font-family: 'Inter', system-ui, sans-serif;
211
+ --figma-text-xs: 0.75rem; /* 12px */
212
+ --figma-text-sm: 0.875rem; /* 14px */
213
+ --figma-text-base: 1rem; /* 16px */
214
+ --figma-text-lg: 1.125rem; /* 18px */
215
+ --figma-text-xl: 1.25rem; /* 20px */
216
+ --figma-leading-tight: 1.25;
217
+ --figma-leading-normal: 1.5;
218
+
219
+ /* Spacing */
220
+ --figma-space-1: 0.25rem; /* 4px */
221
+ --figma-space-2: 0.5rem; /* 8px */
222
+ --figma-space-3: 0.75rem; /* 12px */
223
+ --figma-space-4: 1rem; /* 16px */
224
+ --figma-space-6: 1.5rem; /* 24px */
225
+ --figma-space-8: 2rem; /* 32px */
226
+
227
+ /* Shadows */
228
+ --figma-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
229
+ --figma-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
230
+
231
+ /* Border Radius */
232
+ --figma-radius-sm: 0.25rem; /* 4px */
233
+ --figma-radius-md: 0.5rem; /* 8px */
234
+ --figma-radius-lg: 0.75rem; /* 12px */
235
+ --figma-radius-full: 9999px;
236
+ }
237
+ ```
238
+
239
+ **Tailwind extend (if Tailwind detected):**
240
+
241
+ ```js
242
+ // figma.config.ts — merge into tailwind.config.ts theme.extend
243
+ export const figmaTokens = {
244
+ colors: {
245
+ figma: {
246
+ primary: '#3B82F6',
247
+ 'primary-hover': '#2563EB',
248
+ // ...
249
+ },
250
+ },
251
+ spacing: { /* ... */ },
252
+ borderRadius: { /* ... */ },
253
+ };
254
+ ```
255
+
256
+ **SCSS (if `*.scss` or `sass` detected):**
257
+
258
+ ```scss
259
+ // _figma-tokens.scss — Auto-generated from Figma. Do not edit manually.
260
+
261
+ // ── Variables ──
262
+ $figma-primary: #3B82F6;
263
+ $figma-primary-hover: #2563EB;
264
+ $figma-surface: #FFFFFF;
265
+ $figma-text-primary: #111827;
266
+ $figma-text-secondary: #6B7280;
267
+ $figma-border: #E5E7EB;
268
+
269
+ $figma-font-family: 'Inter', system-ui, sans-serif;
270
+ $figma-text-xs: 0.75rem;
271
+ $figma-text-sm: 0.875rem;
272
+ $figma-text-base: 1rem;
273
+ $figma-text-lg: 1.125rem;
274
+ $figma-text-xl: 1.25rem;
275
+
276
+ $figma-space-1: 0.25rem;
277
+ $figma-space-2: 0.5rem;
278
+ $figma-space-4: 1rem;
279
+ $figma-space-6: 1.5rem;
280
+ $figma-space-8: 2rem;
281
+
282
+ $figma-radius-sm: 0.25rem;
283
+ $figma-radius-md: 0.5rem;
284
+ $figma-radius-lg: 0.75rem;
285
+
286
+ $figma-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
287
+ $figma-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
288
+
289
+ // ── Breakpoints ──
290
+ $figma-bp: 1024px;
291
+ $figma-bp-mobile-min: 360px;
292
+ $figma-bp-pc-target: 1920px;
293
+
294
+ // ── Mixins ──
295
+ @mixin figma-pc {
296
+ @media (min-width: $figma-bp) { @content; }
297
+ }
298
+
299
+ @mixin figma-mobile-only {
300
+ @media (max-width: $figma-bp - 1px) { @content; }
301
+ }
302
+
303
+ // ── Functions ──
304
+ @function figma-fluid($mobile, $desktop, $min-vw: $figma-bp-mobile-min, $max-vw: $figma-bp-pc-target) {
305
+ $slope: ($desktop - $mobile) / ($max-vw - $min-vw);
306
+ $intercept: $mobile - $slope * $min-vw;
307
+ @return clamp(#{$mobile}, #{$intercept} + #{$slope * 100}vw, #{$desktop});
308
+ }
309
+
310
+ // Usage: font-size: figma-fluid(1rem, 2rem);
311
+ ```
312
+
313
+ **SCSS 사용 시 추가 규칙:**
314
+ - CSS custom properties 대신 `$변수` 사용 (프로젝트 컨벤션에 따라 둘 다 가능)
315
+ - `@mixin figma-pc` 로 breakpoint 일관성 유지 — `@media` 직접 사용 금지
316
+ - `figma-fluid()` 함수로 clamp() 계산 자동화 — 수동 계산 금지
317
+ - 파일명: `_figma-tokens.scss` (partial, `_` prefix)
318
+ - `@use 'figma-tokens' as figma;` 로 네임스페이스 import
319
+
320
+ ### 4-3. Responsive Token Format (responsive mode only)
321
+
322
+ When `responsive.json` exists, tokens that **differ across viewports** use `clamp()` for fluid scaling.
323
+ Tokens that are **identical** across viewports remain static.
324
+
325
+ **clamp() range uses breakpoints from Phase 3-3:**
326
+
327
+ ```
328
+ minVw = mobileMinimum (default: 360px)
329
+ maxVw = pcTarget (default: 1920px)
330
+ breakpoint = breakpoint (default: 1024px) ← used for @media
331
+
332
+ Design values must be scaled before clamp:
333
+ PC Figma value × (pcTarget / designPc) = target PC value
334
+ Mobile Figma value × (mobilePortrait / designMobile) = target mobile value
335
+ ```
336
+
337
+ **CSS Custom Properties (responsive):**
338
+
339
+ ```css
340
+ /* figma-tokens.css — Responsive tokens from Figma */
341
+ /* clamp range: {mobileMinimum}px → {pcTarget}px */
342
+ /* Breakpoint: {breakpoint}px (PC↔Mobile) */
343
+ /* Design scale: PC {designPc}→{pcTarget}, Mobile {designMobile}→{mobilePortrait} */
344
+
345
+ :root {
346
+ /* === Shared (same across all viewports) === */
347
+ --figma-primary: #3B82F6;
348
+ --figma-font-family: 'Inter', system-ui, sans-serif;
349
+ --figma-radius-md: 0.5rem;
350
+ --figma-shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
351
+
352
+ /* === Fluid Typography (scales with viewport) === */
353
+ /* Figma PC 96px → target 36px, Figma Mobile 48px → target 32px */
354
+ --figma-text-h1: clamp(2rem, {intercept}rem + {slope}vw, 2.25rem);
355
+ --figma-text-body: clamp(0.875rem, {intercept}rem + {slope}vw, 1rem);
356
+
357
+ /* === Fluid Spacing (scales with viewport) === */
358
+ --figma-space-section: clamp(1rem, {intercept}rem + {slope}vw, 3rem);
359
+ --figma-space-content: clamp(0.75rem, {intercept}rem + {slope}vw, 1.5rem);
360
+
361
+ /* === Breakpoint (from config, user-customizable) === */
362
+ --figma-bp: 1024px;
363
+ }
364
+ ```
365
+
366
+ **clamp() calculation formula:**
367
+
368
+ ```
369
+ Step 1: Scale Figma values to target viewport
370
+ targetMobile = figmaMobileValue × (mobilePortrait / designMobile)
371
+ targetPc = figmaPcValue × (pcTarget / designPc)
372
+
373
+ Example (defaults): Figma PC h1=96px, Figma Mobile h1=48px
374
+ targetPc = 96 × (1920 / 2560) = 72px
375
+ targetMobile = 48 × (480 / 720) = 32px
376
+
377
+ Step 2: Calculate clamp()
378
+ minVw = mobileMinimum (360)
379
+ maxVw = pcTarget (1920)
380
+ min = targetMobile, max = targetPc
381
+
382
+ slope = (max - min) / (maxVw - minVw)
383
+ intercept = min - slope * minVw
384
+ → clamp({min/16}rem, {intercept/16}rem + {slope*100}vw, {max/16}rem)
385
+
386
+ Example:
387
+ slope = (72 - 32) / (1920 - 360) = 0.02564
388
+ intercept = 32 - 0.02564 × 360 = 22.77
389
+ → clamp(2rem, 1.423rem + 2.564vw, 4.5rem)
390
+ ```
391
+
392
+ **Tailwind (responsive — if Tailwind detected):**
393
+
394
+ Use Tailwind's responsive prefixes instead of clamp() for layout, clamp() for typography/spacing:
395
+
396
+ ```js
397
+ export const figmaTokens = {
398
+ fontSize: {
399
+ 'figma-h1': ['clamp(1.5rem, 1.076rem + 1.878vw, 3rem)', { lineHeight: '1.2' }],
400
+ 'figma-body': ['clamp(0.875rem, 0.828rem + 0.188vw, 1rem)', { lineHeight: '1.5' }],
401
+ },
402
+ spacing: {
403
+ 'figma-section': 'clamp(1rem, 0.248rem + 3.286vw, 3rem)',
404
+ },
405
+ };
406
+ ```
407
+
408
+ **SCSS (responsive):**
409
+
410
+ ```scss
411
+ // _figma-tokens.scss — figma-fluid() 함수로 자동 계산
412
+ @use 'sass:math';
413
+
414
+ $figma-bp: 1024px;
415
+ $figma-bp-mobile-min: 360px;
416
+ $figma-bp-pc-target: 1920px;
417
+
418
+ @function figma-fluid($mobile, $desktop, $min-vw: $figma-bp-mobile-min, $max-vw: $figma-bp-pc-target) {
419
+ $slope: math.div($desktop - $mobile, $max-vw - $min-vw);
420
+ $intercept: $mobile - $slope * $min-vw;
421
+ @return clamp(#{$mobile}, #{$intercept} + #{$slope * 100}vw, #{$desktop});
422
+ }
423
+
424
+ @mixin figma-pc { @media (min-width: $figma-bp) { @content; } }
425
+
426
+ // Token 사용
427
+ $figma-text-h1: figma-fluid(2rem, 4.5rem);
428
+ $figma-text-body: figma-fluid(0.875rem, 1rem);
429
+ $figma-space-section: figma-fluid(1rem, 3rem);
430
+ ```
431
+
432
+ ```scss
433
+ // Component.module.scss — 사용 예시
434
+ @use 'figma-tokens' as figma;
435
+
436
+ .heroSection {
437
+ padding: figma.$figma-space-section;
438
+ }
439
+
440
+ .heroTitle {
441
+ font-size: figma.$figma-text-h1;
442
+ }
443
+
444
+ .cardGrid {
445
+ display: grid;
446
+ grid-template-columns: 1fr;
447
+
448
+ @include figma.figma-pc {
449
+ grid-template-columns: repeat(3, 1fr);
450
+ }
451
+ }
452
+ ```
453
+
454
+ ### 4-4. Class Naming Rules
455
+
456
+ 클래스 이름은 **역할(role)**을 드러내야 하며, 구조나 스타일 속성을 이름에 넣지 않는다.
457
+
458
+ #### 네이밍 원칙
459
+
460
+ | 원칙 | 좋은 예 | 나쁜 예 |
461
+ |------|--------|--------|
462
+ | **역할 기반** | `.heroSection`, `.productCard`, `.navPrimary` | `.section1`, `.card`, `.nav` |
463
+ | **용도 명시** | `.heroBg`, `.cardThumbnail`, `.avatarImg` | `.bg`, `.img`, `.image1` |
464
+ | **상태 포함** | `.buttonPrimary`, `.inputError` | `.blueButton`, `.redBorder` |
465
+ | **관계 표현** | `.heroTitle`, `.heroDescription` | `.title`, `.text` |
466
+ | **축약 금지** | `.navigationMenu`, `.backgroundImage` | `.navMnu`, `.bgImg` |
467
+
468
+ #### 구체적 규칙
469
+
470
+ ```
471
+ 1. 컴포넌트 루트: 섹션/컴포넌트 이름 그대로
472
+ .loginForm, .heroSection, .productGrid
473
+
474
+ 2. 자식 요소: 부모이름 + 역할
475
+ .heroTitle, .heroDescription, .heroCta
476
+ .loginFormInput, .loginFormSubmit
477
+
478
+ 3. 이미지 클래스: 반드시 용도를 명시
479
+ .heroBg ← 히어로 배경 이미지
480
+ .heroBgOverlay ← 배경 위 오버레이
481
+ .productPhoto ← 상품 사진
482
+ .brandLogo ← 브랜드 로고
483
+
484
+ 4. 상태 변형: variant/state 접미사
485
+ .buttonPrimary, .buttonDisabled
486
+ .cardHighlight, .cardCompact
487
+ ```
488
+
489
+ #### Anti-Patterns
490
+
491
+ ```css
492
+ /* WRONG: 의미 없는 이름 */
493
+ .wrapper { }
494
+ .inner { }
495
+ .box { }
496
+ .item { }
497
+ .text1 { }
498
+
499
+ /* CORRECT: 역할이 드러나는 이름 */
500
+ .eventSection { }
501
+ .eventContent { }
502
+ .rewardCard { }
503
+ .rewardItem { }
504
+ .eventDescription { }
505
+ ```
506
+
507
+ **Component style file MUST reference global tokens:**
508
+
509
+ ```css
510
+ /* LoginForm.module.css */
511
+ .loginForm {
512
+ padding: var(--figma-space-6);
513
+ background: var(--figma-surface);
514
+ border-radius: var(--figma-radius-lg);
515
+ box-shadow: var(--figma-shadow-md);
516
+ }
517
+
518
+ .loginFormTitle {
519
+ font-size: var(--figma-text-xl);
520
+ font-weight: 600;
521
+ color: var(--figma-text-primary);
522
+ line-height: var(--figma-leading-tight);
523
+ }
524
+
525
+ .loginFormSubmit {
526
+ background: var(--figma-primary);
527
+ color: var(--figma-surface);
528
+ border-radius: var(--figma-radius-md);
529
+ padding: var(--figma-space-2) var(--figma-space-4);
530
+ transition: background 150ms ease;
531
+ }
532
+
533
+ .loginFormSubmit:hover {
534
+ background: var(--figma-primary-hover);
535
+ }
536
+ ```
537
+
538
+ ---
539
+
540
+ ## Phase 7: Token Mapping (default mode)
541
+
542
+ **Only in default (project integration) mode.** Map extracted Figma tokens to the project's existing token system.
543
+
544
+ ### Token Source Priority
545
+
546
+ ```
547
+ 1. MASTER.md (.claude/vibe/design-system/{project}/MASTER.md) ← 최우선
548
+ 2. design-context.json (.claude/vibe/design-context.json) ← 보조
549
+ 3. Project theme files (tailwind.config, CSS variables, etc.) ← 폴백
550
+ 4. Generate new figma-tokens ← 마지막 수단
551
+ ```
552
+
553
+ ### Mapping Rules
554
+
555
+ 1. **MASTER.md exists** → authoritative token source
556
+ - Figma color ≈ MASTER.md color (ΔE < 5) → use MASTER.md token name
557
+ - Figma spacing ≈ MASTER.md spacing (±2px) → use MASTER.md token name
558
+ - Figma font ≈ MASTER.md font → use MASTER.md token name
559
+ - Unmatched Figma values → add to `figma-tokens.css` as supplementary tokens
560
+
561
+ 2. **No MASTER.md, but design-context.json exists** →
562
+ - Use `detectedStack` info for naming convention
563
+ - Use `aesthetic.colorMood` to validate token naming (e.g., warm palette → warm- prefix)
564
+ - Generate `figma-tokens.css` grouped by category
565
+
566
+ 3. **No design system at all** →
567
+ - Generate `figma-tokens.css` (or Tailwind extend)
568
+ - Group tokens by category (color, typography, spacing, shadow)
569
+
570
+ ### Output Mapping Comment
571
+
572
+ Always output token mapping as a comment block at the top of the token file:
573
+
574
+ ```
575
+ /* Figma Token Mapping:
576
+ * Figma "Primary/Default" → var(--figma-primary) = #3B82F6
577
+ * ✅ Matched: var(--color-blue-500) from MASTER.md
578
+ * Figma "Text/Body" → var(--figma-text-base) = 1rem / 1.5
579
+ * ✅ Matched: var(--text-base) from MASTER.md
580
+ * Figma "Accent/Glow" → var(--figma-accent-glow) = #7C3AED
581
+ * ⚠️ New token: no existing match
582
+ */
583
+ ```