# Phase 2: Figma Block Design Specification > 7개 블록 + 2개 서브 컴포넌트 상세 설계 > 기준: FIGMA-DESIGN-LANGUAGE.md 분석 결과 > 최종 업데이트: 2026-04-08 --- ## 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` 슬롯으로 전달 - 블록은 `` 태그로 렌더링, 크기는 CSS로 컨테이너에 맞춤 ### S2. CTA 버튼 - 독립 블록이 아닌 **다른 블록 내 선택적 요소** - `cta_text` 슬롯으로 전달 (없으면 미표시) - CSS: 그라디언트 바 + 둥근 버튼 (r:7) --- ## 구현 결과 (전체 7개 블록) | # | 블록 | 카테고리 | 출처 | 상태 | 핵심 수학 | |---|------|---------|------|------|----------| | 1 | `hero-icon-cards` | cards | Page 1/Frame 2 | ✅ 완료 | 3D 리본 fold_offset 계산 (badge_y→box_y×scale) | | 2 | `compare-2col-badge` | cards | Page 1/Frame 3 | ✅ 완료 | 3D 리본 fold_offset + 틸 테두리 | | 3 | `compare-detail-gradient` | cards | Page 1/Frame 4 | ✅ 완료 | CSS Grid 행 공유 + As-Is/To-Be + 연속 그라디언트 | | 4 | `category-strip-table` | cards | Page 2/001_개요 | ✅ 완료 | scale=1200/2123, N열 동적 Grid | | 5 | `checklist-dark` | emphasis | Page 3/f5 | ✅ 완료 | scale=1200/1770, 행 간격 계산 | | 6 | `system-2col-center` | cards | Page 3/f8 | ✅ 완료 | scale=1200/2446, 3열 Grid | | 7 | `cycle-orbit` | visuals | Page 4/Frame 1 | ✅ 완료 | **3D 원 Z축 기울임(80°) → 2D 투영**, 사이각 축소, 접선 회전 | ### 핵심 교훈 1. **수학적 계산 필수**: Figma 좌표 → 스케일 → CSS값. 시행착오 금지. 2. **3D 리본은 이미지 추출**: CSS로 재현 불가, Figma에서 PNG 추출. 3. **CSS Grid 행 공유**: 좌/우 섹션 Y선 정렬 문제 해결. 4. **연속 그라디언트**: 셀별 배경 → Grid 전체 배경으로 끊김 방지. 5. **3D 원 투영**: `project(α) = (cx+R×cos(α), cy+R×sin(α)×cos(θ))` — N개 노드 자동 배치. 6. **텍스트 배치**: 좌측 노드→이름 좌측에, 우측/상단 노드→이름 우측에. 7. **비교 리뷰 필수**: Figma PNG vs HTML을 같은 폭으로 위/아래 비교.