WIP: hero-icon-cards_1 블록 + 오답노트 + figma 관련 파일
- hero-icon-cards_1.html: hero-icon-cards 변형 (icon → 소제목+불릿 계층) - compare-detail-gradient.html: 하단 2열 비교 블록 (Figma Frame 4 기반) - 오답노트.md: 절대 하지 말아야 하는 실수 목록 - figma_to_html.py: Figma→HTML 변환 스크립트 - static/figma-assets/: Figma export 이미지 (배지, 화살표) - 주의: compare-detail-gradient CSS 폰트 크기가 임의 수정됨 — 원본 복원 필요 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
112
FIGMA-DESIGN-LANGUAGE.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Figma Design Language Analysis
|
||||
|
||||
> Phase 1 결과 문서 (2026-04-07)
|
||||
> Figma Source: `9S6LsQyO6zlRxtiqZccOUM` / Page 1
|
||||
|
||||
## 1. 스코프
|
||||
|
||||
| 프레임 | 역할 | 판정 |
|
||||
|--------|------|------|
|
||||
| Frame 1 (1:3) | 3D 수렴 화살표 | 서브 컴포넌트 (장식 이미지) |
|
||||
| Frame 2 (1:5) | Solution 제작 목표 | **블록화** → hero-icon-cards |
|
||||
| Frame 3 (1:35) | 정책 달성 (Engn.Solution vs DfMA) | **블록화** → compare-2col-badge |
|
||||
| Frame 4 (1:49) | 과정 vs 결과의 혁신 | **블록화** → compare-detail-gradient |
|
||||
| Frame 5 (1:74) | 상세보기 버튼 | 서브 컴포넌트 (CTA) |
|
||||
| Frame 6 (1:80) | 정책방향 (세로 문서) | **제외** (1280×720 부적합) |
|
||||
|
||||
## 2. 스케일 변환
|
||||
|
||||
Figma 캔버스 → 슬라이드(1280px) 변환 비율:
|
||||
- Frame 2, 3: ×0.71 (1808px → 1280px)
|
||||
- Frame 4: ×0.33 (3848px, 양쪽 합쳐서 2패널)
|
||||
|
||||
| Figma | 슬라이드 환산 | 역할 |
|
||||
|-------|-------------|------|
|
||||
| 70px | 28-35px | 대섹션 헤더 |
|
||||
| 60px | 24-28px | Hero 메시지 |
|
||||
| 50px | 22-26px | 섹션 제목, 배지 |
|
||||
| 45px | 20-22px | 카드 타이틀 (EN) |
|
||||
| 40px | 16-20px | 본문 |
|
||||
| 35px | 14-18px | 부제, 한국어 서브 |
|
||||
| 32px | 12-14px | 버튼 |
|
||||
|
||||
## 3. 색상 팔레트 (Warm Theme)
|
||||
|
||||
기존 블루/슬레이트 테마와 **병존**하는 새 팔레트:
|
||||
|
||||
| 토큰 | Hex | Figma 원본 | 용도 |
|
||||
|------|-----|-----------|------|
|
||||
| `--color-warm-brown` | `#5C3714` | rgba(92,55,20) | 과정/프로세스 섹션 제목 |
|
||||
| `--color-dark-teal` | `#084C56` | rgba(8,76,86) | 결과/디지털 섹션 제목 |
|
||||
| `--color-teal` | `#227582` | rgba(34,117,130) | 설명 텍스트 |
|
||||
| `--color-forest` | `#548235` | rgba(84,130,53) | 배경 그라디언트 |
|
||||
| `--color-beige` | `#E4D9C0` | rgba(228,217,192) | 서브틀 배경/버튼 |
|
||||
| `--color-warm-yellow` | `#FAEDCB` | rgba(250,237,203) | 하이라이트 바 |
|
||||
|
||||
### 그라디언트 패턴
|
||||
- 왼쪽(과정): `rgba(165,161,150,0.10) → rgba(57,50,30,1.00)` (베이지→브라운)
|
||||
- 오른쪽(결과): `rgba(41,107,85,0.10) → rgba(3,33,24,1.00)` (틸→다크)
|
||||
- 버튼: `rgba(255,255,255,0.00) → rgba(228,217,192,1.00)` (투명→베이지)
|
||||
- 배경: `rgba(84,130,53,1.00) → rgba(37,62,31,0.00)` (그린→투명)
|
||||
|
||||
## 4. 타이포그래피
|
||||
|
||||
- **폰트**: Pretendard Variable 유지 (Noto Sans KR은 이미 fallback)
|
||||
- **핵심은 크기/굵기 위계**
|
||||
|
||||
| 레벨 | 크기 (슬라이드) | Weight | 스트로크 | 정렬 |
|
||||
|------|---------------|--------|---------|------|
|
||||
| Hero Statement | 24-28px | 700 | white 1.5px | center |
|
||||
| Section Header | 28-35px | 900 | white 5px | center/left |
|
||||
| Badge Title | 22-26px | 700 | 없음 | center |
|
||||
| Card Title (EN) | 20-22px | 900 | white 5px | center |
|
||||
| Card Subtitle (KR) | 14-18px | 500 | white 1.5px | center |
|
||||
| Body Text | 16-20px | 700 | white 1px | left |
|
||||
| Section Sub-title | 22-26px | 900 | 없음 | left |
|
||||
|
||||
### 텍스트 스트로크 기법
|
||||
Figma 디자인의 특징: 다양한 배경 위에서 가독성 확보를 위해 **흰색 스트로크** 사용
|
||||
```css
|
||||
-webkit-text-stroke: 1.5px white; /* 일반 텍스트 */
|
||||
-webkit-text-stroke: 5px white; /* 강조 텍스트 */
|
||||
paint-order: stroke fill; /* 스트로크가 텍스트 뒤로 */
|
||||
```
|
||||
|
||||
## 5. 레이아웃 패턴
|
||||
|
||||
### A. Badge Header
|
||||
- 이미지/그라디언트 배경 위 `border-radius: 20px` 바
|
||||
- 중앙 흰색 텍스트 (50px/700 → 22-26px/700)
|
||||
- 높이: ~88px (Figma) → ~44-50px (슬라이드)
|
||||
|
||||
### B. Hero Statement
|
||||
- 전체 폭 중앙 정렬
|
||||
- 큰 텍스트 (60px/700) + 흰색 스트로크
|
||||
- 키워드 **굵은 강조** 가능
|
||||
|
||||
### C. Icon Card Row
|
||||
- N개 카드 수평 배치, 세로 구분선
|
||||
- 각 카드: 아이콘 이미지 + 영문 제목(900) + 한국어 부제(500)
|
||||
- 흰색 둥근 컨테이너 (borderRadius: 20)
|
||||
|
||||
### D. Two-Col Comparison
|
||||
- 좌/우 그라디언트 배경
|
||||
- 각 열: 헤더 바 + (섹션 제목 + 본문) × N개
|
||||
- 색상으로 좌/우 구분 (브라운 vs 틸)
|
||||
|
||||
### E. CTA Button
|
||||
- 그라디언트 바 (투명→베이지) + 둥근 버튼 (r:7)
|
||||
- 흰색 텍스트
|
||||
|
||||
## 6. 디자인 시스템 vs 콘텐츠 전용 경계
|
||||
|
||||
### 디자인 시스템 (블록에 포함)
|
||||
- 색상 팔레트, 그라디언트 패턴
|
||||
- 타이포그래피 위계, 텍스트 스트로크
|
||||
- 둥근 모서리 컨테이너 (r:20)
|
||||
- Badge Header, 2열 비교, N열 카드 레이아웃 구조
|
||||
|
||||
### 콘텐츠 전용 (블록에 포함하지 않음)
|
||||
- 3D 화살표 이미지 (Frame 1) → 콘텐츠가 제공
|
||||
- 특정 아이콘 이미지들 (brain, thunder 등) → 콘텐츠가 제공
|
||||
- 도메인 텍스트 → 슬롯으로 처리
|
||||
441
FIGMA-EXTRACTION.md
Normal file
@@ -0,0 +1,441 @@
|
||||
# Figma → HTML 블록 변환 프로세스
|
||||
|
||||
> 2026-04-07 확립. Figma 디자인을 design_agent 블록으로 변환하는 정확한 방법론.
|
||||
|
||||
---
|
||||
|
||||
## 1. 전체 워크플로우
|
||||
|
||||
```
|
||||
[Step 1] Figma API로 파일 구조 추출
|
||||
↓
|
||||
[Step 2] 프레임별 렌더링 이미지(PNG) 다운로드
|
||||
↓
|
||||
[Step 3] 노드별 상세 데이터 추출 (좌표, 색상, 폰트, 크기)
|
||||
↓
|
||||
[Step 4] 디자인 언어 분석 (공통 패턴 vs 콘텐츠 전용 구분)
|
||||
↓
|
||||
[Step 5] 블록 설계 (슬롯, 동적 규칙, schema)
|
||||
↓
|
||||
[Step 6] 수학적 계산 (Figma 좌표 → 스케일 → CSS값)
|
||||
↓
|
||||
[Step 7] HTML/CSS 구현
|
||||
↓
|
||||
[Step 8] 비교 리뷰 (Figma PNG vs HTML, 같은 폭으로 위/아래 배치)
|
||||
↓
|
||||
[Step 9] 피드백 반영 → Step 6~8 반복
|
||||
↓
|
||||
[Step 10] Jinja2 템플릿화 + catalog.yaml 등록
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Figma API 사용법
|
||||
|
||||
### 2.1 파일 구조 가져오기
|
||||
```bash
|
||||
curl -s -H "X-Figma-Token: {TOKEN}" \
|
||||
"https://api.figma.com/v1/files/{FILE_KEY}" \
|
||||
| python -m json.tool
|
||||
```
|
||||
|
||||
### 2.2 특정 노드 상세 데이터
|
||||
```bash
|
||||
curl -s -H "X-Figma-Token: {TOKEN}" \
|
||||
"https://api.figma.com/v1/files/{FILE_KEY}/nodes?ids={NODE_IDS}&geometry=paths"
|
||||
```
|
||||
|
||||
### 2.3 노드 이미지 렌더링 (PNG)
|
||||
```bash
|
||||
curl -s -H "X-Figma-Token: {TOKEN}" \
|
||||
"https://api.figma.com/v1/images/{FILE_KEY}?ids={NODE_IDS}&format=png&scale=2"
|
||||
```
|
||||
- `scale=2`: 2배 해상도로 다운로드 (선명도 확보)
|
||||
- 응답의 `images` 객체에 각 노드 ID별 S3 URL 제공
|
||||
|
||||
### 2.4 추출해야 하는 핵심 데이터
|
||||
|
||||
| 데이터 | API 필드 | 용도 |
|
||||
|-------|---------|------|
|
||||
| 위치 | `absoluteBoundingBox.x, .y` | 요소 간 관계 계산 |
|
||||
| 크기 | `absoluteBoundingBox.width, .height` | 스케일 계산 |
|
||||
| 텍스트 | `characters` | 콘텐츠 확인 |
|
||||
| 폰트 | `style.fontFamily, .fontSize, .fontWeight` | 타이포그래피 |
|
||||
| 색상 | `fills[].color` | 색상 팔레트 |
|
||||
| 테두리 | `strokes[], strokeWeight` | 박스 스타일 |
|
||||
| 라운드 | `cornerRadius` | border-radius |
|
||||
| 이미지 | `fills[].imageRef` | 이미지 자산 식별 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 수학적 계산 (핵심)
|
||||
|
||||
### 3.1 스케일 팩터
|
||||
|
||||
```
|
||||
슬라이드 콘텐츠 폭 = 1280px - padding(40px × 2) = 1200px
|
||||
|
||||
scale = 1200 / figma_frame_width
|
||||
```
|
||||
|
||||
| Figma 프레임 | 폭 | 스케일 |
|
||||
|-------------|-----|--------|
|
||||
| Frame 2 | 1808px | 0.6637 |
|
||||
| Frame 3 | 1807px | 0.6641 |
|
||||
| Frame 4 | 3848px | 0.3118 |
|
||||
|
||||
### 3.2 요소 간 정렬 계산
|
||||
|
||||
**절대 원칙: Figma 좌표 차이값 → 스케일 적용 → CSS값**
|
||||
|
||||
```python
|
||||
# 예: 리본 접힘선과 박스 테두리 정렬
|
||||
badge_y = 1431 # Figma에서 badge 이미지 top Y
|
||||
box_y = 1449 # Figma에서 box top Y
|
||||
fold_offset = box_y - badge_y # = 18px (Figma 기준)
|
||||
|
||||
# 스케일 적용
|
||||
fold_offset_css = round(fold_offset * scale) # = 12px (CSS)
|
||||
```
|
||||
|
||||
**금지: "좀 더 올려볼게요" 식의 시행착오 px 조정**
|
||||
|
||||
### 3.3 이미지 자산 크기 계산
|
||||
|
||||
```python
|
||||
# Figma 원본 크기에 스케일 적용
|
||||
ribbon_width_css = round(badge_img_width * scale)
|
||||
ribbon_height_css = round(badge_img_height * scale)
|
||||
|
||||
# 비율 계산 (CSS에서 width만 지정하면 height는 자동)
|
||||
aspect_ratio = badge_img_width / badge_img_height
|
||||
```
|
||||
|
||||
### 3.4 패딩/여백 계산
|
||||
|
||||
```python
|
||||
# 리본이 박스 안에 들어오는 높이 = 리본 전체 높이 - 접힘선 오프셋
|
||||
ribbon_inside_box = ribbon_height_css - fold_offset_css
|
||||
|
||||
# 박스 상단 패딩 = 리본 침입 높이 + 여유
|
||||
box_padding_top = ribbon_inside_box + 6 # 6px 여유
|
||||
```
|
||||
|
||||
### 3.5 실제 계산 예시 (Frame 2)
|
||||
|
||||
```
|
||||
입력 (Figma 원본):
|
||||
badge 이미지: 508×94px, y=1431
|
||||
box: y=1449
|
||||
frame width: 1808px
|
||||
|
||||
계산:
|
||||
scale = 1200/1808 = 0.6637
|
||||
ribbon_w = 508 × 0.6637 = 337px
|
||||
ribbon_h = 94 × 0.6637 = 62px
|
||||
fold_offset = (1449-1431) × 0.6637 = 12px
|
||||
ribbon_below_fold = 62 - 12 = 50px
|
||||
box_padding_top = 50 + 6 = 56px
|
||||
|
||||
CSS 출력:
|
||||
.ribbon { width: 337px; top: -12px; }
|
||||
.box { padding-top: 56px; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 이미지 자산 처리
|
||||
|
||||
### 4.1 CSS로 만들면 안 되는 것
|
||||
|
||||
| 요소 | 이유 | 처리 |
|
||||
|------|------|------|
|
||||
| 3D 리본/두루마리 | 입체감, 그림자, 곡면 → CSS 불가 | Figma에서 PNG 추출 |
|
||||
| 복잡한 그라디언트 배경 | 다중 정지점, 비선형 → CSS 근사 불가 | 이미지 사용 |
|
||||
| 아이콘 이미지 | 디자이너가 만든 고유 자산 | 원본 이미지 사용 |
|
||||
|
||||
### 4.2 CSS로 만들 수 있는 것
|
||||
|
||||
| 요소 | CSS 구현 |
|
||||
|------|---------|
|
||||
| 단색/2색 그라디언트 배경 | `linear-gradient()` |
|
||||
| 둥근 모서리 테두리 박스 | `border + border-radius` |
|
||||
| 텍스트 스타일 | `font-size, font-weight, color` |
|
||||
| 그리드/플렉스 레이아웃 | `display: grid / flex` |
|
||||
| 구분선 | `border` or `background` |
|
||||
|
||||
### 4.3 이미지 추출 및 저장
|
||||
|
||||
```bash
|
||||
# Figma API로 특정 노드 이미지 추출
|
||||
curl -s -H "X-Figma-Token: {TOKEN}" \
|
||||
"https://api.figma.com/v1/images/{FILE_KEY}?ids={NODE_ID}&format=png&scale=2"
|
||||
|
||||
# 다운로드 → static/figma-assets/ 에 저장
|
||||
curl -s -o static/figma-assets/{name}.png "{S3_URL}"
|
||||
```
|
||||
|
||||
저장 위치: `static/figma-assets/`
|
||||
|
||||
---
|
||||
|
||||
## 5. 비교 리뷰 페이지 작성법
|
||||
|
||||
### 5.1 레이아웃
|
||||
|
||||
```
|
||||
같은 폭으로 위/아래 배치 (좌/우 아님 — 크기 차이 문제)
|
||||
|
||||
┌─ 빨간 테두리 ──────────────┐
|
||||
│ Figma Original (PNG) │
|
||||
└─────────────────────────────┘
|
||||
─ 구분선 ─
|
||||
┌─ 초록 테두리 ──────────────┐
|
||||
│ HTML Block │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
### 5.2 HTML 스케일링
|
||||
|
||||
```css
|
||||
.html-inner {
|
||||
width: 1280px; /* 슬라이드 원본 크기 */
|
||||
transform-origin: top left;
|
||||
transform: scale(0.74); /* 960px 컨테이너에 맞춤: 960/1280 */
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 비교 리뷰 파일 위치
|
||||
|
||||
`data/figma_ref/comparison.html`
|
||||
|
||||
---
|
||||
|
||||
## 6. Jinja2 템플릿 변환 규칙
|
||||
|
||||
### 6.1 고정값 → 변수
|
||||
|
||||
```html
|
||||
<!-- Figma 원본의 텍스트 → Jinja2 변수 -->
|
||||
<span>정책 달성</span> → <span>{{ badge_title }}</span>
|
||||
<span>Engn. Solution</span> → <span>{{ left_title }}</span>
|
||||
```
|
||||
|
||||
### 6.2 반복 요소 → 루프
|
||||
|
||||
```html
|
||||
<!-- N개 카드 → for loop -->
|
||||
{% for card in cards %}
|
||||
<div class="card">{{ card.title }}</div>
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
### 6.3 이미지 자산 → 슬롯
|
||||
|
||||
```html
|
||||
<!-- 리본 이미지: 색상에 따라 다른 자산 사용 가능 -->
|
||||
<img src="{{ ribbon_image | default('figma-assets/badge_solution.png') }}">
|
||||
```
|
||||
|
||||
### 6.4 계산된 CSS → CSS 변수
|
||||
|
||||
```html
|
||||
<!-- 수학적 계산 결과를 CSS 변수로 -->
|
||||
<div style="--ribbon-width: {{ ribbon_width }}px; --fold-offset: {{ fold_offset }}px;">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 디자인 언어 vs 콘텐츠 전용 구분
|
||||
|
||||
### 디자인 언어 (블록에 포함, 재사용 가능)
|
||||
- 색상 팔레트 (warm 테마: 브라운, 틸, 베이지)
|
||||
- 타이포그래피 위계 (크기, 굵기 단계)
|
||||
- 레이아웃 구조 (2열 비교, N열 카드 등)
|
||||
- 장식 요소 (3D 리본, 둥근 컨테이너)
|
||||
|
||||
### 콘텐츠 전용 (블록에 포함하지 않음)
|
||||
- 특정 텍스트 ("디지털전환은 사용자...")
|
||||
- 특정 아이콘 이미지 (brain, thunder 등)
|
||||
- 도메인 전문 용어 (DfMA, Engn. Solution)
|
||||
|
||||
---
|
||||
|
||||
## 8. 파일 구조
|
||||
|
||||
```
|
||||
design_agent/
|
||||
├── static/figma-assets/ ← Figma에서 추출한 이미지 자산
|
||||
│ ├── badge_policy.png (틸 3D 리본)
|
||||
│ ├── badge_solution.png (빨간 3D 리본)
|
||||
│ ├── box_policy_container.png
|
||||
│ └── box_solution_cards.png
|
||||
├── data/figma_ref/ ← 비교 리뷰용
|
||||
│ ├── comparison.html (Figma vs HTML 비교 페이지)
|
||||
│ ├── frame2_1-5.png (Figma 원본 PNG)
|
||||
│ ├── frame3_1-35.png
|
||||
│ └── frame4_1-49.png
|
||||
├── templates/blocks/cards/ ← 블록 템플릿
|
||||
│ ├── hero-icon-cards.html
|
||||
│ ├── compare-2col-badge.html
|
||||
│ └── compare-detail-gradient.html
|
||||
├── FIGMA-DESIGN-LANGUAGE.md ← 디자인 언어 분석 결과
|
||||
├── FIGMA-EXTRACTION.md ← 이 문서
|
||||
└── PHASE-FIGMA-BLOCKS.md ← 블록 설계 명세
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 고급 레이아웃 패턴
|
||||
|
||||
### 9.1 좌/우 열 섹션 Y선 정렬 (CSS Grid 행 공유)
|
||||
|
||||
2열 비교에서 좌/우 섹션 제목이 같은 Y선에 있어야 할 때:
|
||||
|
||||
**문제**: 각 열을 독립 flex-column으로 만들면, 좌측 섹션 본문이 길면 우측 다음 섹션이 밀림.
|
||||
```
|
||||
flex-column (잘못):
|
||||
좌: [제목1] [긴본문] [제목2]
|
||||
우: [제목1] [짧은본문] [제목2] ← 제목2가 좌측과 Y가 다름
|
||||
```
|
||||
|
||||
**해결**: CSS Grid 2열 × N행으로 행을 공유하면 자동 정렬.
|
||||
```css
|
||||
.block {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr; /* 2열 */
|
||||
grid-template-rows: auto auto auto auto; /* 헤더 + N행 */
|
||||
}
|
||||
```
|
||||
```
|
||||
Grid (올바름):
|
||||
[좌 헤더] [우 헤더] ← Row 0
|
||||
[좌 섹션1] [우 섹션1] ← Row 1 (행 높이 = max(좌,우))
|
||||
[좌 섹션2] [우 섹션2] ← Row 2 (Y선 자동 정렬!)
|
||||
```
|
||||
|
||||
**실제 계산 (Frame 4)**:
|
||||
```
|
||||
Figma Y좌표:
|
||||
Row 1: 좌 1166, 우 1166 → 0px 차이 (이미 정렬)
|
||||
Row 2: 좌 1529, 우 1467 → 62px 차이 (Grid가 해결)
|
||||
Row 3: 좌 1845, 우 1845 → 0px 차이 (이미 정렬)
|
||||
원인: Row 1 좌측에 As-Is→To-Be 구조가 있어서 본문이 62px 더 높음
|
||||
```
|
||||
|
||||
### 9.2 As-Is → To-Be 수평 서브 레이아웃
|
||||
|
||||
한 섹션 안에서 변환 전/후를 수평 배치할 때:
|
||||
|
||||
```html
|
||||
<div class="asis-tobe">
|
||||
<div class="asis">
|
||||
<div class="bullet">이전 상태 1</div>
|
||||
<div class="bullet">이전 상태 2</div>
|
||||
</div>
|
||||
<img src="arrow.png" class="arrow" alt="→">
|
||||
<div class="tobe">
|
||||
<div class="bullet">변환 후 1</div>
|
||||
<div class="bullet">변환 후 2</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
```css
|
||||
.asis-tobe { display: flex; align-items: center; gap: 8px; }
|
||||
.asis, .tobe { flex: 1; }
|
||||
.arrow { width: 60px; height: auto; flex-shrink: 0; }
|
||||
```
|
||||
|
||||
**Figma 좌표로 검증**:
|
||||
```
|
||||
As-Is: x=2737, w=539
|
||||
Arrow: x=3375, w=252
|
||||
To-Be: x=3687, w=672
|
||||
→ 세 요소가 같은 Y(1269)에 수평 배치됨을 좌표로 확인
|
||||
```
|
||||
|
||||
### 9.3 3D 리본/두루마리 배지 정렬 공식
|
||||
|
||||
리본 이미지의 접힘선(fold-back)이 박스 테두리와 정확히 일치해야 할 때:
|
||||
|
||||
```
|
||||
┌── 리본 이미지 ──────────────┐
|
||||
│ 접힘 삼각형 (fold) │ ← fold_offset (이미지 top에서)
|
||||
│ 리본 본체 │
|
||||
│ │
|
||||
└──────────────────────────────┘
|
||||
════════════════════════════════ ← 박스 top border (여기에 fold가 일치해야 함)
|
||||
┌── 박스 ──────────────────────┐
|
||||
│ padding-top = ribbon_below │
|
||||
│ 콘텐츠 시작 │
|
||||
|
||||
계산:
|
||||
fold_offset = (box_y - badge_y) × scale → CSS: top 값
|
||||
ribbon_below = ribbon_height - fold_offset → 박스 안 침입 높이
|
||||
box_padding_top = ribbon_below + 여유(6px) → 콘텐츠 겹침 방지
|
||||
```
|
||||
|
||||
**핵심**: 리본을 올리거나 내리는 게 아니라, **박스의 위치를 계산**하는 것.
|
||||
- `top: -fold_offset` → 리본 접힘선 = 박스 top border
|
||||
- 리본은 그대로, 박스와의 관계만 수학적으로 결정
|
||||
|
||||
---
|
||||
|
||||
## 10. 실수 방지 (Anti-patterns)
|
||||
|
||||
### 10.1 절대 하면 안 되는 것
|
||||
|
||||
| Anti-pattern | 왜 안 되는지 | 올바른 방법 |
|
||||
|-------------|------------|-----------|
|
||||
| px 시행착오 조정 ("좀 더 올려볼게") | 3번 이상 실패, 시간 낭비 | Figma 좌표에서 수학적 계산 |
|
||||
| 3D 효과를 CSS로 재현 | 평면적이라 품질 차이 심각 | Figma에서 PNG 추출 |
|
||||
| 비교 리뷰를 좌/우 배치 | 크기 차이로 비교 불가 | 위/아래 같은 폭으로 배치 |
|
||||
| Jinja2 템플릿을 브라우저에서 직접 열기 | 변수 미렌더, 이미지 경로 깨짐 | comparison.html 또는 FastAPI로 확인 |
|
||||
| 독립 flex-column으로 2열 비교 | 행 정렬 안 됨 | CSS Grid 행 공유 |
|
||||
| 느낌으로 폰트/색상 설정 | Figma와 다른 결과물 | Figma API에서 정확한 값 추출 |
|
||||
|
||||
### 10.2 반드시 해야 하는 것
|
||||
|
||||
| 원칙 | 이유 |
|
||||
|------|------|
|
||||
| CSS 주석에 계산 근거 기록 | 나중에 왜 이 값인지 추적 가능 |
|
||||
| 비교 리뷰 후 진행 | 디자인 차이를 사전에 발견 |
|
||||
| 이미지 자산은 `static/figma-assets/`에 저장 | FastAPI가 서빙, 경로 일관성 |
|
||||
| `comparison.html`에 모든 프레임 포함 | 한 페이지에서 전체 리뷰 가능 |
|
||||
| Figma 노드 ID 기록 | 나중에 업데이트된 디자인 재추출 가능 |
|
||||
|
||||
---
|
||||
|
||||
## 11. Figma 소스 정보
|
||||
|
||||
### 현재 등록된 Figma 파일
|
||||
|
||||
| 항목 | 값 |
|
||||
|------|---|
|
||||
| File Key | `9S6LsQyO6zlRxtiqZccOUM` |
|
||||
| Page | Page 1 (0:1) |
|
||||
| Frame 2 (hero-icon-cards) | Node `1:5` |
|
||||
| Frame 3 (compare-2col-badge) | Node `1:35` |
|
||||
| Frame 4 (compare-detail-gradient) | Node `1:49` |
|
||||
| Badge (빨간 리본) | Node `1:33` (image 4019) |
|
||||
| Badge (틸 리본) | Node `1:43` (image 2197) |
|
||||
| Arrow (As-Is→To-Be) | Node `1:67` (image 2645) |
|
||||
| Box (빨간 테두리) | Node `1:12` (Rectangle 42894) |
|
||||
| Box (틸 테두리) | Node `1:37` (Rectangle 42598) |
|
||||
|
||||
---
|
||||
|
||||
## 12. 체크리스트
|
||||
|
||||
새 Figma 프레임을 블록으로 변환할 때:
|
||||
|
||||
- [ ] Figma API로 노드 데이터 추출 (좌표, 크기, 색상, 폰트)
|
||||
- [ ] PNG 렌더링 다운로드 (scale=2)
|
||||
- [ ] 복잡한 비주얼 요소 식별 → 이미지로 추출 (CSS로 만들지 않음)
|
||||
- [ ] 스케일 팩터 계산 (1200 / frame_width)
|
||||
- [ ] 핵심 정렬 포인트 수학적 계산 (좌표 차이 × 스케일)
|
||||
- [ ] CSS 값 도출 (계산 근거를 주석으로 기록)
|
||||
- [ ] 비교 리뷰 페이지에 추가 (위/아래 같은 폭)
|
||||
- [ ] 사용자 피드백 확인
|
||||
- [ ] Jinja2 템플릿 변환 (고정값→변수, 반복→루프)
|
||||
- [ ] catalog.yaml 등록
|
||||
463
PHASE-FIGMA-BLOCKS.md
Normal file
@@ -0,0 +1,463 @@
|
||||
# Phase 2: Figma Block Design Specification
|
||||
|
||||
> 3개 블록 + 2개 서브 컴포넌트 상세 설계
|
||||
> 기준: FIGMA-DESIGN-LANGUAGE.md 분석 결과
|
||||
|
||||
---
|
||||
|
||||
## Block 1: `hero-icon-cards`
|
||||
|
||||
### 1.1 시각적 구조
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ [Hero Statement - 큰 텍스트, 중앙] │ ← zone: header or full-width
|
||||
│ │
|
||||
│ ┌─[Badge Title]─┐ │
|
||||
│──────────┤ ├───────────────────│
|
||||
│ ┌─────┐ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
|
||||
│ │icon │ │ │icon │ │icon │ │icon │ │icon │ │ ← N개 카드 (2~6)
|
||||
│ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
│ │Title│ ╎ │Title│ │Title│ │Title│ │Title│ │ ← 세로 구분선
|
||||
│ │(sub)│ │ │(sub)│ │(sub)│ │(sub)│ │(sub)│ │
|
||||
│ └─────┘ │ └─────┘ └─────┘ └─────┘ └─────┘ │
|
||||
│ └───────────────────────────────────│
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.2 슬롯 정의
|
||||
|
||||
| 슬롯 | 필수 | 타입 | 설명 |
|
||||
|------|------|------|------|
|
||||
| `statement` | O | string | Hero 메시지 (1-2줄) |
|
||||
| `badge_title` | X | string | 배지 바 텍스트 |
|
||||
| `cards[]` | O | array | 카드 배열 |
|
||||
| `cards[].icon` | X | string | 아이콘 이미지 URL 또는 이모지 |
|
||||
| `cards[].title` | O | string | 영문 또는 주제목 |
|
||||
| `cards[].subtitle` | X | string | 한국어 부제 |
|
||||
| `cards[].color` | X | string | 개별 카드 강조색 |
|
||||
|
||||
### 1.3 동적 재구성 규칙
|
||||
|
||||
#### 그리드 계산
|
||||
```
|
||||
입력: N = cards.length, W = container_width_px
|
||||
|
||||
N ≤ 5: 1행 N열
|
||||
col_count = N
|
||||
card_width = (W - padding*2 - gap*(N-1)) / N
|
||||
|
||||
N = 6: 1행 6열 (gap 축소)
|
||||
col_count = 6
|
||||
gap = 8px (기본 16px에서 축소)
|
||||
|
||||
N > 6: 2행
|
||||
col_count = ceil(N / 2)
|
||||
row_count = 2
|
||||
```
|
||||
|
||||
#### 폰트 스케일링
|
||||
```
|
||||
card_width ≥ 200px → title: 20px, subtitle: 14px
|
||||
card_width ≥ 150px → title: 16px, subtitle: 12px
|
||||
card_width < 150px → title: 14px, subtitle: 11px
|
||||
```
|
||||
|
||||
#### 높이 계산
|
||||
```
|
||||
hero_height = statement_lines * line_height + padding
|
||||
badge_height = 44px (고정)
|
||||
card_area_height = icon_height + title_lines * title_lh + subtitle_lh + padding
|
||||
- 1행: card_area_height
|
||||
- 2행: card_area_height * 2 + gap
|
||||
|
||||
total_min_height = hero_height + badge_height + card_area_height + gaps
|
||||
```
|
||||
|
||||
### 1.4 catalog.yaml schema
|
||||
|
||||
```yaml
|
||||
- id: hero-icon-cards
|
||||
name: 히어로 문구 + 아이콘 카드
|
||||
category: cards
|
||||
template: blocks/cards/hero-icon-cards.html
|
||||
height_cost: xlarge
|
||||
min_height_px: 280
|
||||
relation_types: [definition, flow]
|
||||
min_items: 2
|
||||
max_items: 6
|
||||
visual: >
|
||||
상단에 큰 Hero 메시지(24px bold, 중앙) + 배지 바 +
|
||||
하단에 N열 아이콘 카드(둥근 흰색 컨테이너, 세로 구분선).
|
||||
각 카드는 아이콘 이미지 + 영문 제목(20px/900) + 한국어 부제(14px/500).
|
||||
when: >
|
||||
핵심 목표나 가치를 N개 키워드로 선언할 때.
|
||||
각 키워드에 아이콘이나 이미지가 있을 때.
|
||||
"우리가 추구하는 5가지 가치" 같은 구조.
|
||||
not_for: >
|
||||
비교/대조 구조 → compare-2col-badge.
|
||||
상세 설명이 길 때 → card-icon-desc.
|
||||
순서/단계 → card-step-vertical 또는 process-horizontal.
|
||||
purpose_fit: [핵심전달, 가치선언]
|
||||
zone: full-width-only
|
||||
slots:
|
||||
required: [statement, cards[]]
|
||||
optional: [badge_title, cards[].icon, cards[].subtitle, cards[].color]
|
||||
schema:
|
||||
statement:
|
||||
max_lines: 2
|
||||
font_size: 24
|
||||
ref_chars:
|
||||
body: 60
|
||||
note: "24px bold, 중앙정렬, 흰색 스트로크"
|
||||
badge_title:
|
||||
max_lines: 1
|
||||
font_size: 18
|
||||
ref_chars:
|
||||
body: 20
|
||||
note: "18px bold white, 배지 바 위"
|
||||
card_title:
|
||||
max_lines: 2
|
||||
font_size: 20
|
||||
ref_chars:
|
||||
body: 15
|
||||
note: "20px black/900, 중앙정렬"
|
||||
card_subtitle:
|
||||
max_lines: 1
|
||||
font_size: 14
|
||||
ref_chars:
|
||||
body: 10
|
||||
note: "14px medium, 한국어 부제"
|
||||
padding_overhead_px: 60
|
||||
padding_h_px: 32
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Block 2: `compare-2col-badge`
|
||||
|
||||
### 2.1 시각적 구조
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ ┌─[Badge Title]─┐ │
|
||||
│────────────┤ ├─────────────────│
|
||||
│ │
|
||||
│ ┌── Left Column ──┐ ╎ ┌── Right Column ──┐ │
|
||||
│ │ │ ╎ │ │ │
|
||||
│ │ [Big Title] │ ╎ │ [Big Title] │ │
|
||||
│ │ │ ╎ │ │ │
|
||||
│ │ body text... │ ╎ │ body text... │ │
|
||||
│ │ body text... │ ╎ │ body text... │ │
|
||||
│ │ │ ╎ │ │ │
|
||||
│ └──────────────────┘ ╎ └──────────────────┘ │
|
||||
│ │
|
||||
│ [Optional: Hero Statement] │
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 슬롯 정의
|
||||
|
||||
| 슬롯 | 필수 | 타입 | 설명 |
|
||||
|------|------|------|------|
|
||||
| `badge_title` | O | string | 배지 바 텍스트 |
|
||||
| `left_title` | O | string | 좌측 열 대제목 |
|
||||
| `left_body` | O | string | 좌측 열 본문 |
|
||||
| `right_title` | O | string | 우측 열 대제목 |
|
||||
| `right_body` | O | string | 우측 열 본문 |
|
||||
| `statement` | X | string | 하단 Hero 메시지 |
|
||||
| `left_color` | X | string | 좌측 강조색 (기본: --color-teal) |
|
||||
| `right_color` | X | string | 우측 강조색 (기본: --color-teal) |
|
||||
|
||||
### 2.3 동적 재구성 규칙
|
||||
|
||||
#### 레이아웃 계산
|
||||
```
|
||||
container_width = 컨테이너 전체 폭
|
||||
padding_h = 32px * 2
|
||||
|
||||
2열 모드 (기본):
|
||||
col_width = (container_width - padding_h - divider_gap) / 2
|
||||
divider_gap = 32px
|
||||
|
||||
1열 모드 (sidebar zone, 폭 < 500px):
|
||||
좌/우가 세로 스택
|
||||
col_width = container_width - padding_h
|
||||
```
|
||||
|
||||
#### 높이 계산
|
||||
```
|
||||
badge_height = 44px
|
||||
left_height = title_height + body_lines * line_height + padding
|
||||
right_height = title_height + body_lines * line_height + padding
|
||||
content_height = max(left_height, right_height)
|
||||
statement_height = statement ? (statement_lines * 28 + 16) : 0
|
||||
|
||||
total = badge_height + content_height + statement_height + gaps
|
||||
```
|
||||
|
||||
#### 텍스트 피팅
|
||||
```
|
||||
col_width에 따른 body 글자수 제한:
|
||||
col_width ≥ 500px → ~40자/줄, font: 16px
|
||||
col_width ≥ 350px → ~28자/줄, font: 14px
|
||||
col_width < 350px → ~20자/줄, font: 13px
|
||||
```
|
||||
|
||||
### 2.4 catalog.yaml schema
|
||||
|
||||
```yaml
|
||||
- id: compare-2col-badge
|
||||
name: 배지 헤더 2열 비교
|
||||
category: cards
|
||||
template: blocks/cards/compare-2col-badge.html
|
||||
height_cost: large
|
||||
min_height_px: 200
|
||||
relation_types: [comparison, contrast]
|
||||
visual: >
|
||||
상단 배지 바(이미지/그라디언트 배경 + 흰색 텍스트) 아래
|
||||
2열 비교 레이아웃. 좌/우 각각 대제목(24px/900) + 본문(16px/700).
|
||||
중앙 세로 구분선. 둥근 흰색 컨테이너(r:20).
|
||||
선택적 하단 Hero 메시지.
|
||||
when: >
|
||||
두 개념/방법/전략을 나란히 비교할 때.
|
||||
배지 헤더로 상위 주제를 명시.
|
||||
예: "Engn. Solution vs DfMA", "현재 vs 미래"
|
||||
not_for: >
|
||||
3개 이상 항목 비교 → compare-3col-badge.
|
||||
장/단점 목록 → comparison-2col.
|
||||
상세 내용이 길고 섹션이 많을 때 → compare-detail-gradient.
|
||||
purpose_fit: [비교대조, 개념정의]
|
||||
zone: full-width-only
|
||||
slots:
|
||||
required: [badge_title, left_title, left_body, right_title, right_body]
|
||||
optional: [statement, left_color, right_color]
|
||||
schema:
|
||||
badge_title:
|
||||
max_lines: 1
|
||||
font_size: 18
|
||||
ref_chars:
|
||||
body: 15
|
||||
note: "18px bold white, 배지 바"
|
||||
left_title:
|
||||
max_lines: 1
|
||||
font_size: 24
|
||||
ref_chars:
|
||||
body: 15
|
||||
note: "24px black/900, 흰색 스트로크"
|
||||
left_body:
|
||||
max_lines: 6
|
||||
font_size: 16
|
||||
ref_chars:
|
||||
body: 200
|
||||
note: "16px/700, 틸 색상"
|
||||
right_title:
|
||||
max_lines: 1
|
||||
font_size: 24
|
||||
ref_chars:
|
||||
body: 15
|
||||
note: "24px black/900, 흰색 스트로크"
|
||||
right_body:
|
||||
max_lines: 6
|
||||
font_size: 16
|
||||
ref_chars:
|
||||
body: 200
|
||||
note: "16px/700, 틸 색상"
|
||||
statement:
|
||||
max_lines: 2
|
||||
font_size: 20
|
||||
ref_chars:
|
||||
body: 50
|
||||
note: "20px bold, 중앙정렬"
|
||||
padding_overhead_px: 56
|
||||
padding_h_px: 32
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Block 3: `compare-detail-gradient`
|
||||
|
||||
### 3.1 시각적 구조
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ ┌───── Left Header Bar (gradient) ─────┐┌── Right ─────┐│
|
||||
│ │ [Left Column Title] ││ [Right Title] ││
|
||||
│ └──────────────────────────────────────┘└───────────────┘│
|
||||
│ ┌─────── Left BG (warm) ──────┐┌──── Right BG (teal) ──┐│
|
||||
│ │ ││ ││
|
||||
│ │ [Section 1 Title] ││ [Section 1 Title] ││
|
||||
│ │ • body text ││ • body text ││
|
||||
│ │ • body text ││ • body text ││
|
||||
│ │ ││ ││
|
||||
│ │ [Section 2 Title] ││ [Section 2 Title] ││
|
||||
│ │ • body text ││ • body text ││
|
||||
│ │ • body text ││ • body text ││
|
||||
│ │ ││ ││
|
||||
│ │ [Section N Title] ││ [Section M Title] ││
|
||||
│ │ • body text ││ • body text ││
|
||||
│ └──────────────────────────────┘└───────────────────────┘│
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.2 슬롯 정의
|
||||
|
||||
| 슬롯 | 필수 | 타입 | 설명 |
|
||||
|------|------|------|------|
|
||||
| `left_header` | O | string | 좌측 열 헤더 타이틀 |
|
||||
| `right_header` | O | string | 우측 열 헤더 타이틀 |
|
||||
| `left_sections[]` | O | array | 좌측 섹션 배열 |
|
||||
| `left_sections[].title` | O | string | 섹션 소제목 |
|
||||
| `left_sections[].body` | O | string | 섹션 본문 (줄바꿈 허용) |
|
||||
| `right_sections[]` | O | array | 우측 섹션 배열 |
|
||||
| `right_sections[].title` | O | string | 섹션 소제목 |
|
||||
| `right_sections[].body` | O | string | 섹션 본문 |
|
||||
| `left_color_theme` | X | string | 좌측 테마 (기본: warm) |
|
||||
| `right_color_theme` | X | string | 우측 테마 (기본: teal) |
|
||||
|
||||
### 3.3 동적 재구성 규칙 (★ 가장 수학적으로 복잡)
|
||||
|
||||
#### 그리드 계산
|
||||
```
|
||||
container_width에서 2열 분할:
|
||||
col_width = (container_width - gap) / 2
|
||||
gap = 0px (그라디언트가 맞닿음)
|
||||
```
|
||||
|
||||
#### 섹션 높이 계산 (핵심)
|
||||
```
|
||||
header_bar_height = 48px (고정)
|
||||
|
||||
각 섹션의 높이:
|
||||
section_height(s) =
|
||||
title_height(s.title, title_font_size, col_width) +
|
||||
body_height(s.body, body_font_size, col_width) +
|
||||
section_padding
|
||||
|
||||
title_height = ceil(char_count / chars_per_line) * title_line_height
|
||||
body_height = line_count * body_line_height
|
||||
|
||||
chars_per_line = floor(col_width / (font_size * 0.55)) // 한글 평균 0.55em
|
||||
|
||||
좌측 전체:
|
||||
left_total = header_bar + sum(section_height for s in left_sections) + gaps
|
||||
|
||||
우측 전체:
|
||||
right_total = header_bar + sum(section_height for s in right_sections) + gaps
|
||||
|
||||
content_height = max(left_total, right_total)
|
||||
```
|
||||
|
||||
#### 오버플로 방지 — Fit 검증
|
||||
```
|
||||
if content_height > container_available_height:
|
||||
|
||||
전략 1: 폰트 축소
|
||||
body_font_size -= 1px (최소 12px)
|
||||
재계산
|
||||
|
||||
전략 2: 섹션 본문 줄 수 제한
|
||||
max_body_lines = floor(
|
||||
(available_per_section - title_height) / body_line_height
|
||||
)
|
||||
available_per_section = (container_height - header*2 - gaps) / max(N_left, N_right)
|
||||
|
||||
전략 3: Kei 에스컬레이션 (기존 파이프라인)
|
||||
content 요약 요청
|
||||
```
|
||||
|
||||
#### 색상 테마 매핑
|
||||
```
|
||||
warm (좌측 기본):
|
||||
header_gradient: rgba(165,161,150,0.10) → rgba(57,50,30,1.00)
|
||||
section_title_color: var(--color-warm-brown)
|
||||
bg: rgba(255,255,255,0.30) → rgba(57,50,30,0.30)
|
||||
|
||||
teal (우측 기본):
|
||||
header_gradient: rgba(41,107,85,0.10) → rgba(3,33,24,1.00)
|
||||
section_title_color: var(--color-dark-teal)
|
||||
bg: rgba(41,107,85,0.30) → rgba(255,255,255,0.30)
|
||||
```
|
||||
|
||||
### 3.4 catalog.yaml schema
|
||||
|
||||
```yaml
|
||||
- id: compare-detail-gradient
|
||||
name: 그라디언트 상세 2열 비교
|
||||
category: cards
|
||||
template: blocks/cards/compare-detail-gradient.html
|
||||
height_cost: xlarge
|
||||
min_height_px: 300
|
||||
relation_types: [comparison, contrast, process]
|
||||
min_items: 2 # 좌/우 최소 1섹션씩
|
||||
max_items: 10 # 좌+우 합계
|
||||
visual: >
|
||||
좌우 그라디언트 배경(워 브라운 vs 다크틸)으로 나뉜 2열 비교.
|
||||
각 열 상단에 그라디언트 헤더 바 + 큰 제목(28px/900).
|
||||
하단에 N개 섹션(소제목 22px/900 + 본문 16px/700) 반복.
|
||||
좌측은 따뜻한 톤(과정/As-Is), 우측은 차가운 톤(결과/To-Be).
|
||||
when: >
|
||||
두 카테고리를 상세하게 비교할 때.
|
||||
각 카테고리에 여러 하위 항목이 있을 때.
|
||||
과정 vs 결과, As-Is vs To-Be, 문제 vs 해결 구조.
|
||||
not_for: >
|
||||
간단한 2항목 비교(본문 짧을 때) → compare-2col-badge.
|
||||
3열 비교 → compare-3col-badge.
|
||||
비교가 아닌 단독 리스트 → dark-bullet-list.
|
||||
purpose_fit: [비교대조, 구조시각화, 근거사례]
|
||||
zone: full-width-only
|
||||
slots:
|
||||
required: [left_header, right_header, left_sections[], right_sections[]]
|
||||
optional: [left_color_theme, right_color_theme]
|
||||
schema:
|
||||
left_header:
|
||||
max_lines: 1
|
||||
font_size: 28
|
||||
ref_chars:
|
||||
body: 20
|
||||
note: "28px black/900, 그라디언트 바 위"
|
||||
right_header:
|
||||
max_lines: 1
|
||||
font_size: 28
|
||||
ref_chars:
|
||||
body: 20
|
||||
note: "28px black/900, 그라디언트 바 위"
|
||||
section_title:
|
||||
max_lines: 2
|
||||
font_size: 22
|
||||
ref_chars:
|
||||
body: 30
|
||||
note: "22px/900, 색상 테마별 (브라운 or 틸)"
|
||||
section_body:
|
||||
max_lines: 4
|
||||
font_size: 16
|
||||
ref_chars:
|
||||
body: 120
|
||||
note: "16px/700, black"
|
||||
padding_overhead_px: 48
|
||||
padding_h_px: 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 서브 컴포넌트
|
||||
|
||||
### S1. 장식 이미지 (3D 화살표 등)
|
||||
- 블록이 아닌 **콘텐츠 이미지**로 처리
|
||||
- `cards[].icon` 또는 별도 `decoration_image` 슬롯으로 전달
|
||||
- 블록은 `<img>` 태그로 렌더링, 크기는 CSS로 컨테이너에 맞춤
|
||||
|
||||
### S2. CTA 버튼
|
||||
- 독립 블록이 아닌 **다른 블록 내 선택적 요소**
|
||||
- `cta_text` 슬롯으로 전달 (없으면 미표시)
|
||||
- CSS: 그라디언트 바 + 둥근 버튼 (r:7)
|
||||
|
||||
---
|
||||
|
||||
## 구현 우선순위
|
||||
|
||||
| 순서 | 블록 | 이유 |
|
||||
|------|------|------|
|
||||
| 1 (파일럿) | `compare-2col-badge` | 중간 복잡도, 기존 compare-2col-split과 비교 검증 가능 |
|
||||
| 2 | `hero-icon-cards` | N개 카드 그리드 계산 필요, 파일럿 경험 활용 |
|
||||
| 3 | `compare-detail-gradient` | 가장 복잡 (N개 섹션 × 2열, 높이 균형, 오버플로 방지) |
|
||||
261
scripts/figma_to_html.py
Normal file
@@ -0,0 +1,261 @@
|
||||
"""Figma 프레임을 HTML/CSS로 변환.
|
||||
|
||||
기존 블록 템플릿(templates/blocks/)과 동일한 방식으로
|
||||
Figma API 데이터에서 정확한 HTML을 생성한다.
|
||||
|
||||
Usage:
|
||||
python scripts/figma_to_html.py data/runs/figma_beps_full.json templates/blocks/BEPs/
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import math
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_fill_css(fills: list[dict]) -> tuple[str, str]:
|
||||
"""fills 배열에서 CSS background와 color를 추출.
|
||||
|
||||
Returns: (background_css, color_css)
|
||||
"""
|
||||
bg = ""
|
||||
color = ""
|
||||
for f in fills:
|
||||
if not f.get("visible", True):
|
||||
continue
|
||||
ftype = f.get("type", "")
|
||||
if ftype == "SOLID":
|
||||
c = f["color"]
|
||||
r, g, b = int(c["r"] * 255), int(c["g"] * 255), int(c["b"] * 255)
|
||||
a = f.get("opacity", c.get("a", 1))
|
||||
if a < 0.99:
|
||||
val = f"rgba({r},{g},{b},{a:.2f})"
|
||||
else:
|
||||
val = f"#{r:02x}{g:02x}{b:02x}"
|
||||
bg = val
|
||||
color = val
|
||||
elif "GRADIENT" in ftype:
|
||||
stops = f.get("gradientStops", [])
|
||||
handles = f.get("gradientHandlePositions", [])
|
||||
if len(stops) < 2:
|
||||
continue
|
||||
# 각도 계산
|
||||
if len(handles) >= 2:
|
||||
dx = handles[1]["x"] - handles[0]["x"]
|
||||
dy = handles[1]["y"] - handles[0]["y"]
|
||||
angle = math.degrees(math.atan2(dy, dx)) + 90
|
||||
else:
|
||||
angle = 180
|
||||
stop_str = ",".join(
|
||||
f"#{int(s['color']['r']*255):02x}{int(s['color']['g']*255):02x}{int(s['color']['b']*255):02x} {s['position']*100:.0f}%"
|
||||
for s in stops
|
||||
)
|
||||
bg = f"linear-gradient({angle:.0f}deg,{stop_str})"
|
||||
color = bg
|
||||
elif ftype == "IMAGE":
|
||||
bg = "__IMAGE__"
|
||||
return bg, color
|
||||
|
||||
|
||||
def node_to_html(node: dict, ox: float, oy: float, scale: float) -> str:
|
||||
"""단일 노드를 HTML div로 변환."""
|
||||
ntype = node.get("type", "")
|
||||
name = node.get("name", "")
|
||||
bb = node.get("absoluteBoundingBox")
|
||||
if not bb or not bb.get("width"):
|
||||
return ""
|
||||
|
||||
x = (bb["x"] - ox) * scale
|
||||
y = (bb["y"] - oy) * scale
|
||||
w = bb["width"] * scale
|
||||
h = bb["height"] * scale
|
||||
|
||||
fills = node.get("fills", [])
|
||||
visible_fills = [f for f in fills if f.get("visible", True)]
|
||||
|
||||
if ntype == "TEXT":
|
||||
chars = node.get("characters", "")
|
||||
if not chars:
|
||||
return ""
|
||||
style = node.get("style", {})
|
||||
fs = style.get("fontSize", 12) * scale
|
||||
fw = style.get("fontWeight", 400)
|
||||
align_h = style.get("textAlignHorizontal", "LEFT").lower()
|
||||
align_v = style.get("textAlignVertical", "TOP")
|
||||
lh_px = style.get("lineHeightPx", 0)
|
||||
lh = lh_px * scale if lh_px else fs * 1.5
|
||||
ls = style.get("letterSpacing", 0) * scale
|
||||
|
||||
# 텍스트 fill 처리
|
||||
has_gradient = any(
|
||||
"GRADIENT" in f.get("type", "") for f in visible_fills
|
||||
)
|
||||
if has_gradient:
|
||||
bg, _ = get_fill_css(
|
||||
[f for f in visible_fills if "GRADIENT" in f.get("type", "")]
|
||||
)
|
||||
text_style = (
|
||||
f"background:{bg};"
|
||||
f"-webkit-background-clip:text;"
|
||||
f"-webkit-text-fill-color:transparent;"
|
||||
)
|
||||
else:
|
||||
_, c = get_fill_css(visible_fills)
|
||||
text_style = f"color:{c or '#000'};"
|
||||
|
||||
lh_css = f"line-height:{lh:.1f}px;"
|
||||
ls_css = f"letter-spacing:{ls:.1f}px;" if ls > 0.1 else ""
|
||||
align_css = f"text-align:{align_h};" if align_h != "left" else ""
|
||||
valign_css = (
|
||||
"display:flex;align-items:center;" if align_v == "CENTER" else ""
|
||||
)
|
||||
|
||||
text_html = chars.replace("\n", "<br>")
|
||||
return (
|
||||
f'<div style="position:absolute;left:{x:.1f}px;top:{y:.1f}px;'
|
||||
f"width:{w:.1f}px;height:{h:.1f}px;"
|
||||
f"font-size:{fs:.1f}px;font-weight:{fw};"
|
||||
f"{text_style}{lh_css}{ls_css}{align_css}{valign_css}"
|
||||
f'overflow:hidden;">{text_html}</div>'
|
||||
)
|
||||
|
||||
elif ntype in ("RECTANGLE", "VECTOR"):
|
||||
bg, _ = get_fill_css(visible_fills)
|
||||
if not bg:
|
||||
return ""
|
||||
if bg == "__IMAGE__":
|
||||
# 이미지 placeholder
|
||||
return (
|
||||
f'<div style="position:absolute;left:{x:.1f}px;top:{y:.1f}px;'
|
||||
f"width:{w:.1f}px;height:{h:.1f}px;"
|
||||
f'background:#ddd;border:1px solid #ccc;"></div>'
|
||||
)
|
||||
|
||||
cr = node.get("cornerRadius", 0)
|
||||
cr_css = f"border-radius:{cr * scale:.1f}px;" if cr > 0 else ""
|
||||
|
||||
strokes = node.get("strokes", [])
|
||||
stroke_css = ""
|
||||
if strokes:
|
||||
for s in strokes:
|
||||
if not s.get("visible", True):
|
||||
continue
|
||||
sc = s.get("color", {})
|
||||
sr = int(sc.get("r", 0) * 255)
|
||||
sg = int(sc.get("g", 0) * 255)
|
||||
sb = int(sc.get("b", 0) * 255)
|
||||
sw = node.get("strokeWeight", 1) * scale
|
||||
stroke_css = (
|
||||
f"border:{sw:.1f}px solid #{sr:02x}{sg:02x}{sb:02x};"
|
||||
)
|
||||
break
|
||||
|
||||
return (
|
||||
f'<div style="position:absolute;left:{x:.1f}px;top:{y:.1f}px;'
|
||||
f"width:{w:.1f}px;height:{h:.1f}px;"
|
||||
f"background:{bg};{cr_css}{stroke_css}"
|
||||
f'"></div>'
|
||||
)
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
def frame_to_html(frame: dict, target_width: int = 1280) -> str:
|
||||
"""프레임 전체를 HTML 문서로 변환."""
|
||||
bb = frame.get("absoluteBoundingBox", {})
|
||||
ox, oy = bb["x"], bb["y"]
|
||||
fw, fh = bb["width"], bb["height"]
|
||||
scale = target_width / fw
|
||||
out_h = int(fh * scale)
|
||||
|
||||
# 모든 리프 노드 수집 (재귀)
|
||||
elements: list[tuple[int, str]] = [] # (z-order, html)
|
||||
|
||||
def collect(node: dict, z: int = 0):
|
||||
ntype = node.get("type", "")
|
||||
children = node.get("children", [])
|
||||
|
||||
if ntype in ("GROUP", "FRAME", "CANVAS", "COMPONENT", "INSTANCE"):
|
||||
# 컨테이너는 자식만 순회
|
||||
for i, child in enumerate(children):
|
||||
collect(child, z + i)
|
||||
else:
|
||||
html = node_to_html(node, ox, oy, scale)
|
||||
if html:
|
||||
elements.append((z, html))
|
||||
for i, child in enumerate(children):
|
||||
collect(child, z + i)
|
||||
|
||||
collect(frame, 0)
|
||||
|
||||
# 프레임 배경
|
||||
frame_fills = frame.get("fills", [])
|
||||
frame_bg, _ = get_fill_css(
|
||||
[f for f in frame_fills if f.get("visible", True)]
|
||||
)
|
||||
frame_bg_css = f"background:{frame_bg};" if frame_bg else "background:#fff;"
|
||||
|
||||
# z-order 순으로 정렬 (rect 먼저, text 나중)
|
||||
rects = [(z, h) for z, h in elements if "font-size" not in h]
|
||||
texts = [(z, h) for z, h in elements if "font-size" in h]
|
||||
|
||||
parts = [
|
||||
f"""<!DOCTYPE html><html><head><meta charset="UTF-8">
|
||||
<style>
|
||||
*{{margin:0;padding:0;box-sizing:border-box;}}
|
||||
body{{background:#e5e5e5;padding:10px;font-family:'Pretendard Variable','Noto Sans KR',sans-serif;word-break:keep-all;}}
|
||||
</style></head><body>
|
||||
<div style="width:{target_width}px;height:{out_h}px;position:relative;{frame_bg_css}overflow:hidden;">"""
|
||||
]
|
||||
|
||||
# rect를 먼저 (배경), text를 나중 (전경)
|
||||
for _, h in sorted(rects, key=lambda x: x[0]):
|
||||
parts.append(h)
|
||||
for _, h in sorted(texts, key=lambda x: x[0]):
|
||||
parts.append(h)
|
||||
|
||||
parts.append("</div></body></html>")
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print(
|
||||
"Usage: python scripts/figma_to_html.py <figma_json> <output_dir>"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
figma_json = Path(sys.argv[1])
|
||||
output_dir = Path(sys.argv[2])
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
data = json.loads(figma_json.read_text(encoding="utf-8"))
|
||||
doc = data.get("document", {})
|
||||
pages = doc.get("children", [])
|
||||
|
||||
for page in pages:
|
||||
for i, frame in enumerate(page.get("children", [])):
|
||||
if frame.get("type") not in ("FRAME", "COMPONENT"):
|
||||
continue
|
||||
name = frame.get("name", f"frame_{i}")
|
||||
# 파일명 정리
|
||||
safe_name = (
|
||||
name.replace(" ", "_")
|
||||
.replace("/", "_")
|
||||
.replace("\\", "_")
|
||||
)
|
||||
html = frame_to_html(frame)
|
||||
out_path = output_dir / f"{safe_name}.html"
|
||||
out_path.write_text(html, encoding="utf-8")
|
||||
bb = frame.get("absoluteBoundingBox", {})
|
||||
print(
|
||||
f" {safe_name}.html"
|
||||
f" ({bb.get('width', 0):.0f}x{bb.get('height', 0):.0f}"
|
||||
f" -> 1280px)"
|
||||
)
|
||||
|
||||
print(f"\n완료: {output_dir}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
static/figma-assets/arrow_asis_tobe.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
static/figma-assets/badge_policy.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
static/figma-assets/badge_solution.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
static/figma-assets/box_policy_container.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
static/figma-assets/box_solution_cards.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
@@ -34,6 +34,14 @@
|
||||
--spacing-inner: 16px;
|
||||
--spacing-small: 8px;
|
||||
|
||||
/* Warm 테마 (Figma 2026-04) */
|
||||
--color-warm-brown: #5C3714;
|
||||
--color-dark-teal: #084C56;
|
||||
--color-teal: #227582;
|
||||
--color-forest: #548235;
|
||||
--color-beige: #E4D9C0;
|
||||
--color-warm-yellow: #FAEDCB;
|
||||
|
||||
/* 기타 */
|
||||
--radius: 6px;
|
||||
--border-width: 1px;
|
||||
|
||||
29
templates/blocks/BEPs/detail-button.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!-- 상세보기 버튼: 그라데이션 배경 + 둥근 모서리 -->
|
||||
<!--
|
||||
📋 detail-button
|
||||
─────────────────
|
||||
용도: 팝업 또는 상세 페이지로 이동하는 버튼
|
||||
슬롯: label (기본: "상세보기")
|
||||
Figma 원본: Frame 1171276068
|
||||
색상: 배경 그라데이션 #fff → #e3d8bf, 텍스트 #ffffff
|
||||
-->
|
||||
<div class="block-detail-btn">
|
||||
<span class="db-label">{{ label | default('상세보기') }}</span>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block-detail-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(90deg, #ffffff, #e3d8bf);
|
||||
border-radius: 7px;
|
||||
padding: 4px 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.db-label {
|
||||
font-size: var(--font-caption, 11px);
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
73
templates/blocks/BEPs/engn-solution-dfma.html
Normal file
@@ -0,0 +1,73 @@
|
||||
<!-- Engn. Solution / DfMA 설명 + 정책 달성 -->
|
||||
<!--
|
||||
📋 engn-solution-dfma
|
||||
─────────────────
|
||||
용도: Engn. Solution과 DfMA 개념 설명 + 정책 달성 연결
|
||||
슬롯:
|
||||
items[]: 항목 목록
|
||||
- title: 제목 (예: "Engn. Solution", "DfMA")
|
||||
- description: 설명 텍스트
|
||||
- color: 제목 색상 (예: "#217581")
|
||||
main_text: 하단 메인 텍스트
|
||||
policy_label: 정책 달성 라벨
|
||||
Figma 원본: Frame 1171276070
|
||||
-->
|
||||
<div class="block-engn">
|
||||
<div class="engn-items">
|
||||
{% for item in items %}
|
||||
<div class="engn-item">
|
||||
<div class="engn-title" style="color: {{ item.color | default('#217581') }}">{{ item.title }}</div>
|
||||
<div class="engn-desc">{{ item.description }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if main_text %}
|
||||
<div class="engn-main">{{ main_text }}</div>
|
||||
{% endif %}
|
||||
{% if policy_label %}
|
||||
<div class="engn-policy">{{ policy_label }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block-engn {
|
||||
padding: 16px 20px;
|
||||
}
|
||||
.engn-items {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.engn-item {
|
||||
flex: 1;
|
||||
}
|
||||
.engn-title {
|
||||
font-size: var(--font-subtitle, 16px);
|
||||
font-weight: 900;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.engn-desc {
|
||||
font-size: var(--font-caption, 12px);
|
||||
font-weight: 700;
|
||||
color: #217581;
|
||||
line-height: var(--line-height-ko, 1.7);
|
||||
}
|
||||
.engn-main {
|
||||
font-size: var(--font-body, 14px);
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
line-height: var(--line-height-ko, 1.7);
|
||||
text-align: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.engn-policy {
|
||||
background: #1a365d;
|
||||
color: #fff;
|
||||
font-size: var(--font-body, 14px);
|
||||
font-weight: 700;
|
||||
padding: 8px 20px;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
</style>
|
||||
25
templates/blocks/BEPs/image-placeholder.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<!-- 이미지 플레이스홀더: 이미지 슬롯만 있는 프레임 -->
|
||||
<!--
|
||||
📋 image-placeholder
|
||||
─────────────────
|
||||
용도: 이미지만 배치되는 프레임 (텍스트 없음)
|
||||
슬롯: src (이미지 경로), alt (대체 텍스트), width, height
|
||||
Figma 원본: Frame 1171276071 (949x275, 이미지만)
|
||||
-->
|
||||
<div class="block-img-ph">
|
||||
<img src="{{ src }}" alt="{{ alt | default('') }}" style="width:100%;height:100%;object-fit:contain;">
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block-img-ph {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.block-img-ph img {
|
||||
border-radius: var(--radius, 6px);
|
||||
}
|
||||
</style>
|
||||
87
templates/blocks/BEPs/policy-direction.html
Normal file
@@ -0,0 +1,87 @@
|
||||
<!-- 정책방향: 좌측 설명 + 우측 항목/이미지 -->
|
||||
<!--
|
||||
📋 policy-direction
|
||||
─────────────────
|
||||
용도: 인프라 건설산업의 정책방향 설명 (좌측 텍스트 + 우측 구조도)
|
||||
슬롯:
|
||||
title: 대제목 (예: "인프라 건설산업의 정책방향")
|
||||
description: 설명 텍스트
|
||||
sub_sections[]: 하위 섹션 목록
|
||||
- title: 섹션 제목
|
||||
- bullets[]: 불릿
|
||||
questions[]: 질문 목록 (좌하단)
|
||||
- text: 질문 텍스트
|
||||
Figma 원본: Frame 1171276067
|
||||
-->
|
||||
<div class="block-policy">
|
||||
<div class="pol-header">
|
||||
<div class="pol-title">{{ title }}</div>
|
||||
{% if description %}
|
||||
<div class="pol-desc">{{ description }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="pol-content">
|
||||
{% for section in sub_sections %}
|
||||
<div class="pol-section">
|
||||
<div class="pol-section-title">{{ section.title }}</div>
|
||||
{% for bullet in section.bullets %}
|
||||
<div class="pol-bullet">• {{ bullet }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if questions %}
|
||||
<div class="pol-questions">
|
||||
{% for q in questions %}
|
||||
<div class="pol-q">{{ q.text }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block-policy {
|
||||
padding: 16px 20px;
|
||||
}
|
||||
.pol-header {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.pol-title {
|
||||
font-size: var(--font-subtitle, 16px);
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.pol-desc {
|
||||
font-size: var(--font-caption, 12px);
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
line-height: var(--line-height-ko, 1.7);
|
||||
}
|
||||
.pol-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.pol-section-title {
|
||||
font-size: var(--font-body, 13px);
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.pol-bullet {
|
||||
font-size: var(--font-caption, 11px);
|
||||
color: #333;
|
||||
line-height: var(--line-height-ko, 1.7);
|
||||
padding-left: 8px;
|
||||
}
|
||||
.pol-questions {
|
||||
margin-top: 16px;
|
||||
border-top: 1px solid #ccc;
|
||||
padding-top: 12px;
|
||||
}
|
||||
.pol-q {
|
||||
font-size: var(--font-body, 13px);
|
||||
font-weight: 700;
|
||||
color: #c41e3a;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
187
templates/blocks/BEPs/process-product-2col.html
Normal file
@@ -0,0 +1,187 @@
|
||||
<!-- Process/Product 2단 비교: 좌측 과정의 혁신 + 우측 결과의 혁신 -->
|
||||
<!--
|
||||
📋 process-product-2col
|
||||
─────────────────
|
||||
용도: Process 혁신과 Product 변화를 좌우 2단으로 비교
|
||||
슬롯:
|
||||
left_title, right_title: 좌/우 제목
|
||||
left_sections[], right_sections[]: 섹션 목록 (title + bullets[])
|
||||
left_compare: As-is → To-be 비교 (선택, title + left_items[] + right_items[])
|
||||
Figma 원본: Frame 1171276073 (3848x1487 → 1280x495)
|
||||
색상 (Figma에서 추출):
|
||||
좌 배경: linear-gradient(180deg, #ffffff 46%, #39311e 100%)
|
||||
우 배경: linear-gradient(0deg, #296b55 0%, #ffffff 56%)
|
||||
좌 제목바: linear-gradient(270deg, #a4a096, #39311e)
|
||||
우 제목바: linear-gradient(0deg, #296b55, #022017)
|
||||
좌 제목텍스트: gradient(#296b55→#123328) + solid #3e3523
|
||||
우 제목텍스트: gradient(#296b55→#123328) + solid #225e4a
|
||||
좌 중제목: #5c3614, 16.6px/900
|
||||
우 중제목: #084c56, 16.6px/900
|
||||
본문: #000000, 13.3px/700, lineH=23.3px
|
||||
-->
|
||||
<div class="block-pp2">
|
||||
<div class="pp2-col pp2-left">
|
||||
<div class="pp2-header-bar pp2-header-bar--left">
|
||||
<span class="pp2-header-text pp2-header-text--left">{{ left_title }}</span>
|
||||
</div>
|
||||
<div class="pp2-body">
|
||||
{% if left_compare %}
|
||||
<div class="pp2-mid-title pp2-mid-title--left">{{ left_compare.title }}</div>
|
||||
<div class="pp2-compare">
|
||||
<div class="pp2-compare-col">
|
||||
{% for item in left_compare.left_items %}
|
||||
<div class="pp2-body-text">• {{ item }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="pp2-compare-arrow">
|
||||
<img src="{{ arrow_image | default('') }}" alt="→" class="pp2-arrow-img">
|
||||
</div>
|
||||
<div class="pp2-compare-col">
|
||||
{% for item in left_compare.right_items %}
|
||||
<div class="pp2-body-text">• {{ item }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for section in left_sections %}
|
||||
<div class="pp2-mid-title pp2-mid-title--left">{{ section.title }}</div>
|
||||
{% for bullet in section.bullets %}
|
||||
<div class="pp2-body-text">• {{ bullet }}</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pp2-col pp2-right">
|
||||
<div class="pp2-header-bar pp2-header-bar--right">
|
||||
<span class="pp2-header-text pp2-header-text--right">{{ right_title }}</span>
|
||||
</div>
|
||||
<div class="pp2-body">
|
||||
{% for section in right_sections %}
|
||||
<div class="pp2-mid-title pp2-mid-title--right">{{ section.title }}</div>
|
||||
{% for bullet in section.bullets %}
|
||||
<div class="pp2-body-text">• {{ bullet }}</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block-pp2 {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pp2-col {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 495px;
|
||||
}
|
||||
|
||||
/* 좌측 배경: Figma gradient 180deg #fff 46% → #39311e 100% */
|
||||
.pp2-left {
|
||||
background: linear-gradient(180deg, #ffffff 46%, #39311e 100%);
|
||||
}
|
||||
|
||||
/* 우측 배경: Figma gradient 0deg #296b55 0% → #fff 56% */
|
||||
.pp2-right {
|
||||
background: linear-gradient(0deg, #296b55 0%, #ffffff 56%);
|
||||
}
|
||||
|
||||
/* 제목 바: height=47.2px, top=20.6px → padding-top으로 처리 */
|
||||
.pp2-header-bar {
|
||||
height: 47px;
|
||||
margin-top: 21px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 좌 제목바: 우측 둥글게, 그라데이션 우→좌 */
|
||||
.pp2-header-bar--left {
|
||||
background: linear-gradient(270deg, #a4a096 0%, #39311e 100%);
|
||||
border-radius: 0 24px 24px 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 우 제목바: 좌측 둥글게, 그라데이션 좌→우 */
|
||||
.pp2-header-bar--right {
|
||||
background: linear-gradient(90deg, #296b55 0%, #022017 100%);
|
||||
border-radius: 24px 0 0 24px;
|
||||
padding-left: 71px;
|
||||
}
|
||||
|
||||
/* 제목 텍스트: 23.3px/900, letterSpacing=1.2px */
|
||||
.pp2-header-text {
|
||||
font-size: 23.3px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 1.2px;
|
||||
line-height: 33.7px;
|
||||
}
|
||||
|
||||
/* 좌 제목: Figma solid fill #3e3523 */
|
||||
.pp2-header-text--left {
|
||||
color: #3e3523;
|
||||
}
|
||||
|
||||
/* 우 제목: Figma solid fill #225e4a */
|
||||
.pp2-header-text--right {
|
||||
color: #225e4a;
|
||||
}
|
||||
|
||||
/* 본문 영역 */
|
||||
.pp2-body {
|
||||
padding: 10px 27px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 중제목: 16.6px/900, lineH=31.6px */
|
||||
.pp2-mid-title {
|
||||
font-size: 16.6px;
|
||||
font-weight: 900;
|
||||
line-height: 31.6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.pp2-mid-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.pp2-mid-title--left {
|
||||
color: #5c3614;
|
||||
}
|
||||
.pp2-mid-title--right {
|
||||
color: #084c56;
|
||||
}
|
||||
|
||||
/* 본문: 13.3px/700, lineH=23.3px */
|
||||
.pp2-body-text {
|
||||
font-size: 13.3px;
|
||||
font-weight: 700;
|
||||
color: #000000;
|
||||
line-height: 23.3px;
|
||||
}
|
||||
|
||||
/* As-is → To-be 비교 */
|
||||
.pp2-compare {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.pp2-compare-col {
|
||||
flex: 1;
|
||||
}
|
||||
.pp2-compare-arrow {
|
||||
flex-shrink: 0;
|
||||
width: 84px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.pp2-arrow-img {
|
||||
width: 84px;
|
||||
height: 30px;
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
189
templates/blocks/BEPs/process-product-2col.svg
Normal file
|
After Width: | Height: | Size: 6.0 MiB |
198
templates/blocks/BEPs/process-product-2col_svg_test.html
Normal file
141
templates/blocks/BEPs/process-product-2col_test.html
Normal file
@@ -0,0 +1,141 @@
|
||||
<!DOCTYPE html><html><head><meta charset="UTF-8">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box;}
|
||||
body{background:#e5e5e5;padding:10px;font-family:'Pretendard Variable','Noto Sans KR',sans-serif;word-break:keep-all;}
|
||||
|
||||
.block-pp2 {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
width: 1280px;
|
||||
height: 495px;
|
||||
}
|
||||
.pp2-col {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.pp2-left {
|
||||
background: linear-gradient(180deg, #ffffff 46%, #39311e 100%);
|
||||
}
|
||||
.pp2-right {
|
||||
background: linear-gradient(0deg, #296b55 0%, #ffffff 56%);
|
||||
}
|
||||
.pp2-header-bar {
|
||||
height: 47px;
|
||||
margin-top: 21px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.pp2-header-bar--left {
|
||||
background: linear-gradient(270deg, #a4a096 0%, #39311e 100%);
|
||||
border-radius: 0 24px 24px 0;
|
||||
justify-content: center;
|
||||
}
|
||||
.pp2-header-bar--right {
|
||||
background: linear-gradient(90deg, #296b55 0%, #022017 100%);
|
||||
border-radius: 24px 0 0 24px;
|
||||
padding-left: 71px;
|
||||
}
|
||||
.pp2-header-text {
|
||||
font-size: 23.3px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 1.2px;
|
||||
line-height: 33.7px;
|
||||
}
|
||||
.pp2-header-text--left {
|
||||
color: #3e3523;
|
||||
}
|
||||
.pp2-header-text--right {
|
||||
color: #225e4a;
|
||||
}
|
||||
.pp2-body {
|
||||
padding: 10px 27px;
|
||||
flex: 1;
|
||||
}
|
||||
.pp2-mid-title {
|
||||
font-size: 16.6px;
|
||||
font-weight: 900;
|
||||
line-height: 31.6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.pp2-mid-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.pp2-mid-title--left {
|
||||
color: #5c3614;
|
||||
}
|
||||
.pp2-mid-title--right {
|
||||
color: #084c56;
|
||||
}
|
||||
.pp2-body-text {
|
||||
font-size: 13.3px;
|
||||
font-weight: 700;
|
||||
color: #000000;
|
||||
line-height: 23.3px;
|
||||
}
|
||||
.pp2-compare {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.pp2-compare-col {
|
||||
flex: 1;
|
||||
}
|
||||
.pp2-compare-arrow {
|
||||
flex-shrink: 0;
|
||||
width: 84px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
color: #5c3614;
|
||||
}
|
||||
</style></head><body>
|
||||
|
||||
<div class="block-pp2">
|
||||
<div class="pp2-col pp2-left">
|
||||
<div class="pp2-header-bar pp2-header-bar--left">
|
||||
<span class="pp2-header-text pp2-header-text--left">과정 (Process)의 혁신</span>
|
||||
</div>
|
||||
<div class="pp2-body">
|
||||
<div class="pp2-mid-title pp2-mid-title--left">Analogue 개념 기반 업무의 Digital Transformation</div>
|
||||
<div class="pp2-compare">
|
||||
<div class="pp2-compare-col">
|
||||
<div class="pp2-body-text">• 개념, 도서, 행정 절차 중심</div>
|
||||
<div class="pp2-body-text">• 2D 도면, 전문가, 규정</div>
|
||||
<div class="pp2-body-text">• 업무 구분(단절), 책임</div>
|
||||
</div>
|
||||
<div class="pp2-compare-arrow">➠</div>
|
||||
<div class="pp2-compare-col">
|
||||
<div class="pp2-body-text">• 시각화된 목적물, 소통, 투명성 중심</div>
|
||||
<div class="pp2-body-text">• 3D 모델, 참여자, 실체</div>
|
||||
<div class="pp2-body-text">• 협업(융·복합), 창의성</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pp2-mid-title pp2-mid-title--left">위치기반의 3D 모델을 사용하는 Process 혁신</div>
|
||||
<div class="pp2-body-text">• 위치기반(지리적, 지형, 지반상태 포함)의 GIS와 3D 모델(형상, 내용속성) 기반의 건설 정보를 포함하는 BIM의 연계를 통한 업무 프로세스의 혁신</div>
|
||||
<div class="pp2-mid-title pp2-mid-title--left">사용자 중심의 Solution(S/W) 제공</div>
|
||||
<div class="pp2-body-text">• 인프라 건설산업의 1차적인 Process 혁신은 등고선 중심의 지형도가 아닌 속성이 포함된 수치지형도와 본태 측량에서 획득한 3D 지반모델 구축 필수</div>
|
||||
<div class="pp2-body-text">• 설계와 시공에 관련된 기술을 정리하고 디지털화하여 S/W로 기술 축적</div>
|
||||
<div class="pp2-body-text">• 서로 다른 S/W로 작성되어 분절화된 Analogue 방식의 성과물과 정보물을 연계가 가능하도록 설계, 시공 Solution 제공</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pp2-col pp2-right">
|
||||
<div class="pp2-header-bar pp2-header-bar--right">
|
||||
<span class="pp2-header-text pp2-header-text--right">결과 (Products)의 혁신</span>
|
||||
</div>
|
||||
<div class="pp2-body">
|
||||
<div class="pp2-mid-title pp2-mid-title--right">Copy & Paste로 인한 하향 평준화된 기존 성과품의 품질 향상</div>
|
||||
<div class="pp2-body-text">• 과거 수작업으로 시행하면서 발생하던 오류 등의 최소화</div>
|
||||
<div class="pp2-body-text">• 정확한 Data에 기반한 계획과 개선된 높은 품질의 성과물</div>
|
||||
<div class="pp2-mid-title pp2-mid-title--right">Analogue 기반 도서 외 Digital 기반 정보물 추가</div>
|
||||
<div class="pp2-body-text">• 규정에만 의존한 도면, 수량, 계산서, 시방서 등의 성과물에 3D 모델, 시뮬레이션 등의 Digital 기반 정보물(Information Data and Products)이 추가</div>
|
||||
<div class="pp2-mid-title pp2-mid-title--right">Solution을 이용한 업무효율화(사용자 편의, 협업 및 의사소통 강화 등)</div>
|
||||
<div class="pp2-body-text">• 디지털 기반 성과물인 Graphic 중심의 3D 모델, 시뮬레이션을 제대로 활용하기 위해서는 기존의 낮은 수준이 아니라 공학용 사이니지(H/W) 시스템이 필수로 갖춰야만 함</div>
|
||||
<div class="pp2-body-text">• Engn. Solution을 통해 프로젝트에 관한 이슈를 함께 검토하고 논의하고 다양한 건설단계별 정보를 디지털 데이터로 저장하여 건설의 전 과정을 통합관리</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body></html>
|
||||
76
templates/blocks/BEPs/solution-goals.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<!-- Solution 제작 목표: 5개 목표 카드 + 중앙 텍스트 -->
|
||||
<!--
|
||||
📋 solution-goals
|
||||
─────────────────
|
||||
용도: Solution 제작의 5가지 핵심 목표 (Easy, Convenient, Quality, Cost, Speed)
|
||||
슬롯:
|
||||
main_text: 중앙 메인 텍스트
|
||||
goals[]: 목표 카드 목록
|
||||
- en: 영문 라벨 (예: "Easy Like Breath")
|
||||
- ko: 한글 라벨 (예: "(쉬운 이해)")
|
||||
- icon: 아이콘 이미지 경로 (선택)
|
||||
Figma 원본: Frame 1171276072
|
||||
색상: 제목 #ffffff on dark, 본문 #000000, 영문 gradient text
|
||||
-->
|
||||
<div class="block-sol-goals">
|
||||
<div class="sg-title">Solution 제작 목표</div>
|
||||
<div class="sg-main">{{ main_text }}</div>
|
||||
<div class="sg-grid" style="--sg-count: {{ goals|length }}">
|
||||
{% for g in goals %}
|
||||
<div class="sg-card">
|
||||
{% if g.icon %}<img class="sg-icon" src="{{ g.icon }}" alt="{{ g.en }}">{% endif %}
|
||||
<div class="sg-en">{{ g.en }}</div>
|
||||
<div class="sg-ko">{{ g.ko }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block-sol-goals {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.sg-title {
|
||||
font-size: var(--font-subtitle, 16px);
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
background: #1a365d;
|
||||
display: inline-block;
|
||||
padding: 6px 20px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.sg-main {
|
||||
font-size: var(--font-body, 14px);
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
line-height: var(--line-height-ko, 1.7);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.sg-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--sg-count, 5), 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.sg-card {
|
||||
text-align: center;
|
||||
}
|
||||
.sg-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
object-fit: contain;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.sg-en {
|
||||
font-size: 13px;
|
||||
font-weight: 900;
|
||||
color: #1a365d;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.sg-ko {
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: #000;
|
||||
}
|
||||
</style>
|
||||
106
templates/blocks/cards/compare-2col-badge.html
Normal file
@@ -0,0 +1,106 @@
|
||||
<!-- 배지 헤더 2열 비교: 3D 리본 탭 + 좌/우 비교 + 선택적 하단 문구 -->
|
||||
<!--
|
||||
📋 compare-2col-badge
|
||||
─────────────────
|
||||
용도: 두 개념/전략/기술을 나란히 비교. 3D 리본 탭이 컨테이너 위에 걸침.
|
||||
슬롯: badge_title, left_title, left_body, right_title, right_body, statement(선택)
|
||||
Figma 원본: Frame 3 (정책 달성 — 틸 리본 + 흰 둥근 컨테이너 + 2열 비교)
|
||||
수학적 계산:
|
||||
badge 517x88 at y=929, box at y=947, frame_w=1807
|
||||
scale = 1200/1807 = 0.6641
|
||||
ribbon: 343x58px, fold_offset: 12px, ribbon_below_fold: 46px
|
||||
box padding-top: 52px
|
||||
-->
|
||||
<div class="block-c2b">
|
||||
<div class="c2b-main">
|
||||
{% if badge_title %}
|
||||
<div class="c2b-ribbon">
|
||||
<img src="{{ ribbon_image | default('/static/figma-assets/badge_policy.png') }}" class="c2b-ribbon-img" alt="">
|
||||
<span class="c2b-ribbon-text">{{ badge_title }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="c2b-container">
|
||||
<div class="c2b-col c2b-left">
|
||||
<div class="c2b-title" style="color: {{ left_color | default('var(--color-teal, #227582)') }}">
|
||||
{{ left_title }}
|
||||
</div>
|
||||
<div class="c2b-body" style="color: {{ left_color | default('var(--color-teal, #227582)') }}">
|
||||
{{ left_body }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="c2b-divider"></div>
|
||||
|
||||
<div class="c2b-col c2b-right">
|
||||
<div class="c2b-title" style="color: {{ right_color | default('var(--color-teal, #227582)') }}">
|
||||
{{ right_title }}
|
||||
</div>
|
||||
<div class="c2b-body" style="color: {{ right_color | default('var(--color-teal, #227582)') }}">
|
||||
{{ right_body }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if statement %}
|
||||
<div class="c2b-statement">{{ statement }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block-c2b { display: flex; flex-direction: column; width: 100%; }
|
||||
|
||||
/*
|
||||
리본+박스 수학적 계산 (Figma 원본):
|
||||
badge 517x88 at y=929, box at y=947
|
||||
접힘선 = 18px = 이미지 높이의 20.5%
|
||||
scale(1200/1807) → ribbon 343x58, fold top에서 12px, 아래 46px
|
||||
*/
|
||||
.c2b-main { position: relative; }
|
||||
|
||||
/* ── 3D 리본 배지 ── */
|
||||
.c2b-ribbon {
|
||||
position: absolute;
|
||||
top: -12px; /* 접힘선이 박스 top border와 정확히 일치 */
|
||||
left: 50%; transform: translateX(-50%);
|
||||
z-index: 2; width: 343px; text-align: center;
|
||||
}
|
||||
.c2b-ribbon-img { width: 100%; height: auto; display: block; }
|
||||
.c2b-ribbon-text {
|
||||
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
||||
font-size: 22px; font-weight: 700; color: #fff;
|
||||
letter-spacing: 0.03em; white-space: nowrap;
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
/* ── 틸 테두리 흰 박스 ── */
|
||||
.c2b-container {
|
||||
display: grid; grid-template-columns: 1fr auto 1fr;
|
||||
background: #fff;
|
||||
border: 2px solid #5a9ea8;
|
||||
border-radius: 20px;
|
||||
padding: 52px 40px 36px; /* top: ribbon_below(46) + 6px 여유 */
|
||||
}
|
||||
.c2b-divider { width: 1px; background: var(--color-border, #e2e8f0); margin: 0 32px; align-self: stretch; }
|
||||
.c2b-col { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
|
||||
.c2b-title { font-size: 36px; font-weight: var(--weight-black, 900); line-height: 1.2; word-break: keep-all; }
|
||||
.c2b-body { font-size: 22px; font-weight: var(--weight-bold, 700); line-height: 1.9; white-space: pre-line; word-break: keep-all; }
|
||||
|
||||
/* ── Statement ── */
|
||||
.c2b-statement {
|
||||
margin-top: 24px; text-align: center; font-size: 28px; font-weight: var(--weight-bold, 700);
|
||||
color: var(--color-primary, #1e293b); line-height: 1.6; word-break: keep-all; padding: 0 20px;
|
||||
}
|
||||
.c2b-statement strong { font-weight: 900; font-size: 1.15em; }
|
||||
.c2b-statement em { color: #dc2626; font-style: normal; font-weight: 900; font-size: 1.2em; }
|
||||
|
||||
/* ── Responsive: 좁은 컨테이너 ── */
|
||||
@container (max-width: 500px) {
|
||||
.c2b-container { grid-template-columns: 1fr; gap: 20px; }
|
||||
.c2b-divider { width: 100%; height: 1px; margin: 0; }
|
||||
.c2b-title { font-size: 24px; }
|
||||
.c2b-body { font-size: 16px; }
|
||||
.c2b-statement { font-size: 20px; }
|
||||
}
|
||||
</style>
|
||||
113
templates/blocks/cards/compare-detail-gradient.html
Normal file
@@ -0,0 +1,113 @@
|
||||
<!-- 그라디언트 상세 2열 비교: 비대칭 라운드 헤더 + 행 공유 + As-Is/To-Be -->
|
||||
<!--
|
||||
📋 compare-detail-gradient
|
||||
─────────────────
|
||||
용도: 두 카테고리의 상세 비교 (각각 N개 하위 섹션, 행 공유 정렬)
|
||||
슬롯: left_header, right_header, sections[] (각 row에 left + right)
|
||||
Figma 원본: Frame 4 (과정의 혁신 vs 결과의 혁신)
|
||||
디자인:
|
||||
- 좌측 헤더: 우측 라운드 + 텍스트 우정렬 (warm 그라디언트)
|
||||
- 우측 헤더: 좌측 라운드 + 텍스트 좌정렬 (teal 그라디언트)
|
||||
- CSS Grid 2열×N행: 좌/우 섹션 제목이 같은 Y선에 정렬
|
||||
- 첫 번째 섹션: As-Is → 화살표 → To-Be 수평 배치 가능
|
||||
수학적 계산:
|
||||
좌/우 섹션 Y좌표 — Row1: 1166/1166(정렬), Row2: 1529/1467(62px차→Grid해결), Row3: 1845/1845(정렬)
|
||||
-->
|
||||
<div class="block-cdg">
|
||||
<!-- Row 0: Headers -->
|
||||
<div class="cdg-header cdg-header-warm">
|
||||
<span class="cdg-header-text">{{ left_header }}</span>
|
||||
</div>
|
||||
<div class="cdg-header cdg-header-teal">
|
||||
<span class="cdg-header-text">{{ right_header }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Rows: sections (행 공유로 좌/우 Y선 자동 정렬) -->
|
||||
{% for row in sections %}
|
||||
<div class="cdg-cell cdg-cell-warm">
|
||||
<div class="cdg-sec-title cdg-title-warm">{{ row.left.title }}</div>
|
||||
{% if row.left.asis is defined %}
|
||||
<div class="cdg-asis-tobe">
|
||||
<div class="cdg-asis">
|
||||
{% for b in row.left.asis %}<div class="cdg-bullet">{{ b }}</div>{% endfor %}
|
||||
</div>
|
||||
<img src="{{ arrow_image | default('../../../static/figma-assets/arrow_asis_tobe.png') }}" class="cdg-arrow" alt="→">
|
||||
<div class="cdg-tobe">
|
||||
{% for b in row.left.tobe %}<div class="cdg-bullet">{{ b }}</div>{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% elif row.left.bullets is defined %}
|
||||
<div class="cdg-sec-body">
|
||||
{% for b in row.left.bullets %}<div class="cdg-bullet">{{ b }}</div>{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="cdg-sec-body">{{ row.left.body }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="cdg-cell cdg-cell-teal">
|
||||
<div class="cdg-sec-title cdg-title-teal">{{ row.right.title }}</div>
|
||||
{% if row.right.bullets is defined %}
|
||||
<div class="cdg-sec-body">
|
||||
{% for b in row.right.bullets %}<div class="cdg-bullet">{{ b }}</div>{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="cdg-sec-body">{{ row.right.body }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/*
|
||||
CSS Grid: 2열 × (1+N)행
|
||||
행 공유 → 좌/우 섹션 제목이 같은 Y선에 자동 정렬
|
||||
Figma Row2 차이: 좌 y=1529, 우 y=1467 (62px) → Grid가 해결
|
||||
*/
|
||||
.block-cdg {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── Headers (비대칭 라운드) ── */
|
||||
.cdg-header { padding: 8px 20px; display: flex; min-height: 36px; align-items: center; }
|
||||
.cdg-header-text { font-size: 15px; font-weight: var(--weight-black, 900); color: #000; line-height: 1.3; }
|
||||
.cdg-header-warm {
|
||||
background: linear-gradient(90deg, rgba(165,161,150,0.15), rgba(57,50,30,0.85));
|
||||
border-radius: 0 28px 28px 0; justify-content: flex-end; text-align: right; margin-right: 4px;
|
||||
}
|
||||
.cdg-header-teal {
|
||||
background: linear-gradient(90deg, rgba(3,33,24,0.85), rgba(41,107,85,0.15));
|
||||
border-radius: 28px 0 0 28px; justify-content: flex-start; text-align: left; margin-left: 4px;
|
||||
}
|
||||
|
||||
/* ── Grid Cells (행 공유 → 좌우 Y선 정렬) ── */
|
||||
.cdg-cell {
|
||||
padding: 8px 14px;
|
||||
display: flex; flex-direction: column; gap: 4px;
|
||||
align-self: start;
|
||||
}
|
||||
.cdg-cell-warm { background: linear-gradient(180deg, rgba(255,255,255,0.4), rgba(57,50,30,0.08)); }
|
||||
.cdg-cell-teal { background: linear-gradient(180deg, rgba(41,107,85,0.06), rgba(255,255,255,0.4)); }
|
||||
|
||||
/* ── Section Title & Body ── */
|
||||
.cdg-sec-title { font-size: 12px; font-weight: var(--weight-black, 900); line-height: 1.4; word-break: keep-all; margin-bottom: 2px; }
|
||||
.cdg-title-warm { color: var(--color-warm-brown, #5C3714); }
|
||||
.cdg-title-teal { color: var(--color-dark-teal, #084C56); }
|
||||
.cdg-sec-body { padding-left: 8px; }
|
||||
|
||||
/* ── Bullets ── */
|
||||
.cdg-bullet {
|
||||
position: relative; padding-left: 14px;
|
||||
font-size: 11px; font-weight: var(--weight-bold, 700); color: #1a1a1a;
|
||||
line-height: 1.7; word-break: keep-all;
|
||||
}
|
||||
.cdg-bullet::before { content: '•'; position: absolute; left: 0; color: #666; }
|
||||
|
||||
/* ── As-Is → To-Be 수평 레이아웃 ── */
|
||||
.cdg-asis-tobe { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
|
||||
.cdg-asis, .cdg-tobe { flex: 1; padding-left: 8px; }
|
||||
.cdg-arrow { width: 60px; height: auto; flex-shrink: 0; opacity: 0.7; }
|
||||
</style>
|
||||
98
templates/blocks/cards/hero-icon-cards.html
Normal file
@@ -0,0 +1,98 @@
|
||||
<!-- 히어로 문구 + 아이콘 카드: 큰 선언문 + 3D 리본 배지 + N열 아이콘 카드 -->
|
||||
<!--
|
||||
📋 hero-icon-cards
|
||||
─────────────────
|
||||
용도: 핵심 목표/가치를 선언하고 N개 키워드 카드로 시각화
|
||||
슬롯: statement, badge_title, cards[] (icon, title, subtitle)
|
||||
Figma 원본: Frame 2 (Solution 제작 목표 — 다크 테마, 빨간 리본+테두리, 5열 카드)
|
||||
수학적 계산:
|
||||
badge 508x94 at y=1431, box at y=1449, frame_w=1808
|
||||
scale = 1200/1808 = 0.6637
|
||||
ribbon: 337x62px, fold_offset: 12px, ribbon_below_fold: 50px
|
||||
box padding-top: 56px
|
||||
-->
|
||||
<div class="block-hic">
|
||||
{% if statement %}
|
||||
<div class="hic-statement">{{ statement }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="hic-card-area">
|
||||
{% if badge_title %}
|
||||
<div class="hic-ribbon">
|
||||
<img src="{{ ribbon_image | default('/static/figma-assets/badge_solution.png') }}" class="hic-ribbon-img" alt="">
|
||||
<span class="hic-ribbon-text">{{ badge_title }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="hic-box">
|
||||
<div class="hic-cards">
|
||||
{% for card in cards %}
|
||||
<div class="hic-card">
|
||||
{% if card.icon %}<div class="hic-icon">{{ card.icon }}</div>{% endif %}
|
||||
<div class="hic-title">{{ card.title }}</div>
|
||||
{% if card.subtitle %}<div class="hic-sub">({{ card.subtitle }})</div>{% endif %}
|
||||
</div>
|
||||
{% if not loop.last %}<div class="hic-sep"></div>{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block-hic {
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
width: 100%; background: #0a0a0a; border-radius: 16px;
|
||||
padding: 36px 32px 28px; overflow: visible;
|
||||
}
|
||||
|
||||
/* ── Hero Statement ── */
|
||||
.hic-statement {
|
||||
text-align: center; font-size: 28px; font-weight: var(--weight-bold, 700);
|
||||
color: #fff; line-height: 1.6; word-break: keep-all;
|
||||
margin-bottom: 28px; padding: 0 24px;
|
||||
}
|
||||
.hic-statement strong { font-weight: 900; font-size: 1.1em; }
|
||||
.hic-statement em { color: #ef4444; font-style: normal; font-weight: 900; font-size: 1.15em; }
|
||||
|
||||
/*
|
||||
리본+박스 수학적 계산 (Figma 원본):
|
||||
badge 508x94 at y=1431, box at y=1449
|
||||
접힘선 = 18px = 이미지 높이의 19.1%
|
||||
scale(1200/1808) → ribbon 337x62, fold top에서 12px, 아래 50px
|
||||
*/
|
||||
.hic-card-area { position: relative; width: 100%; margin-top: 20px; }
|
||||
|
||||
.hic-ribbon {
|
||||
position: absolute;
|
||||
top: -12px; /* 접힘선이 박스 top border와 정확히 일치 */
|
||||
left: 50%; transform: translateX(-50%);
|
||||
z-index: 2; width: 337px; text-align: center;
|
||||
}
|
||||
.hic-ribbon-img { width: 100%; height: auto; display: block; }
|
||||
.hic-ribbon-text {
|
||||
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
||||
font-size: 20px; font-weight: 700; color: #fff;
|
||||
letter-spacing: 0.03em; white-space: nowrap;
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
/* 빨간 테두리 흰 박스 */
|
||||
.hic-box {
|
||||
position: relative; background: #fff;
|
||||
border: 6px solid #9b1b1b; border-radius: 20px;
|
||||
padding: 56px 8px 8px; /* top: ribbon_below(50) + 6px 여유 */
|
||||
}
|
||||
|
||||
.hic-cards { display: flex; align-items: stretch; width: 100%; }
|
||||
.hic-card {
|
||||
flex: 1; display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center; text-align: center;
|
||||
padding: 16px 12px 20px; gap: 8px;
|
||||
}
|
||||
.hic-sep { width: 1px; background: #e0e0e0; align-self: stretch; margin: 8px 0; }
|
||||
.hic-icon { font-size: 48px; line-height: 1; margin-bottom: 8px; }
|
||||
.hic-icon img { width: 64px; height: 64px; object-fit: contain; }
|
||||
.hic-title { font-size: 20px; font-weight: var(--weight-black, 900); color: #1a1a1a; line-height: 1.3; }
|
||||
.hic-sub { font-size: 15px; font-weight: var(--weight-medium, 500); color: #666; }
|
||||
</style>
|
||||
111
templates/blocks/cards/hero-icon-cards_1.html
Normal file
@@ -0,0 +1,111 @@
|
||||
<!-- 히어로 카드 변형1: 3D 리본 배지 + N열 카드 (제목+중제목+불릿 계층) -->
|
||||
<!--
|
||||
📋 hero-icon-cards_1
|
||||
─────────────────
|
||||
용도: 핵심 요건/목표를 N열 카드로 시각화. 각 카드에 제목+중제목+불릿 계층.
|
||||
슬롯: statement(선택), badge_title, cards[] (title, sections[{title, bullets[]}])
|
||||
변형 원본: hero-icon-cards
|
||||
변경점: icon/title/subtitle → sections[{title, bullets[]}] 계층으로 변경
|
||||
수학적 계산: hero-icon-cards와 동일
|
||||
badge 508x94 at y=1431, box at y=1449, frame_w=1808
|
||||
scale = 1200/1808 = 0.6637
|
||||
ribbon: 337x62px, fold_offset: 12px, ribbon_below_fold: 50px
|
||||
box padding-top: 56px
|
||||
-->
|
||||
<div class="block-hic">
|
||||
{% if statement %}
|
||||
<div class="hic-statement">{{ statement }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="hic-card-area">
|
||||
{% if badge_title %}
|
||||
<div class="hic-ribbon">
|
||||
<img src="{{ ribbon_image | default('../../../static/figma-assets/badge_solution.png') }}" class="hic-ribbon-img" alt="">
|
||||
<span class="hic-ribbon-text">{{ badge_title }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="hic-box">
|
||||
<div class="hic-cards">
|
||||
{% for card in cards %}
|
||||
<div class="hic-card">
|
||||
<div class="hic-title">{{ card.title }}</div>
|
||||
{% for section in card.sections %}
|
||||
<div class="hic-sec-title">{{ section.title }}</div>
|
||||
{% for bullet in section.bullets %}
|
||||
<div class="hic-bullet">{{ bullet }}</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if not loop.last %}<div class="hic-sep"></div>{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block-hic {
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
width: 100%; overflow: visible;
|
||||
}
|
||||
|
||||
/* ── Hero Statement ── */
|
||||
.hic-statement {
|
||||
text-align: center; font-size: 16px; font-weight: var(--weight-bold, 700);
|
||||
color: #1e293b; line-height: 1.6; word-break: keep-all;
|
||||
margin-bottom: 8px; padding: 0 24px;
|
||||
}
|
||||
.hic-statement strong { font-weight: 900; font-size: 1.1em; }
|
||||
.hic-statement em { color: #ef4444; font-style: normal; font-weight: 900; font-size: 1.15em; }
|
||||
|
||||
/*
|
||||
리본+박스 수학적 계산 (Figma 원본):
|
||||
badge 508x94 at y=1431, box at y=1449
|
||||
접힘선 = 18px = 이미지 높이의 19.1%
|
||||
scale(1200/1808) → ribbon 337x62, fold top에서 12px, 아래 50px
|
||||
*/
|
||||
.hic-card-area { position: relative; width: 100%; }
|
||||
|
||||
.hic-ribbon {
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
left: 50%; transform: translateX(-50%);
|
||||
z-index: 2; width: 337px; text-align: center;
|
||||
}
|
||||
.hic-ribbon-img { width: 100%; height: auto; display: block; }
|
||||
.hic-ribbon-text {
|
||||
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
||||
font-size: 14px; font-weight: 700; color: #fff;
|
||||
letter-spacing: 0.03em; white-space: nowrap;
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
/* 얇은 테두리 박스 */
|
||||
.hic-box {
|
||||
position: relative; background: #fff;
|
||||
border: 1px solid #e2e8f0; border-radius: 8px;
|
||||
padding: 36px 8px 8px;
|
||||
}
|
||||
|
||||
.hic-cards { display: flex; align-items: stretch; width: 100%; }
|
||||
.hic-card {
|
||||
flex: 1; display: flex; flex-direction: column;
|
||||
padding: 8px 10px 10px; gap: 0;
|
||||
}
|
||||
.hic-sep { width: 1px; background: #e0e0e0; align-self: stretch; margin: 4px 0; }
|
||||
|
||||
/* ── 카드 내부 (변형: icon → 소제목+불릿 계층) ── */
|
||||
/* 중제목 (카드 헤더: 기술/사람/자연): 중앙 정렬 */
|
||||
.hic-title { font-size: 14px; font-weight: var(--weight-black, 900); color: #0d7377; line-height: 1.3; text-align: center; margin-bottom: 6px; }
|
||||
/* 소제목 (D1): 12px/700, 들여쓰기 1단 */
|
||||
.hic-sec-title { font-size: 12px; font-weight: 700; color: #1a1a1a; line-height: 1.4; margin-top: 6px; margin-bottom: 2px; padding-left: 8px; }
|
||||
.hic-sec-title:first-of-type { margin-top: 0; }
|
||||
/* 불릿 (D2): 11px/500, 들여쓰기 2단 */
|
||||
.hic-bullet {
|
||||
position: relative; padding-left: 24px;
|
||||
font-size: 11px; font-weight: 500; color: #334155;
|
||||
line-height: 1.7; word-break: keep-all;
|
||||
}
|
||||
.hic-bullet::before { content: '•'; position: absolute; left: 14px; color: #666; }
|
||||
</style>
|
||||
169
templates/blocks/cards/test-compare-2col-badge.html
Normal file
@@ -0,0 +1,169 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>compare-2col-badge 테스트</title>
|
||||
<link rel="stylesheet" href="../../../static/tokens.css">
|
||||
<link rel="stylesheet" href="../../../static/base.css">
|
||||
<style>
|
||||
body { background: #f0f0f0; padding: 40px; }
|
||||
h2 { margin: 40px 0 16px; font-size: 18px; color: #333; }
|
||||
.slide {
|
||||
width: 1280px; height: 720px;
|
||||
background: #fff; padding: 40px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
||||
margin-bottom: 40px;
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
font-family: 'Pretendard Variable', 'Noto Sans KR', sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>Test 1: Figma Frame 3 원본 (Engn. Solution vs DfMA)</h2>
|
||||
<div class="slide" style="grid-template-rows: auto 1fr auto;">
|
||||
|
||||
<!-- compare-2col-badge 블록 시작 -->
|
||||
<div class="block-c2b">
|
||||
<div class="c2b-badge">
|
||||
<span class="c2b-badge-text">정책 달성</span>
|
||||
</div>
|
||||
<div class="c2b-container">
|
||||
<div class="c2b-col c2b-left">
|
||||
<div class="c2b-title" style="color: var(--color-teal, #227582)">Engn. Solution</div>
|
||||
<div class="c2b-body" style="color: var(--color-teal, #227582)">목적 시설물에 대한 설계, 시공, 유지관리 정보를 고객이 쉽고 편리하게 사용하고, 편익이 발생하도록 제공하는 다양한 형태의 정보물과 이를 구현할 수 있는 S/W 및 H/W와 그 기술을 포함하는 서비스</div>
|
||||
</div>
|
||||
<div class="c2b-divider"></div>
|
||||
<div class="c2b-col c2b-right">
|
||||
<div class="c2b-title" style="color: var(--color-teal, #227582)">DfMA</div>
|
||||
<div class="c2b-body" style="color: var(--color-teal, #227582)">Design for Manufacture and Assembly는 공장 생산, 현장조립이 가능한 설계를 의미하며, 현장 중심의 건설에서 공장 생산 방식으로 전환하는 OSC(Off Site Construction) 시스템을 위한 핵심기술</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="c2b-statement">디지털전환은 <strong>사용자</strong>(발주자, 수급자, 엔지니어)가<br><strong>쉽고, 편하고, 편익</strong>이 있어야만 한다.</div>
|
||||
</div>
|
||||
<!-- 블록 끝 -->
|
||||
|
||||
</div>
|
||||
|
||||
<h2>Test 2: 다른 콘텐츠 (범용성 확인)</h2>
|
||||
<div class="slide" style="grid-template-rows: auto 1fr;">
|
||||
|
||||
<div class="block-c2b">
|
||||
<div class="c2b-badge">
|
||||
<span class="c2b-badge-text">디지털 전환 전략</span>
|
||||
</div>
|
||||
<div class="c2b-container">
|
||||
<div class="c2b-col c2b-left">
|
||||
<div class="c2b-title" style="color: var(--color-warm-brown, #5C3714)">As-Is (현재)</div>
|
||||
<div class="c2b-body" style="color: var(--color-warm-brown, #5C3714)">종이 도면 기반의 업무 프로세스
|
||||
수작업 검증과 품질 관리
|
||||
분절된 단계별 정보 전달
|
||||
개인 경험에 의존하는 의사결정</div>
|
||||
</div>
|
||||
<div class="c2b-divider"></div>
|
||||
<div class="c2b-col c2b-right">
|
||||
<div class="c2b-title" style="color: var(--color-dark-teal, #084C56)">To-Be (미래)</div>
|
||||
<div class="c2b-body" style="color: var(--color-dark-teal, #084C56)">3D 모델 기반의 통합 프로세스
|
||||
자동화된 검증과 품질 관리
|
||||
연속적인 디지털 정보 흐름
|
||||
데이터 기반의 의사결정 지원</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h2>Test 3: 짧은 콘텐츠 (최소 케이스)</h2>
|
||||
<div class="slide" style="grid-template-rows: auto 1fr;">
|
||||
|
||||
<div class="block-c2b">
|
||||
<div class="c2b-badge">
|
||||
<span class="c2b-badge-text">비교</span>
|
||||
</div>
|
||||
<div class="c2b-container">
|
||||
<div class="c2b-col c2b-left">
|
||||
<div class="c2b-title" style="color: var(--color-teal, #227582)">BIM</div>
|
||||
<div class="c2b-body" style="color: var(--color-teal, #227582)">건축정보모델링</div>
|
||||
</div>
|
||||
<div class="c2b-divider"></div>
|
||||
<div class="c2b-col c2b-right">
|
||||
<div class="c2b-title" style="color: var(--color-teal, #227582)">GIS</div>
|
||||
<div class="c2b-body" style="color: var(--color-teal, #227582)">지리정보시스템</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 블록 자체 스타일 -->
|
||||
<style>
|
||||
.block-c2b {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.c2b-badge {
|
||||
background: linear-gradient(135deg, var(--color-dark-teal, #084C56), var(--color-teal, #227582));
|
||||
border-radius: 12px 12px 0 0;
|
||||
padding: 10px 24px;
|
||||
text-align: center;
|
||||
}
|
||||
.c2b-badge-text {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.c2b-container {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr;
|
||||
background: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-top: none;
|
||||
border-radius: 0 0 16px 16px;
|
||||
padding: 24px 28px;
|
||||
gap: 0;
|
||||
}
|
||||
.c2b-divider {
|
||||
width: 1px;
|
||||
background: #e2e8f0;
|
||||
margin: 0 24px;
|
||||
align-self: stretch;
|
||||
}
|
||||
.c2b-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
.c2b-title {
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
line-height: 1.3;
|
||||
word-break: keep-all;
|
||||
}
|
||||
.c2b-body {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
line-height: 1.7;
|
||||
white-space: pre-line;
|
||||
word-break: keep-all;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.c2b-statement {
|
||||
margin-top: 16px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
line-height: 1.6;
|
||||
word-break: keep-all;
|
||||
padding: 0 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
149
templates/blocks/cards/test-compare-detail-gradient_1.html
Normal file
@@ -0,0 +1,149 @@
|
||||
<!DOCTYPE html><html><head><meta charset="UTF-8">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box;}
|
||||
body{background:#f0f0f0;padding:20px;font-family:'Pretendard Variable','Noto Sans KR',sans-serif;word-break:keep-all;}
|
||||
</style></head><body>
|
||||
<div style="width:1280px;background:#fff;padding:40px;">
|
||||
<!-- 그라디언트 상세 2열 비교: 비대칭 라운드 헤더 + 행 공유 + As-Is/To-Be -->
|
||||
<!--
|
||||
📋 compare-detail-gradient
|
||||
─────────────────
|
||||
용도: 두 카테고리의 상세 비교 (각각 N개 하위 섹션, 행 공유 정렬)
|
||||
슬롯: left_header, right_header, sections[] (각 row에 left + right)
|
||||
Figma 원본: Frame 4 (과정의 혁신 vs 결과의 혁신)
|
||||
디자인:
|
||||
- 좌측 헤더: 우측 라운드 + 텍스트 우정렬 (warm 그라디언트)
|
||||
- 우측 헤더: 좌측 라운드 + 텍스트 좌정렬 (teal 그라디언트)
|
||||
- CSS Grid 2열×N행: 좌/우 섹션 제목이 같은 Y선에 정렬
|
||||
- 첫 번째 섹션: As-Is → 화살표 → To-Be 수평 배치 가능
|
||||
수학적 계산:
|
||||
좌/우 섹션 Y좌표 — Row1: 1166/1166(정렬), Row2: 1529/1467(62px차→Grid해결), Row3: 1845/1845(정렬)
|
||||
-->
|
||||
<div class="block-cdg">
|
||||
<!-- Row 0: Headers -->
|
||||
<div class="cdg-header cdg-header-warm">
|
||||
<span class="cdg-header-text">과정 (Process)의 혁신</span>
|
||||
</div>
|
||||
<div class="cdg-header cdg-header-teal">
|
||||
<span class="cdg-header-text">결과 (Products)의 변화</span>
|
||||
</div>
|
||||
|
||||
<!-- Rows: sections (행 공유로 좌/우 Y선 자동 정렬) -->
|
||||
|
||||
<div class="cdg-cell cdg-cell-warm">
|
||||
<div class="cdg-sec-title cdg-title-warm">Analogue 기반 업무의 Digital Transformation</div>
|
||||
|
||||
<div class="cdg-asis-tobe">
|
||||
<div class="cdg-asis">
|
||||
<div class="cdg-bullet">개념·문서·행정 절차 중심</div><div class="cdg-bullet">2D 도면, 전문가, 규정</div><div class="cdg-bullet">업무 구분(단절), 책임</div>
|
||||
</div>
|
||||
<img src="/static/figma-assets/arrow_asis_tobe.png" class="cdg-arrow" alt="→">
|
||||
<div class="cdg-tobe">
|
||||
<div class="cdg-bullet">시각화된 목적물, 소통, 투명성 중심</div><div class="cdg-bullet">3D 모델, 참여자, 실체</div><div class="cdg-bullet">협업(융·복합), 창의성</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="cdg-cell cdg-cell-teal">
|
||||
<div class="cdg-sec-title cdg-title-teal">Copy & Paste로 인한 하향 평준화된 기존 성과물의 품질 향상</div>
|
||||
|
||||
<div class="cdg-sec-body">
|
||||
<div class="cdg-bullet">과거 수작업으로 시행하면서 발생하던 오류 등의 최소화</div><div class="cdg-bullet">정확한 Data에 기반한 계획으로 고품질 성과물 도출</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="cdg-cell cdg-cell-warm">
|
||||
<div class="cdg-sec-title cdg-title-warm">위치기반의 3D 모델을 사용하는 Process 혁신</div>
|
||||
|
||||
<div class="cdg-sec-body">
|
||||
<div class="cdg-bullet">지리·지형·지반 등 위치정보(GIS)와 3D모델(형상, 속성정보) 기반의 건설 정보를 포함하는 BIM의 연계를 통한 업무 프로세스의 혁신</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="cdg-cell cdg-cell-teal">
|
||||
<div class="cdg-sec-title cdg-title-teal">Analogue 기반 도서 외 Digital 기반 정보물 추가</div>
|
||||
|
||||
<div class="cdg-sec-body">
|
||||
<div class="cdg-bullet">기존 성과물(도면, 수량, 계산서, 시방서 등)에 3D 모델, Simulation 등의 Digital 기반 정보물 추가</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="cdg-cell cdg-cell-warm">
|
||||
<div class="cdg-sec-title cdg-title-warm">사용자 중심의 Solution(S/W) 제공</div>
|
||||
|
||||
<div class="cdg-sec-body">
|
||||
<div class="cdg-bullet">서로 다른 S/W로 작성되어 분절화된 Analogue 방식의 성과물과 정보물을 연계할 수 있는 설계·시공 Solution 제공</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="cdg-cell cdg-cell-teal">
|
||||
<div class="cdg-sec-title cdg-title-teal">Solution을 활용한 업무 효율화</div>
|
||||
|
||||
<div class="cdg-sec-body">
|
||||
<div class="cdg-bullet">Engn. Solution을 통해 성과물에 관한 이슈를 함께 검토·논의하는 협업 환경 조성</div><div class="cdg-bullet">건설 단계별 정보를 디지털 데이터로 축적하여, 건설 전 과정을 통합관리</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/*
|
||||
CSS Grid: 2열 × (1+N)행
|
||||
행 공유 → 좌/우 섹션 제목이 같은 Y선에 자동 정렬
|
||||
Figma Row2 차이: 좌 y=1529, 우 y=1467 (62px) → Grid가 해결
|
||||
*/
|
||||
.block-cdg {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ── Headers (비대칭 라운드) ── */
|
||||
.cdg-header { padding: 12px 28px; display: flex; min-height: 52px; align-items: center; }
|
||||
.cdg-header-text { font-size: 23.3px; font-weight: var(--weight-black, 900); color: #000; line-height: 1.3; }
|
||||
.cdg-header-warm {
|
||||
background: linear-gradient(90deg, rgba(165,161,150,0.15), rgba(57,50,30,0.85));
|
||||
border-radius: 0 28px 28px 0; justify-content: flex-end; text-align: right; margin-right: 4px;
|
||||
}
|
||||
.cdg-header-teal {
|
||||
background: linear-gradient(90deg, rgba(3,33,24,0.85), rgba(41,107,85,0.15));
|
||||
border-radius: 28px 0 0 28px; justify-content: flex-start; text-align: left; margin-left: 4px;
|
||||
}
|
||||
|
||||
/* ── Grid Cells (행 공유 → 좌우 Y선 정렬) ── */
|
||||
.cdg-cell {
|
||||
padding: 14px 20px;
|
||||
display: flex; flex-direction: column; gap: 6px;
|
||||
align-self: start;
|
||||
}
|
||||
.cdg-cell-warm { background: linear-gradient(180deg, rgba(255,255,255,0.4), rgba(57,50,30,0.08)); }
|
||||
.cdg-cell-teal { background: linear-gradient(180deg, rgba(41,107,85,0.06), rgba(255,255,255,0.4)); }
|
||||
|
||||
/* ── Section Title & Body ── */
|
||||
.cdg-sec-title { font-size: 16.6px; font-weight: var(--weight-black, 900); line-height: 1.4; word-break: keep-all; margin-bottom: 4px; }
|
||||
.cdg-title-warm { color: var(--color-warm-brown, #5C3714); }
|
||||
.cdg-title-teal { color: var(--color-dark-teal, #084C56); }
|
||||
.cdg-sec-body { padding-left: 8px; }
|
||||
|
||||
/* ── Bullets ── */
|
||||
.cdg-bullet {
|
||||
position: relative; padding-left: 14px;
|
||||
font-size: 13.3px; font-weight: var(--weight-bold, 700); color: #1a1a1a;
|
||||
line-height: 23.3px; word-break: keep-all;
|
||||
}
|
||||
.cdg-bullet::before { content: '•'; position: absolute; left: 0; color: #666; }
|
||||
|
||||
/* ── As-Is → To-Be 수평 레이아웃 ── */
|
||||
.cdg-asis-tobe { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
|
||||
.cdg-asis, .cdg-tobe { flex: 1; padding-left: 8px; }
|
||||
.cdg-arrow { width: 60px; height: auto; flex-shrink: 0; opacity: 0.7; }
|
||||
</style>
|
||||
</div>
|
||||
</body></html>
|
||||
162
templates/blocks/cards/test-hero-icon-cards_1.html
Normal file
@@ -0,0 +1,162 @@
|
||||
<!DOCTYPE html><html><head><meta charset="UTF-8">
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box;}
|
||||
body{background:#f0f0f0;padding:20px;font-family:'Pretendard Variable','Noto Sans KR',sans-serif;word-break:keep-all;}
|
||||
</style></head><body>
|
||||
<div style="width:1280px;background:#fff;padding:40px;">
|
||||
<!-- 히어로 카드 변형1: 3D 리본 배지 + N열 카드 (제목+중제목+불릿 계층) -->
|
||||
<!--
|
||||
📋 hero-icon-cards_1
|
||||
─────────────────
|
||||
용도: 핵심 요건/목표를 N열 카드로 시각화. 각 카드에 제목+중제목+불릿 계층.
|
||||
슬롯: statement(선택), badge_title, cards[] (title, sections[{title, bullets[]}])
|
||||
변형 원본: hero-icon-cards
|
||||
변경점: icon/title/subtitle → sections[{title, bullets[]}] 계층으로 변경
|
||||
수학적 계산: hero-icon-cards와 동일
|
||||
badge 508x94 at y=1431, box at y=1449, frame_w=1808
|
||||
scale = 1200/1808 = 0.6637
|
||||
ribbon: 337x62px, fold_offset: 12px, ribbon_below_fold: 50px
|
||||
box padding-top: 56px
|
||||
-->
|
||||
<div class="block-hic">
|
||||
|
||||
|
||||
<div class="hic-card-area">
|
||||
|
||||
<div class="hic-ribbon">
|
||||
<img src="../../../static/figma-assets/badge_solution.png" class="hic-ribbon-img" alt="">
|
||||
<span class="hic-ribbon-text">DX 시행을 위한 필수 요건</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="hic-box">
|
||||
<div class="hic-cards">
|
||||
|
||||
<div class="hic-card">
|
||||
<div class="hic-title">기술(디지털)</div>
|
||||
|
||||
<div class="hic-sec-title">Digital 기술(S/W, H/W)과 업무 Process의 통합</div>
|
||||
|
||||
<div class="hic-bullet">기존 업무 프로세스에 다양한 디지털 기술을 접목하여 업무 수행</div>
|
||||
|
||||
<div class="hic-bullet">프로젝트 전반에 걸친 업무 프로세스의 연결 및 조율</div>
|
||||
|
||||
|
||||
<div class="hic-sec-title">분야별 전문 지식(설계, 시공, 유지관리 등) 보유</div>
|
||||
|
||||
<div class="hic-bullet">건설 전 단계에 대한 근본적인 이해와 지식 및 경험</div>
|
||||
|
||||
<div class="hic-bullet">최신 토목 기술 트랜드 및 표준 기준 등에 대한 높은 지식</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="hic-sep"></div>
|
||||
|
||||
<div class="hic-card">
|
||||
<div class="hic-title">사람(역량)</div>
|
||||
|
||||
<div class="hic-sec-title">혁신적 사고방식과 창의적 문제 해결 능력</div>
|
||||
|
||||
<div class="hic-bullet">기존 수행 방식과 관습적 사고 등에 의한 접근 방식 탈피</div>
|
||||
|
||||
<div class="hic-bullet">디지털 기술을 활용한 창의적, 혁신적인 솔루션 제시</div>
|
||||
|
||||
|
||||
<div class="hic-sec-title">사용자 중심 사고와 DX 수행 경험</div>
|
||||
|
||||
<div class="hic-bullet">사용자의 요구와 기대를 충족시키는 설계 및 구현</div>
|
||||
|
||||
<div class="hic-bullet">시행착오를 포함한 수행 경험과 사용자 경험(UX)을 반영한 해결 방안 제시</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="hic-sep"></div>
|
||||
|
||||
<div class="hic-card">
|
||||
<div class="hic-title">자연(여건)</div>
|
||||
|
||||
<div class="hic-sec-title">지속적인 투자 및 실행 의지</div>
|
||||
|
||||
<div class="hic-bullet">기술 도입 초기 단계에 필요한 인력·기간·비용 등의 대규모 투자</div>
|
||||
|
||||
<div class="hic-bullet">기술 고도화를 위한 지속적인 개선 및 투자 체계 구축</div>
|
||||
|
||||
<div class="hic-bullet">변화와 혁신을 통해 부가가치를 창출하려는 실행 의지와 추진력</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.block-hic {
|
||||
display: flex; flex-direction: column; align-items: center;
|
||||
width: 100%; border-radius: 16px;
|
||||
padding: 36px 32px 28px; overflow: visible;
|
||||
}
|
||||
|
||||
/* ── Hero Statement ── */
|
||||
.hic-statement {
|
||||
text-align: center; font-size: 28px; font-weight: var(--weight-bold, 700);
|
||||
color: #fff; line-height: 1.6; word-break: keep-all;
|
||||
margin-bottom: 28px; padding: 0 24px;
|
||||
}
|
||||
.hic-statement strong { font-weight: 900; font-size: 1.1em; }
|
||||
.hic-statement em { color: #ef4444; font-style: normal; font-weight: 900; font-size: 1.15em; }
|
||||
|
||||
/*
|
||||
리본+박스 수학적 계산 (Figma 원본):
|
||||
badge 508x94 at y=1431, box at y=1449
|
||||
접힘선 = 18px = 이미지 높이의 19.1%
|
||||
scale(1200/1808) → ribbon 337x62, fold top에서 12px, 아래 50px
|
||||
*/
|
||||
.hic-card-area { position: relative; width: 100%; margin-top: 20px; }
|
||||
|
||||
.hic-ribbon {
|
||||
position: absolute;
|
||||
top: -12px; /* 접힘선이 박스 top border와 정확히 일치 */
|
||||
left: 50%; transform: translateX(-50%);
|
||||
z-index: 2; width: 337px; text-align: center;
|
||||
}
|
||||
.hic-ribbon-img { width: 100%; height: auto; display: block; }
|
||||
.hic-ribbon-text {
|
||||
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
||||
font-size: 20px; font-weight: 700; color: #fff;
|
||||
letter-spacing: 0.03em; white-space: nowrap;
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
/* 빨간 테두리 흰 박스 */
|
||||
.hic-box {
|
||||
position: relative; background: #fff;
|
||||
border: 6px solid #9b1b1b; border-radius: 20px;
|
||||
padding: 56px 8px 8px; /* top: ribbon_below(50) + 6px 여유 */
|
||||
}
|
||||
|
||||
.hic-cards { display: flex; align-items: stretch; width: 100%; }
|
||||
.hic-card {
|
||||
flex: 1; display: flex; flex-direction: column;
|
||||
padding: 16px 12px 20px; gap: 0;
|
||||
}
|
||||
.hic-sep { width: 1px; background: #e0e0e0; align-self: stretch; margin: 8px 0; }
|
||||
|
||||
/* ── 카드 내부 (변형: icon → 소제목+불릿 계층) ── */
|
||||
/* 중제목 (카드 헤더: 기술/사람/자연): 중앙 정렬 */
|
||||
.hic-title { font-size: 20px; font-weight: var(--weight-black, 900); color: #1a1a1a; line-height: 1.3; text-align: center; margin-bottom: 12px; }
|
||||
/* 소제목 (D1): 16.6px/900, 들여쓰기 1단 = 16px */
|
||||
.hic-sec-title { font-size: 16.6px; font-weight: 900; color: #1a1a1a; line-height: 1.4; margin-top: 8px; margin-bottom: 3px; padding-left: 16px; }
|
||||
.hic-sec-title:first-of-type { margin-top: 0; }
|
||||
/* 불릿 (D2): 13.3px/700, 들여쓰기 2단 = 36px, lineH=23.3px */
|
||||
.hic-bullet {
|
||||
position: relative; padding-left: 36px;
|
||||
font-size: 13.3px; font-weight: 700; color: #334155;
|
||||
line-height: 23.3px; word-break: keep-all;
|
||||
}
|
||||
.hic-bullet::before { content: '•'; position: absolute; left: 24px; color: #666; }
|
||||
</style>
|
||||
</div>
|
||||
</body></html>
|
||||
BIN
templates/blocks/cards/test-hero-icon-cards_1.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
@@ -751,6 +751,227 @@ blocks:
|
||||
note: 항목 수
|
||||
padding_overhead_px: 22
|
||||
padding_h_px: 32
|
||||
- id: hero-icon-cards
|
||||
name: 히어로 문구 + 아이콘 카드
|
||||
category: cards
|
||||
template: blocks/cards/hero-icon-cards.html
|
||||
height_cost: xlarge
|
||||
min_height_px: 280
|
||||
relation_types:
|
||||
- definition
|
||||
- flow
|
||||
min_items: 2
|
||||
max_items: 6
|
||||
visual: >
|
||||
다크 배경. 상단 큰 Hero 메시지(28px bold, 중앙, 흰 텍스트, 빨간 강조).
|
||||
3D 빨간 리본 배지가 빨간 테두리 흰 박스(r:20) 위에 걸침.
|
||||
박스 안에 N열 아이콘 카드(구분선). 각 카드: 아이콘 + 영문 제목(20px/900) + 한국어 부제(15px/500).
|
||||
visual_diff: >
|
||||
- card-icon-desc: 밝은 배경, 이모지+제목+설명. 리본/테두리 없음.
|
||||
- card-dark-overlay: 다크 배경 이미지+흰 텍스트. 카드 개별 배경.
|
||||
- 이 블록: 다크 배경 + 빨간 리본 배지 + 빨간 테두리 흰 박스 안 N열 카드.
|
||||
적합: 핵심 목표/가치를 N개 키워드로 선언하며 시각적 임팩트 필요할 때.
|
||||
부적합: 상세 설명 → card-icon-desc, 비교 → compare-2col-badge.
|
||||
when: >
|
||||
핵심 목표나 가치를 N개 키워드로 선언할 때.
|
||||
각 키워드에 아이콘이나 이미지가 있을 때.
|
||||
프레젠테이션형 임팩트가 필요할 때.
|
||||
not_for: >
|
||||
비교/대조 → compare-2col-badge.
|
||||
상세 설명 → card-icon-desc.
|
||||
순서/단계 → process-horizontal.
|
||||
purpose_fit:
|
||||
- 핵심전달
|
||||
zone: full-width-only
|
||||
slots:
|
||||
required:
|
||||
- statement
|
||||
- cards[]
|
||||
optional:
|
||||
- badge_title
|
||||
- ribbon_image
|
||||
- cards[].icon
|
||||
- cards[].subtitle
|
||||
schema:
|
||||
statement:
|
||||
max_lines: 2
|
||||
font_size: 28
|
||||
ref_chars:
|
||||
body: 60
|
||||
note: "28px bold white, 중앙, em=빨간 강조"
|
||||
badge_title:
|
||||
max_lines: 1
|
||||
font_size: 20
|
||||
ref_chars:
|
||||
body: 15
|
||||
note: "20px bold white, 3D 리본 위"
|
||||
card_title:
|
||||
max_lines: 2
|
||||
font_size: 20
|
||||
ref_chars:
|
||||
body: 15
|
||||
note: "20px black/900, 중앙정렬"
|
||||
card_subtitle:
|
||||
max_lines: 1
|
||||
font_size: 15
|
||||
ref_chars:
|
||||
body: 10
|
||||
note: "15px medium, 한국어 부제"
|
||||
padding_overhead_px: 80
|
||||
padding_h_px: 32
|
||||
- id: compare-detail-gradient
|
||||
name: 그라디언트 상세 2열 비교
|
||||
category: cards
|
||||
template: blocks/cards/compare-detail-gradient.html
|
||||
height_cost: xlarge
|
||||
min_height_px: 300
|
||||
relation_types:
|
||||
- comparison
|
||||
- contrast
|
||||
- process
|
||||
min_items: 2
|
||||
max_items: 10
|
||||
visual: >
|
||||
좌우 그라디언트 배경(워 브라운 vs 다크틸)으로 나뉜 2열 비교.
|
||||
비대칭 라운드 헤더(좌: 우라운드+우정렬, 우: 좌라운드+좌정렬).
|
||||
N개 섹션(소제목 18px/900 + 불릿 14px/700) 반복.
|
||||
CSS Grid 행 공유로 좌/우 섹션 제목이 같은 Y선에 정렬.
|
||||
첫 섹션에 As-Is → 화살표 → To-Be 수평 배치 가능.
|
||||
visual_diff: >
|
||||
- compare-2col-badge: 배지+2열, 단순 비교. 섹션 반복 없음.
|
||||
- comparison-2col: 좌우 텍스트 블록 비교. 그라디언트 없음.
|
||||
- 이 블록: 그라디언트+비대칭 헤더+N섹션 행 정렬. 가장 상세한 비교.
|
||||
적합: 두 카테고리에 각각 3개+ 하위 항목이 있는 상세 비교.
|
||||
부적합: 간단 비교 → compare-2col-badge, 표 형식 → compare-2col-split.
|
||||
when: >
|
||||
두 카테고리를 상세하게 비교할 때. 각 카테고리에 여러 하위 항목.
|
||||
과정 vs 결과, As-Is vs To-Be, 문제 vs 해결 구조.
|
||||
not_for: >
|
||||
간단 비교(본문 짧을 때) → compare-2col-badge.
|
||||
항목별 행 비교(표 형식) → compare-2col-split.
|
||||
3열 비교 → compare-3col-badge.
|
||||
purpose_fit:
|
||||
- 비교대조
|
||||
- 구조시각화
|
||||
- 근거사례
|
||||
zone: full-width-only
|
||||
slots:
|
||||
required:
|
||||
- left_header
|
||||
- right_header
|
||||
- sections[]
|
||||
optional:
|
||||
- arrow_image
|
||||
- sections[].left.asis
|
||||
- sections[].left.tobe
|
||||
schema:
|
||||
left_header:
|
||||
max_lines: 1
|
||||
font_size: 26
|
||||
ref_chars:
|
||||
body: 20
|
||||
note: "26px black/900, 비대칭 라운드 바"
|
||||
right_header:
|
||||
max_lines: 1
|
||||
font_size: 26
|
||||
ref_chars:
|
||||
body: 20
|
||||
note: "26px black/900, 비대칭 라운드 바"
|
||||
section_title:
|
||||
max_lines: 2
|
||||
font_size: 18
|
||||
ref_chars:
|
||||
body: 30
|
||||
note: "18px/900, 좌=브라운 우=틸"
|
||||
section_body:
|
||||
max_lines: 4
|
||||
font_size: 14
|
||||
ref_chars:
|
||||
body: 120
|
||||
note: "14px/700, 불릿 구조"
|
||||
padding_overhead_px: 52
|
||||
padding_h_px: 0
|
||||
- id: compare-2col-badge
|
||||
name: 배지 헤더 2열 비교
|
||||
category: cards
|
||||
template: blocks/cards/compare-2col-badge.html
|
||||
height_cost: large
|
||||
min_height_px: 200
|
||||
relation_types:
|
||||
- comparison
|
||||
- contrast
|
||||
visual: >
|
||||
상단 배지 바(틸 그라디언트 + 흰색 텍스트, r:12) 아래
|
||||
2열 비교 레이아웃. 좌/우 각각 대제목(22px/900) + 본문(15px/500).
|
||||
중앙 세로 구분선. 흰색 컨테이너(r:16). 선택적 하단 Hero 메시지.
|
||||
visual_diff: >
|
||||
- compare-2col-split: 중앙 '기준 라벨' 열이 있는 3열 표. 행별 상세 비교.
|
||||
- comparison-2col: 좌우 텍스트 블록 비교. 표 없이 자유 형식.
|
||||
- compare-pill-pair: 둥근 박스 2개 + VS. 헤더 역할만.
|
||||
- 이 블록: 배지 헤더로 상위 주제 선언 + 2열 비교 + 선택적 결론 문구.
|
||||
적합: 두 개념/전략의 정의를 비교하며 상위 주제를 배지로 명시할 때.
|
||||
부적합: 항목별 행 비교 → compare-2col-split, 시각적 대비만 → compare-pill-pair.
|
||||
when: >
|
||||
두 개념/방법/전략을 나란히 비교할 때. 배지 헤더로 상위 주제를 명시.
|
||||
예: "정책 달성 — Engn. Solution vs DfMA", "현재 vs 미래"
|
||||
not_for: >
|
||||
항목별 행 비교(5행+) → compare-2col-split.
|
||||
3개 비교 → compare-3col-badge.
|
||||
간단 텍스트만 → comparison-2col.
|
||||
purpose_fit:
|
||||
- 비교대조
|
||||
- 개념정의
|
||||
zone: full-width-only
|
||||
slots:
|
||||
required:
|
||||
- badge_title
|
||||
- left_title
|
||||
- left_body
|
||||
- right_title
|
||||
- right_body
|
||||
optional:
|
||||
- statement
|
||||
- left_color
|
||||
- right_color
|
||||
schema:
|
||||
badge_title:
|
||||
max_lines: 1
|
||||
font_size: 18
|
||||
ref_chars:
|
||||
body: 15
|
||||
note: 18px bold white, 배지 바
|
||||
left_title:
|
||||
max_lines: 1
|
||||
font_size: 22
|
||||
ref_chars:
|
||||
body: 15
|
||||
note: 22px black/900
|
||||
left_body:
|
||||
max_lines: 6
|
||||
font_size: 15
|
||||
ref_chars:
|
||||
body: 200
|
||||
note: 15px/500, 틸 색상
|
||||
right_title:
|
||||
max_lines: 1
|
||||
font_size: 22
|
||||
ref_chars:
|
||||
body: 15
|
||||
note: 22px black/900
|
||||
right_body:
|
||||
max_lines: 6
|
||||
font_size: 15
|
||||
ref_chars:
|
||||
body: 200
|
||||
note: 15px/500, 틸 색상
|
||||
statement:
|
||||
max_lines: 2
|
||||
font_size: 18
|
||||
ref_chars:
|
||||
body: 50
|
||||
note: 18px bold, 중앙정렬
|
||||
padding_overhead_px: 56
|
||||
padding_h_px: 32
|
||||
- id: compare-3col-badge
|
||||
name: VS 배지 비교표
|
||||
category: tables
|
||||
|
||||
117
오답노트.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# 절대 하지 말아야 하는 오답노트
|
||||
|
||||
> 2026-04-07 세션에서 발생한 실수 목록. 반복하면 안 됨.
|
||||
|
||||
---
|
||||
|
||||
## 1. 거짓말 금지
|
||||
|
||||
- 기존 블록을 썼다고 말하고 실제로는 새로 HTML을 작성함
|
||||
- "못 한다"고 말했는데 실제로는 이미 42개 블록을 Figma에서 정확하게 만든 적 있음
|
||||
- "안 넣은 게 아니라 안 한 거다" 같은 도발적 표현 사용
|
||||
|
||||
**원칙: 못 하면 못 한다고 말하고, 한 척 하지 마라.**
|
||||
|
||||
---
|
||||
|
||||
## 2. 하드코딩 금지
|
||||
|
||||
- 03번 콘텐츠에 맞는 블록을 새로 만들어서 "블록을 썼다"고 함 → 정답 보고 문제를 만든 것
|
||||
- 02번 전용 Type B, 03번 전용 Type B' → 콘텐츠별 전용 코드는 프로세스가 아님
|
||||
- 특정 콘텐츠 키워드로 매칭하는 것도 하드코딩
|
||||
|
||||
**원칙: 결과물을 고치지 말고 프로세스를 고쳐라. 프로세스가 범용적이어야 한다.**
|
||||
|
||||
---
|
||||
|
||||
## 3. 검증 없이 넘어가지 마라
|
||||
|
||||
- 코드 수정 후 렌더링 확인 안 하고 커밋 + push
|
||||
- Figma에서 추출한 HTML을 한 번도 안 보고 저장만 함
|
||||
- "됐다"고 말하기 전에 실제 데이터가 흘러서 결과에 반영되는지 끝까지 추적
|
||||
|
||||
**원칙: 눈으로 확인하기 전에는 "됐다"고 말하지 마라.**
|
||||
|
||||
---
|
||||
|
||||
## 4. "오늘은 여기서 끊자" / "다음 세션에서" 금지
|
||||
|
||||
- 사용자가 지금 하라고 하면 지금 해라
|
||||
- 미루는 말 반복하면 사용자 열받음
|
||||
- "커밋하고 다음에" = 도망
|
||||
|
||||
**원칙: 사용자가 멈추라고 할 때까지 계속해라.**
|
||||
|
||||
---
|
||||
|
||||
## 5. 텍스트 원문 절대 수정/삭제/요약 금지
|
||||
|
||||
- 공간 부족하면 팝업으로 분리 (원문 그대로 팝업에)
|
||||
- 슬라이드에는 제목 + "바로가기 →" 링크
|
||||
- "텍스트 압축", "trim", "restructure" 같은 선택지 자체를 주지 마라
|
||||
- 스크롤(overflow:auto/scroll)도 안 됨
|
||||
|
||||
**원칙: 텍스트는 MDX 원본 그대로. 안 들어가면 팝업으로 빼지, 텍스트를 줄이지 마라.**
|
||||
|
||||
---
|
||||
|
||||
## 6. 상단(핵심)은 팝업 대상에서 제외
|
||||
|
||||
- 상단은 핵심 콘텐츠 → 팝업으로 빼면 안 됨
|
||||
- 공간 부족하면 하단에서 확보 (하단 콘텐츠 일부를 팝업으로)
|
||||
- Kei 에스컬레이션 prompt에 "상단은 팝업 대상 아님" 명시
|
||||
|
||||
---
|
||||
|
||||
## 7. Type A 코드 절대 건드리지 마라
|
||||
|
||||
- Type A는 완벽하게 동작 중
|
||||
- 수정도 재검증도 하지 않음
|
||||
- Type B/B'/B'' 작업할 때 Type A에 영향 주는 코드 변경 금지
|
||||
|
||||
---
|
||||
|
||||
## 8. API 낭비 금지
|
||||
|
||||
- 파이프라인 돌리기 전에 코드 변경이 맞는지 먼저 확인
|
||||
- 매번 돌려보면서 "되나?" 하지 마라
|
||||
- 한 번 돌릴 때 정확하게 고쳐서 돌려라
|
||||
|
||||
---
|
||||
|
||||
## 9. Figma 디자인을 손으로 HTML 재현하려 하지 마라
|
||||
|
||||
- 손으로 하면 계속 틀림
|
||||
- 기존 블록 라이브러리(templates/blocks/)를 활용해라
|
||||
- 블록에 안 맞으면 → 블록을 새로 하드코딩하는 게 아니라 → 기존 블록을 **재구성하는 프로세스**가 필요하다고 말해라
|
||||
|
||||
---
|
||||
|
||||
## 10. 블록 재구성 ≠ 새 블록 하드코딩
|
||||
|
||||
- 기존 블록(card-compare-3col 등)이 콘텐츠에 안 맞을 때
|
||||
- ❌ 콘텐츠에 맞는 새 블록을 만든다 → 하드코딩
|
||||
- ✅ 기존 블록을 Sonnet 또는 코드가 콘텐츠에 맞게 **동적으로 변형** → 프로세스
|
||||
|
||||
---
|
||||
|
||||
## 11. 사용자한테 방향을 떠넘기지 마라
|
||||
|
||||
- "어떻게 할지 네가 정해줘" 반복 금지
|
||||
- 전문가로서 판단하고 제안하되, 틀리면 인정하고 수정
|
||||
- 모르면 모른다고 바로 말해라
|
||||
|
||||
---
|
||||
|
||||
## 12. 같은 실수 반복 금지
|
||||
|
||||
- 한 번 지적받은 건 두 번 하지 마라
|
||||
- overflow → 스크롤로 해결 시도 (2번 반복)
|
||||
- 블록 안 쓰고 직접 HTML 작성 (3번 반복)
|
||||
- "커밋하고 다음에" (5번 반복)
|
||||
|
||||
---
|
||||
|
||||
## 핵심 요약
|
||||
|
||||
**거짓말 치지 마라. 하드코딩 하지 마라. 검증 없이 넘어가지 마라. 프로세스를 만들어라.**
|
||||
|
Before Width: | Height: | Size: 4.6 MiB After Width: | Height: | Size: 4.6 MiB |
|
Before Width: | Height: | Size: 449 KiB After Width: | Height: | Size: 449 KiB |
|
Before Width: | Height: | Size: 292 KiB After Width: | Height: | Size: 292 KiB |
|
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 434 KiB |
|
Before Width: | Height: | Size: 379 KiB After Width: | Height: | Size: 379 KiB |
BIN
참고 페이지/figma_center_2x.png
Normal file
|
After Width: | Height: | Size: 9.7 MiB |
BIN
참고 페이지/figma_export_2x.png
Normal file
|
After Width: | Height: | Size: 7.6 MiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |