@su-record/vibe 2.8.19 → 2.8.21
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 +279 -62
- package/skills/vibe-figma-analyze/SKILL.md +4 -227
- package/skills/vibe-figma-codegen/SKILL.md +3 -189
- package/skills/vibe-figma-consolidate/SKILL.md +3 -93
- package/skills/vibe-figma-convert/SKILL.md +284 -0
- package/skills/vibe-figma-extract/SKILL.md +150 -0
- package/skills/vibe-figma-frame/SKILL.md +4 -528
- package/skills/vibe-figma-rules/SKILL.md +3 -456
- package/skills/vibe-figma-style/SKILL.md +3 -207
|
@@ -1,196 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vibe-figma-codegen
|
|
3
|
-
description:
|
|
3
|
+
description: "[Merged] → vibe-figma-convert 참조"
|
|
4
4
|
triggers: []
|
|
5
5
|
tier: standard
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# vibe-figma-codegen
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
이미지 패턴은 **vibe-figma-rules R-4** + **vibe-figma-frame B-3.4** 참조.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## C-1. Semantic HTML (필수)
|
|
16
|
-
|
|
17
|
-
모든 요소는 가장 구체적인 semantic 태그를 사용. `<div>`는 최후 수단.
|
|
18
|
-
|
|
19
|
-
| 시각 요소 | 올바른 태그 | 잘못된 태그 |
|
|
20
|
-
|----------|-----------|-----------|
|
|
21
|
-
| 페이지 섹션 | `<section>`, `<article>`, `<aside>` | `<div>` |
|
|
22
|
-
| 네비게이션 | `<nav>` | `<div class="nav">` |
|
|
23
|
-
| 헤더/푸터 | `<header>`, `<footer>` | `<div class="header">` |
|
|
24
|
-
| 제목 계층 | `<h1>`→`<h6>` (순차, 건너뛰기 금지) | `<div class="title">` |
|
|
25
|
-
| 텍스트 | `<p>` | `<div>` or `<span>` |
|
|
26
|
-
| 리스트 | `<ul>`/`<ol>` + `<li>` | `<div>` 반복 |
|
|
27
|
-
| 액션 버튼 | `<button>` | `<div onClick>` |
|
|
28
|
-
| 링크 | `<a href>` | `<span onClick>` |
|
|
29
|
-
| 폼 필드 | `<input>` + `<label>` | `<div contenteditable>` |
|
|
30
|
-
| 이미지 | `<img alt="설명">` | content 이미지에 `background-image` |
|
|
31
|
-
| 시간 | `<time datetime>` | `<span>` |
|
|
32
|
-
|
|
33
|
-
## C-2. Accessibility Checklist
|
|
34
|
-
|
|
35
|
-
```
|
|
36
|
-
- [ ] 모든 인터랙티브 요소 키보드 접근 가능
|
|
37
|
-
- [ ] <button> = 액션, <a> = 네비게이션 (역할 혼용 금지)
|
|
38
|
-
- [ ] <img> alt 설명적 (장식은 alt="" + aria-hidden)
|
|
39
|
-
- [ ] <input> ↔ <label> 연결
|
|
40
|
-
- [ ] 색상 대비 >= 4.5:1 (텍스트), >= 3:1 (큰 텍스트, UI 컨트롤)
|
|
41
|
-
- [ ] 포커스 인디케이터 표시
|
|
42
|
-
- [ ] 아이콘 전용 버튼에 aria-label
|
|
43
|
-
- [ ] 제목 계층 순차적
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## C-3. 래퍼 제거 + 컴포넌트 통합
|
|
47
|
-
|
|
48
|
-
래퍼 제거 및 80% Rule은 **vibe-figma-rules R-5** 참조.
|
|
49
|
-
|
|
50
|
-
### 컴포넌트 구조 제한
|
|
51
|
-
|
|
52
|
-
```
|
|
53
|
-
Max nesting depth: 3 levels
|
|
54
|
-
Max component length: 50 lines
|
|
55
|
-
Max props: 5 per component
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### 분리 트리거
|
|
59
|
-
|
|
60
|
-
| 신호 | 조치 |
|
|
61
|
-
|------|------|
|
|
62
|
-
| 컴포넌트 > 50줄 | 서브 컴포넌트로 분리 |
|
|
63
|
-
| 시각 패턴 2회+ 반복 | 공유 컴포넌트 추출 |
|
|
64
|
-
| 유사도 80%+ | 단일 컴포넌트 + variant prop (R-5) |
|
|
65
|
-
| 명확한 시각 경계 (카드, 모달, 폼) | 자체 컴포넌트 + 스타일 파일 |
|
|
66
|
-
|
|
67
|
-
## C-4. 스토리보드 스펙 + Design Context 적용
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
storyboardSpec → 코드:
|
|
71
|
-
interactions → CSS :hover/:active/:focus + JS 핸들러
|
|
72
|
-
animations → transition, @keyframes (타이밍/이징 스펙대로)
|
|
73
|
-
states → 조건부 렌더링 + 상태별 UI
|
|
74
|
-
colorGuide / typographyGuide → 토큰 검증
|
|
75
|
-
|
|
76
|
-
design-context.json → 코드:
|
|
77
|
-
accessibility = "AAA" → aria-describedby, prefers-reduced-motion
|
|
78
|
-
devices = ["mobile"] → 모바일 전용, touch target >= 44px
|
|
79
|
-
aesthetic.style = "minimal" → 적은 그림자, 얇은 보더
|
|
80
|
-
aesthetic.style = "bold" → 강한 그림자, 두꺼운 보더
|
|
81
|
-
brand.personality → 브랜드 고유 패턴 보존
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## C-5. 스택별 코드 생성 규칙
|
|
85
|
-
|
|
86
|
-
### React / Next.js + TypeScript
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
- 함수형 컴포넌트, 명시적 return type
|
|
90
|
-
- Props interface 정의, named exports
|
|
91
|
-
- <></> Fragment
|
|
92
|
-
- CSS Modules 또는 Tailwind
|
|
93
|
-
- Next.js: 'use client' 필요 시만, Image/Link 컴포넌트 사용
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Vue 3 / Nuxt
|
|
97
|
-
|
|
98
|
-
```
|
|
99
|
-
- <script setup lang="ts"> composition API
|
|
100
|
-
- defineProps + TypeScript interface
|
|
101
|
-
- <template>로 invisible 그룹핑
|
|
102
|
-
- <style scoped> or <style module>
|
|
103
|
-
- <Teleport> for 모달/오버레이
|
|
104
|
-
- Nuxt: <NuxtLink>, <NuxtImg> 사용
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Svelte
|
|
108
|
-
|
|
109
|
-
```
|
|
110
|
-
- <script lang="ts">
|
|
111
|
-
- export let + 타입
|
|
112
|
-
- {#if}/{#each}/{#await} 블록
|
|
113
|
-
- <slot>, transition: directive
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### SCSS (공통)
|
|
117
|
-
|
|
118
|
-
```
|
|
119
|
-
- @use 'figma-tokens' as figma — 네임스페이스 import
|
|
120
|
-
- @include figma.figma-pc { } — @media 직접 사용 금지
|
|
121
|
-
- figma-fluid($mobile, $desktop) — 수동 clamp() 금지
|
|
122
|
-
- Nesting max 3 levels
|
|
123
|
-
- & for BEM-like modifiers
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
## C-6. 반응형 코드 생성
|
|
127
|
-
|
|
128
|
-
### 원칙: Fluid First, Breakpoint Second
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
1. Typography & Spacing → clamp() fluid 토큰 (브레이크포인트 불필요)
|
|
132
|
-
2. 레이아웃 방향 변경 → @media at breakpoint
|
|
133
|
-
3. 가시성 토글 → @media display toggle
|
|
134
|
-
4. 컴포넌트 교체 → 조건부 렌더링 또는 CSS
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Anti-Patterns (금지)
|
|
138
|
-
|
|
139
|
-
| 잘못됨 | 올바름 |
|
|
140
|
-
|--------|--------|
|
|
141
|
-
| 모바일/데스크탑 별도 컴포넌트 파일 | 단일 컴포넌트 + 반응형 CSS |
|
|
142
|
-
| font-size에 @media | clamp() fluid 토큰 |
|
|
143
|
-
| 하드코딩 `@media (min-width: 768px)` | 프로젝트 브레이크포인트 사용 |
|
|
144
|
-
| px 값 직접 사용 | 토큰 변수 사용 |
|
|
145
|
-
| window.innerWidth JS 체크 | CSS-only 반응형 |
|
|
146
|
-
|
|
147
|
-
## C-7. Correction Notes
|
|
148
|
-
|
|
149
|
-
코드 생성 후 출력:
|
|
150
|
-
|
|
151
|
-
```markdown
|
|
152
|
-
### Generation Mode
|
|
153
|
-
- Mode: default / --new / responsive
|
|
154
|
-
- Output directory: {path}
|
|
155
|
-
|
|
156
|
-
### Files Generated
|
|
157
|
-
| File | Type | Description |
|
|
158
|
-
|
|
159
|
-
### Responsive Summary (responsive mode only)
|
|
160
|
-
| Token | Mobile | Desktop | Strategy |
|
|
161
|
-
|
|
162
|
-
### Markup Quality
|
|
163
|
-
- Semantic tags: {list}
|
|
164
|
-
- Accessibility: {pass/fail}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## C-8. Refine Mode (`--refine`)
|
|
168
|
-
|
|
169
|
-
이전 생성 코드의 완성도가 부족할 때 사용. **새로 만들지 않고 기존 코드를 수정만.**
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
1. URL 재입력 (또는 "이전과 동일")
|
|
173
|
-
2. 기존 코드 스캔 (pages/, components/ 내 피처 폴더)
|
|
174
|
-
3. Figma 원본 재추출 (get_design_context + get_screenshot)
|
|
175
|
-
4. Side-by-side 비교 → Diff 기반 최소 수정
|
|
176
|
-
5. 검증 루프 (vibe-figma-rules R-6, P1=0 될 때까지)
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### Refine 수정 범위
|
|
180
|
-
|
|
181
|
-
| 수정함 | 수정 안 함 |
|
|
182
|
-
|--------|-----------|
|
|
183
|
-
| 누락 에셋, 레이아웃/타이포/색상 불일치 | 파일 구조 변경 |
|
|
184
|
-
| 누락 컴포넌트, 인터랙션 누락 | 컴포넌트 분리/통합 |
|
|
185
|
-
| 반응형 누락, 이미지 경로 | 토큰 파일 재생성 |
|
|
186
|
-
|
|
187
|
-
## Important
|
|
188
|
-
|
|
189
|
-
```
|
|
190
|
-
- Never guess colors — 추출 값만 사용
|
|
191
|
-
- Never invent spacing — 토큰 스케일에 매핑
|
|
192
|
-
- Never hardcode values — 토큰 변수 참조
|
|
193
|
-
- Image is truth — 레이어 구조 혼란 시 이미지 우선
|
|
194
|
-
- No console.log, No div soup
|
|
195
|
-
- Component 50줄 초과 시 분리
|
|
196
|
-
```
|
|
10
|
+
이 스킬은 **vibe-figma-convert**으로 병합되었습니다.
|
|
@@ -1,100 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vibe-figma-consolidate
|
|
3
|
-
description:
|
|
3
|
+
description: "[Merged] → vibe-figma (Phase 4: Verification) 참조"
|
|
4
4
|
triggers: []
|
|
5
5
|
tier: standard
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# vibe-figma-consolidate
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
## D-1. 스타일 공통화
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
1. 모바일/PC에서 동일한 값 → 공통 토큰으로 추출
|
|
16
|
-
2. 중복 CSS/SCSS 규칙 통합
|
|
17
|
-
3. 컴포넌트 내 중복 로직 제거
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
| 유형 | 처리 방법 |
|
|
21
|
-
|------|----------|
|
|
22
|
-
| 색상 (동일) | 공통 CSS custom property |
|
|
23
|
-
| 타이포 (동일 scale) | 공통 토큰 유지 |
|
|
24
|
-
| 간격 (다름) | clamp() fluid 토큰 (계산: vibe-figma-rules R-3) |
|
|
25
|
-
| 레이아웃 방향 (다름) | @media 분기 유지 |
|
|
26
|
-
| 컴포넌트 구조 (동일) | 하나로 통합 |
|
|
27
|
-
|
|
28
|
-
## D-2. 컴포넌트 통합
|
|
29
|
-
|
|
30
|
-
80% Rule 및 variant 통합 기준은 **vibe-figma-rules R-5** 참조.
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
1. 유사 컴포넌트 (80%+) → variant prop 통합
|
|
34
|
-
2. 중복 sub-component → 공유 컴포넌트 추출
|
|
35
|
-
3. 불필요한 래퍼 → Fragment/template 제거
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## D-3. 최종 검증
|
|
39
|
-
|
|
40
|
-
검증 공통 프로세스는 **vibe-figma-rules R-6** 참조.
|
|
41
|
-
|
|
42
|
-
### Step D 추가 검증
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
양쪽 뷰포트 동시 검증:
|
|
46
|
-
- 모바일 Figma vs 코드 (mobile viewport)
|
|
47
|
-
- PC Figma vs 코드 (desktop viewport)
|
|
48
|
-
- 양쪽 모두 P1=0, Match Score 95%+
|
|
49
|
-
- 이미지 에셋 전부 정상 표시
|
|
50
|
-
- 공통 토큰으로 중복 제거 완료
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Model Routing (Step D)
|
|
54
|
-
|
|
55
|
-
| 작업 | 모델 |
|
|
56
|
-
|------|------|
|
|
57
|
-
| 공통화 리팩토링 + 최종 검증 | **Sonnet** |
|
|
58
|
-
| Post — 코드 리뷰 | **gpt-5.3-codex** (Codex 미설치 시 스킵) |
|
|
59
|
-
|
|
60
|
-
## D-4. Design Quality Pipeline (후처리)
|
|
61
|
-
|
|
62
|
-
Step D 완료 후 사용자에게 다음 단계를 제시.
|
|
63
|
-
|
|
64
|
-
### Pre-requisite check
|
|
65
|
-
|
|
66
|
-
`.claude/vibe/design-context.json`이 없으면:
|
|
67
|
-
```
|
|
68
|
-
디자인 컨텍스트가 없습니다. /design-teach 를 먼저 실행하면
|
|
69
|
-
브랜드, 접근성, 타겟 디바이스에 맞춘 더 정확한 코드를 생성할 수 있습니다.
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Quick (기본 추천)
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
/design-normalize → /design-audit
|
|
76
|
-
```
|
|
77
|
-
- Normalize: 하드코딩 값 → MASTER.md 토큰으로 치환
|
|
78
|
-
- Audit: A11Y + 성능 + 반응형 + AI Slop 검출
|
|
79
|
-
|
|
80
|
-
### Thorough (프로덕션 추천)
|
|
81
|
-
|
|
82
|
-
```
|
|
83
|
-
/design-normalize → /design-audit → /design-critique → /design-polish
|
|
84
|
-
```
|
|
85
|
-
- + Critique: Nielsen 10 휴리스틱 + 페르소나 분석
|
|
86
|
-
- + Polish: 인터랙션 상태 보완 (hover/focus/loading/error)
|
|
87
|
-
|
|
88
|
-
### 출력 포맷
|
|
89
|
-
|
|
90
|
-
```markdown
|
|
91
|
-
## Design Quality Pipeline
|
|
92
|
-
|
|
93
|
-
생성된 파일: {file list}
|
|
94
|
-
|
|
95
|
-
추천 다음 단계:
|
|
96
|
-
1. /design-normalize — 토큰 정렬
|
|
97
|
-
2. /design-audit — 기술 품질 검사
|
|
98
|
-
3. /design-critique — UX 휴리스틱 리뷰
|
|
99
|
-
4. /design-polish — 최종 인터랙션 보완
|
|
100
|
-
```
|
|
10
|
+
이 스킬은 **vibe-figma** (Phase 4: Verification)으로 병합되었습니다.
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vibe-figma-convert
|
|
3
|
+
description: 참조 코드(React+Tailwind) → 프로젝트 코드 + 외부 스타일 파일
|
|
4
|
+
triggers: []
|
|
5
|
+
tier: standard
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# vibe-figma-convert — 코드 변환
|
|
9
|
+
|
|
10
|
+
`get_design_context` 참조 코드를 프로젝트 스택 코드 + 외부 SCSS 파일로 변환.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. 외부 스타일 파일 생성
|
|
15
|
+
|
|
16
|
+
### 변환 예시
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
참조 코드 (React+Tailwind):
|
|
20
|
+
<div className="h-[1280px] relative w-[720px]"> ← 섹션 컨테이너
|
|
21
|
+
<div className="absolute h-[1280px] overflow-clip w-[720px]"> ← BG 레이어
|
|
22
|
+
<img src={img21} className="absolute inset-0 object-cover size-full" />
|
|
23
|
+
</div>
|
|
24
|
+
<div className="absolute flex flex-col items-center top-[130px]"> ← Content
|
|
25
|
+
<div className="h-[174px] w-[620px]"> ← Title 이미지
|
|
26
|
+
<img src={imgTitle} className="absolute inset-0 object-cover size-full" />
|
|
27
|
+
</div>
|
|
28
|
+
<p className="text-[24px] text-white leading-[1.4] text-center"> ← 텍스트
|
|
29
|
+
참여 대상 : PC 유저 (Steam, Kakao)
|
|
30
|
+
</p>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
변환 → styles/{feature}/layout/_hero.scss:
|
|
37
|
+
|
|
38
|
+
@use '../tokens' as t;
|
|
39
|
+
|
|
40
|
+
.heroSection {
|
|
41
|
+
position: relative;
|
|
42
|
+
height: 960px; // 1280 × 0.75
|
|
43
|
+
width: 100%;
|
|
44
|
+
overflow: clip;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.heroBg {
|
|
48
|
+
position: absolute;
|
|
49
|
+
inset: 0;
|
|
50
|
+
z-index: 0;
|
|
51
|
+
img { width: 100%; height: 100%; object-fit: cover; }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.heroContent {
|
|
55
|
+
position: relative;
|
|
56
|
+
z-index: 10;
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
align-items: center;
|
|
60
|
+
padding-top: 98px; // 130 × 0.75
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
변환 → styles/{feature}/components/_hero.scss:
|
|
66
|
+
|
|
67
|
+
@use '../tokens' as t;
|
|
68
|
+
|
|
69
|
+
.heroTitle {
|
|
70
|
+
width: 465px; // 620 × 0.75
|
|
71
|
+
height: 131px; // 174 × 0.75
|
|
72
|
+
img { width: 100%; height: 100%; object-fit: cover; }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.heroParticipation {
|
|
76
|
+
font-size: t.$text-sub; // 24px × 0.75 = 18px
|
|
77
|
+
color: t.$color-white;
|
|
78
|
+
line-height: 1.4;
|
|
79
|
+
text-align: center;
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### layout vs components 구분
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
layout/ → position, display, flex/grid, width, height, padding, margin,
|
|
88
|
+
gap, overflow, z-index, background-image, inset
|
|
89
|
+
components/ → font-size, font-weight, color, line-height, letter-spacing,
|
|
90
|
+
text-align, border, border-radius, box-shadow, opacity
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### _tokens.scss 업데이트
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
섹션을 처리할 때마다 새로운 고유값을 토큰에 추가:
|
|
97
|
+
|
|
98
|
+
// Colors
|
|
99
|
+
$color-white: #FFFFFF;
|
|
100
|
+
$color-bg-dark: #0A1628;
|
|
101
|
+
$color-heading: #1B3A1D;
|
|
102
|
+
$color-text-body: #333333;
|
|
103
|
+
$color-period-label: #003879;
|
|
104
|
+
$color-grayscale-950: #171716;
|
|
105
|
+
|
|
106
|
+
// Typography (Figma px × scaleFactor)
|
|
107
|
+
$text-hero: 36px; // 48 × 0.75
|
|
108
|
+
$text-sub: 18px; // 24 × 0.75
|
|
109
|
+
$text-body: 16px; // 21 × 0.75
|
|
110
|
+
$text-period: 21px; // 28 × 0.75
|
|
111
|
+
|
|
112
|
+
// Font families
|
|
113
|
+
$font-pretendard: 'Pretendard', sans-serif;
|
|
114
|
+
$font-roboto-condensed: 'Roboto Condensed', sans-serif;
|
|
115
|
+
|
|
116
|
+
// Spacing
|
|
117
|
+
$space-section: 98px; // 130 × 0.75
|
|
118
|
+
$space-content: 18px; // 24 × 0.75
|
|
119
|
+
|
|
120
|
+
// Breakpoints
|
|
121
|
+
$bp-pc: 1024px;
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 2. 컴포넌트 파일 변환
|
|
127
|
+
|
|
128
|
+
### Vue / Nuxt
|
|
129
|
+
|
|
130
|
+
```vue
|
|
131
|
+
<template>
|
|
132
|
+
<section class="heroSection">
|
|
133
|
+
<!-- BG Layer -->
|
|
134
|
+
<div class="heroBg">
|
|
135
|
+
<img src="/images/{feature}/bg.webp" alt="" aria-hidden="true" />
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<!-- Content Layer -->
|
|
139
|
+
<div class="heroContent">
|
|
140
|
+
<div class="heroTitle">
|
|
141
|
+
<img src="/images/{feature}/title.webp" alt="추운 겨울, 따뜻한 보상이 펑펑" />
|
|
142
|
+
</div>
|
|
143
|
+
<div class="heroSubTitle">
|
|
144
|
+
<img src="/images/{feature}/sub-title.webp" alt="겨울을 녹일 보상, 지금 PC방에서 획득하세요!" />
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<!-- Period Info (참조 코드 data-name="Period") -->
|
|
148
|
+
<div class="heroPeriod">
|
|
149
|
+
<p class="heroPeriodTarget">참여 대상 : PC 유저 (Steam, Kakao)</p>
|
|
150
|
+
<div class="heroPeriodDetails">
|
|
151
|
+
<div class="heroPeriodItem">
|
|
152
|
+
<p class="heroPeriodLabel">이벤트 기간</p>
|
|
153
|
+
<p class="heroPeriodValue">{{ eventPeriod }}</p>
|
|
154
|
+
</div>
|
|
155
|
+
<div class="heroPeriodItem">
|
|
156
|
+
<p class="heroPeriodLabel">교환/응모 종료일</p>
|
|
157
|
+
<p class="heroPeriodValue">{{ exchangeDeadline }}</p>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<!-- Share Button (참조 코드 data-name="BTN_Share") -->
|
|
164
|
+
<button class="heroShareBtn" @click="handleShare">
|
|
165
|
+
<img src="/images/{feature}/share.webp" alt="공유하기" />
|
|
166
|
+
</button>
|
|
167
|
+
</section>
|
|
168
|
+
</template>
|
|
169
|
+
|
|
170
|
+
<script setup lang="ts">
|
|
171
|
+
/**
|
|
172
|
+
* Hero 섹션 — 키비주얼 + 이벤트 기간 정보
|
|
173
|
+
*
|
|
174
|
+
* [인터랙션] 공유 버튼 클릭 → 공유하기 팝업
|
|
175
|
+
* [상태] 로그인 전/후
|
|
176
|
+
*/
|
|
177
|
+
|
|
178
|
+
defineProps<{
|
|
179
|
+
eventPeriod: string
|
|
180
|
+
exchangeDeadline: string
|
|
181
|
+
}>()
|
|
182
|
+
|
|
183
|
+
const emit = defineEmits<{
|
|
184
|
+
share: []
|
|
185
|
+
}>()
|
|
186
|
+
|
|
187
|
+
function handleShare(): void {
|
|
188
|
+
emit('share')
|
|
189
|
+
}
|
|
190
|
+
</script>
|
|
191
|
+
<!-- 스타일: styles/{feature}/layout/_hero.scss + components/_hero.scss -->
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### JSX 변환 규칙 (React+Tailwind → Vue/Nuxt)
|
|
195
|
+
|
|
196
|
+
| React JSX | Vue SFC |
|
|
197
|
+
|-----------|---------|
|
|
198
|
+
| `className="..."` | `class="..."` |
|
|
199
|
+
| `{condition && <X/>}` | `<X v-if="condition" />` |
|
|
200
|
+
| `{items.map(i => <X key={i.id}/>)}` | `<X v-for="i in items" :key="i.id" />` |
|
|
201
|
+
| `onClick={handler}` | `@click="handler"` |
|
|
202
|
+
| `src={변수}` | `src="/images/{feature}/파일.webp"` (imageMap으로 교체) |
|
|
203
|
+
| `style={{ maskImage: ... }}` | `:style="{ maskImage: ... }"` |
|
|
204
|
+
| `<img alt="" />` (장식) | `<img alt="" aria-hidden="true" />` |
|
|
205
|
+
|
|
206
|
+
### JSX 변환 규칙 (React+Tailwind → React/Next.js)
|
|
207
|
+
|
|
208
|
+
| React+Tailwind | React/Next.js + SCSS |
|
|
209
|
+
|----------------|---------------------|
|
|
210
|
+
| `className="text-[48px] font-black"` | `className={styles.heroTitle}` |
|
|
211
|
+
| `<img src={변수}` | `<Image src="/images/{feature}/파일.webp"` |
|
|
212
|
+
| Tailwind 클래스 | CSS Module 또는 외부 SCSS 클래스 |
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 3. 이미지 배치 패턴
|
|
217
|
+
|
|
218
|
+
### 배경 이미지 (참조 코드에서 absolute + inset-0 + object-cover)
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
참조 코드에 이 패턴이 있으면 → 배경 이미지:
|
|
222
|
+
<div className="absolute ...">
|
|
223
|
+
<img src={imgXxx} className="absolute inset-0 object-cover size-full" />
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
→ Multi-Layer 변환:
|
|
227
|
+
.{section}Bg → position: absolute; inset: 0; z-index: 0;
|
|
228
|
+
.{section}Content → position: relative; z-index: 10;
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### 콘텐츠 이미지 (참조 코드에서 독립적 img)
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
→ <img src="/images/{feature}/파일.webp" alt="설명" width={스케일적용} height={스케일적용} />
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 장식 이미지 (참조 코드에서 mix-blend, opacity, blur)
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
→ <img ... alt="" aria-hidden="true" /> (접근성: 장식은 비표시)
|
|
241
|
+
→ CSS: mix-blend-mode, opacity, filter: blur() 등 참조 코드 값 그대로
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 4. 반응형 추가 (데스크탑 URL)
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
같은 섹션에 대해 데스크탑 참조 코드 추출 후:
|
|
250
|
+
|
|
251
|
+
같은 값 → 유지
|
|
252
|
+
다른 px 값:
|
|
253
|
+
@include pc {
|
|
254
|
+
.heroSection { height: 1440px; } // 데스크탑 스케일
|
|
255
|
+
}
|
|
256
|
+
또는 clamp(모바일, calc, 데스크탑) 사용
|
|
257
|
+
|
|
258
|
+
다른 배경 이미지:
|
|
259
|
+
.heroBg img { content: url('/images/{feature}/hero-mobile.webp'); }
|
|
260
|
+
@include pc {
|
|
261
|
+
.heroBg img { content: url('/images/{feature}/hero-pc.webp'); }
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
다른 레이아웃:
|
|
265
|
+
.heroContent { flex-direction: column; }
|
|
266
|
+
@include pc { .heroContent { flex-direction: row; } }
|
|
267
|
+
|
|
268
|
+
기존 모바일 코드 삭제 금지. @media 또는 mixin으로 추가만.
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## 5. Semantic HTML 최소 규칙
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
- 섹션 래퍼: <section>
|
|
277
|
+
- 제목: <h1>~<h6> (순차, 건너뛰기 금지)
|
|
278
|
+
- 텍스트: <p>
|
|
279
|
+
- 버튼: <button> (@click 있으면)
|
|
280
|
+
- 링크: <a> (href 있으면)
|
|
281
|
+
- 리스트: <ul>/<ol> + <li> (반복 패턴)
|
|
282
|
+
- 장식 이미지: alt="" + aria-hidden="true"
|
|
283
|
+
- 콘텐츠 이미지: alt="설명적 텍스트"
|
|
284
|
+
```
|