From 8e577b31639bcac414103b2b7381966fc28d0ce0 Mon Sep 17 00:00:00 2001 From: kyeongmin Date: Fri, 17 Apr 2026 13:56:03 +0900 Subject: [PATCH] =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EA=B8=B0=EB=B0=98=20CSS?= =?UTF-8?q?=20=EC=B2=B4=EA=B3=84=20=EA=B5=AC=EC=B6=95=20+=20slide-base=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tokens: typography(35변수), spacing(28변수), colors(41변수) 정의 - slide-base.html: 인라인 style 제거, Jinja include로 토큰/CSS 조립 - slide-base.css: 모든 직접값을 토큰 변수 참조로 전환 (직접값 0) - block_assembler.py: Template → Environment.from_string (include 지원) - TOKENS-v1.md: 위계 기준표 초안 + component token 후보 - BLOCK-RULES.md: 블록 작성 규칙 (spacing 문구 실제 토큰과 일치) Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/architecture/BLOCK-RULES.md | 162 +++++++++++++++++++++++++ docs/architecture/TOKENS-v1.md | 69 +++++++++++ src/block_assembler.py | 13 +- templates/blocks/slide-base.html | 152 ++--------------------- templates/styles/base/slide-base.css | 133 ++++++++++++++++++++ templates/styles/tokens/colors.css | 76 ++++++++++++ templates/styles/tokens/spacing.css | 47 +++++++ templates/styles/tokens/typography.css | 77 ++++++++++++ 8 files changed, 586 insertions(+), 143 deletions(-) create mode 100644 docs/architecture/BLOCK-RULES.md create mode 100644 docs/architecture/TOKENS-v1.md create mode 100644 templates/styles/base/slide-base.css create mode 100644 templates/styles/tokens/colors.css create mode 100644 templates/styles/tokens/spacing.css create mode 100644 templates/styles/tokens/typography.css diff --git a/docs/architecture/BLOCK-RULES.md b/docs/architecture/BLOCK-RULES.md new file mode 100644 index 0000000..c3e165d --- /dev/null +++ b/docs/architecture/BLOCK-RULES.md @@ -0,0 +1,162 @@ +# 블록 작성 규칙 + +Figma 1:1 HTML을 재사용 가능한 블록으로 전환할 때, 이 규칙을 따른다. +figma_to_html_agent와 design_agent 간의 **계약서**. + +--- + +## 1. HTML 규칙 + +### 구조 스타일은 OK +블록의 배치/정렬을 위한 CSS는 블록 안에 있어야 한다. +```css +/* OK */ +display: flex; +grid-template-columns: repeat(3, 1fr); +align-items: center; +overflow: hidden; +position: relative; +``` + +### 직접값은 금지 +하드코딩된 폰트/색/여백은 블록 안에 넣지 않는다. +```css +/* NG */ +font-size: 11px; +color: #475569; +padding: 16px; + +/* OK — 토큰 참조 */ +font-size: var(--font-body); +color: var(--color-body); +padding: var(--card-padding); +``` + +--- + +## 2. 클래스명 규칙 + +### 공통 클래스 (모든 블록에서 사용) +| 클래스 | 용도 | 토큰 참조 | +|--------|------|-----------| +| `.zone-title` | 중목차 제목 | `--font-zone-title`, `--color-zone-title` | +| `.sub-title` | 소목차 제목 | `--font-sub-title` | +| `.bul` | 본문 블릿 (hanging indent) | `--font-body`, `--bullet-indent` | +| `.body-text` | 본문 텍스트 | `--font-body`, `--color-body` | +| `.caption` | 캡션/보조 | `--font-caption`, `--color-caption` | + +### 블록 고유 클래스 +블록별 고유 요소는 블록 prefix를 붙인다. +``` +.p3c-bar (prerequisites-3col의 gradient 바) +.pp2-col (process-product-2col의 컬럼) +.cid-card (card-icon-desc의 카드) +``` + +--- + +## 3. 슬롯 규칙 + +블록이 받아들이는 콘텐츠 슬롯. catalog에 명시. + +| 슬롯 | 용도 | 예시 | +|------|------|------| +| `zone_title` | 중목차 제목 | "DX의 궁극적 목표" | +| `sub_title` | 소목차 제목 | "안전과 품질" | +| `body` | 본문 텍스트 | 설명 문장 | +| `bullets` | 블릿 리스트 | D2: 항목들 | +| `image` | 이미지 | 시각 앵커 | +| `preview` | 상세 preview | 표 헤더+행 | +| `detail_link` | 자세히보기 링크 | 첨부 파일 연결 | + +--- + +## 4. 토큰 참조 방법 + +### 토큰 파일 위치 +``` +templates/styles/tokens/ +├── typography.css ← 글자 위계 +├── spacing.css ← 여백/간격 +└── colors.css ← 색상 +``` + +### 사용법 +```css +/* 블록 CSS에서 */ +.my-block-title { + font-size: var(--font-sub-title); + font-weight: var(--weight-sub-title); + line-height: var(--lh-sub-title); + color: var(--color-zone-title); + margin-bottom: var(--heading-gap); +} + +.my-block-body { + font-size: var(--font-body); + color: var(--color-body); + padding-left: var(--bullet-indent); + text-indent: calc(var(--bullet-indent) * -1); +} +``` + +### 블록 의미색이 필요할 때 +공통 테마색으로 안 되는 역할색은 colors.css의 2층 의미색을 참조. +```css +/* 3열 비교의 열별 색상 */ +.col-1 .bar { background: linear-gradient(180deg, var(--color-col-1-from), var(--color-col-1-to)); } +.col-2 .bar { background: linear-gradient(180deg, var(--color-col-2-from), var(--color-col-2-to)); } +``` + +--- + +## 5. 블록 HTML 예시 + +### 좋은 예 +```html +
+
+
{{ col.title }}
+
• {{ col.desc }}
+
+
+ + +``` + +### 나쁜 예 +```html + +
+``` + +--- + +## 6. 전환 체크리스트 + +Figma 1:1 HTML → 재사용 블록으로 전환할 때: + +- [ ] font-size 직접값 → `var(--font-*)` 교체 +- [ ] color 직접값 → `var(--color-*)` 교체 +- [ ] padding/margin 직접값 → semantic spacing 또는 `var(--space-*)` 교체 + - 카드 내부: `var(--card-padding)` + - 블릿 들여쓰기: `var(--bullet-indent)` + - 소제목 아래: `var(--heading-gap)` + - 일반 여백: `var(--space-sm)`, `var(--space-md)` 등 +- [ ] gap → `var(--card-gap)`, `var(--flex-gap)` 등 +- [ ] border-radius → `var(--card-radius)` 교체 +- [ ] 공통 클래스 사용 (`.bul`, `.sub-title` 등) +- [ ] 블록 고유 요소에 prefix 클래스 +- [ ] catalog에 슬롯/구조/호환 정보 등록 diff --git a/docs/architecture/TOKENS-v1.md b/docs/architecture/TOKENS-v1.md new file mode 100644 index 0000000..b5fa32e --- /dev/null +++ b/docs/architecture/TOKENS-v1.md @@ -0,0 +1,69 @@ +# Typography Tokens — 초안 v1 + +## Global Hierarchy Tokens + +슬라이드 전체의 공통 글자 위계. 모든 블록이 이 기준을 따른다. + +| 위계 | 토큰명 | font-size | font-weight | line-height | 용도 | +|------|--------|-----------|-------------|-------------|------| +| 대목차 | `--font-slide-title` | 22px | 700 | 1.4 | 슬라이드 상단 제목 | +| 중목차 | `--font-zone-title` | 13px | 700 | 1.4 | zone 제목 (## 대목차 하위) | +| 소목차 | `--font-sub-title` | 12px | 700 | 1.45 | 블록 내 소제목, 카드 제목 | +| 본문 | `--font-body` | 11px | 400~500 | 1.55 | 블릿, 설명 텍스트 | +| 캡션 | `--font-caption` | 10px | 400 | 1.4 | 각주, 출처, 보조 텍스트 | +| footer | `--font-footer` | 20px | 700 | 1.2 | 핵심 인사이트 pill | + +--- + +## Component Semantic Token 후보 + +위계 6종으로 안 덮이는 역할. 가까운 위계에서 기본값을 가져오되, 필요시 override. + +| 역할 | 기반 위계 | 예상 override | 비고 | +|------|-----------|---------------|------| +| body-strong (본문 강조) | 본문 (11px) | weight: 600~700 | 본문 heading, 인라인 강조 | +| detail-link (자세히보기) | 캡션 (10px) | weight: 500, color: muted | 링크 텍스트 | +| pill-label | 소목차 (12px) | weight: 700, color: white | pill/badge 안 라벨 | +| table-header | 소목차 (12px) | weight: 700, color: white, bg: dark | 표 헤더 셀 | +| table-cell | 본문 (11px) | - | 표 데이터 셀 | +| compare-badge | 소목차 (12px) | weight: 700 | 비교 블록 VS 뱃지 | +| callout | 소목차 (12px) | weight: 700, color: accent | 강조 인용 | +| overline | 캡션 (10px) | weight: 600, letter-spacing | 상단 라벨 | + +--- + +## Source + +현재 코드에서 추출한 실제 사용값 기준. + +| 출처 | 대목차 | 중목차 | 소목차 | 본문 | 캡션 | footer | +|------|--------|--------|--------|------|------|--------| +| slide-base.html | 22px | - | - | - | - | 20px | +| block_assembler.py zone title | - | 13px | - | - | - | - | +| block_assembler.py direct render | - | - | 12px | 11px | - | - | +| block_assembler.py .bul | - | - | - | 11px | - | - | +| FontHierarchy (legacy) | - | - | key_msg 14 | core 12 | sidebar 10 | - | + +--- + +## FontHierarchy 매핑 (보조 체계) + +기존 FontHierarchy는 역할 기반이므로, 위계 기반으로 매핑하여 보조로 유지. + +| FontHierarchy | 위계 매핑 | 비고 | +|---------------|-----------|------| +| key_msg (14px) | 소목차~중목차 사이 | 강조 메시지용, component token 후보 | +| core (12px) | 소목차 | 기본 블록 제목급 | +| bg (11px) | 본문 | 배경/보조 텍스트 | +| sidebar (10px) | 캡션 | 사이드바/첨부 | + +--- + +## 상태 + +- [x] inventory 수집 완료 +- [x] global hierarchy v1 초안 +- [x] component token 후보 분리 +- [ ] typography.css 파일 작성 +- [ ] spacing tokens 정의 +- [ ] color tokens 정의 (공통 + 의미색) diff --git a/src/block_assembler.py b/src/block_assembler.py index 2f24860..f5339c1 100644 --- a/src/block_assembler.py +++ b/src/block_assembler.py @@ -1688,9 +1688,16 @@ def assemble_slide_html_final(ctx: "PipelineContext", title_text: str = "", meas "{% block body %}{% endblock %}", body_html, ) - # Jinja2로 title, footer_text 등 변수만 렌더링 - from jinja2 import Template - template = Template(slide_base_raw) + # Jinja2 Environment로 렌더 ({% include %} 지원을 위해) + from jinja2 import Environment, FileSystemLoader, BaseLoader + include_env = Environment(loader=FileSystemLoader(str(templates_dir))) + # slide_base_raw를 문자열 템플릿으로 렌더하되, include는 templates_dir 기준 + from jinja2 import DictLoader + combined_loader = FileSystemLoader(str(templates_dir)) + include_env = Environment(loader=combined_loader) + # slide-base 내용을 임시 템플릿으로 등록 + include_env.loader = FileSystemLoader(str(templates_dir)) + template = include_env.from_string(slide_base_raw) slide_html = template.render( title=title, footer_text=conclusion, diff --git a/templates/blocks/slide-base.html b/templates/blocks/slide-base.html index 1c2ddf7..4bafbb5 100644 --- a/templates/blocks/slide-base.html +++ b/templates/blocks/slide-base.html @@ -5,13 +5,7 @@ 용도: 16:9 슬라이드의 고정 배경. 상단 제목 + 구분선 + 본문 영역 + 하단 결론 pill. 각 블록(new/, cards/, emphasis/ 등)은 본문 영역(.slide-body) 안에 들어간다. Figma 원본: Frame 1171281207 (node 107:22, 3848×2165, Page 2) - 수학적 계산: - 원본 3848×2165 → 16:9 (1280×720) - scale = 1280 / 3848 = 0.33264 - 상단 제목: top 125px × S ≈ 42px, left 155px × S ≈ 52px, font 65.8px × S ≈ 22px - 구분선: top 218px × S ≈ 72px - 하단 pill: top 1980px × S ≈ 659px, height 123px × S ≈ 41px - 본문 영역: top ~72px ~ bottom ~659px = 약 587px 가용 + CSS: styles/tokens/ + styles/base/slide-base.css 로 분리됨. 슬롯: title — 슬라이드 제목 (gradient text) body — {% block body %}{% endblock %} 또는 include 대상 @@ -20,10 +14,6 @@ svg/bg_slide_texture.png — 배경 텍스처 (opacity 2%) svg/line_divider.svg — 구분선 svg/pill_scroll.png — 하단 pill 배경 (두루마리, 이미지 유지) - CSS 요소: - 배경 gradient: linear-gradient(180deg, #f0f0f0, #fff) - 제목 gradient text: linear-gradient(180deg, #296b55, #123328) - 하단 pill: 캡슐 border-radius 50px --> @@ -33,136 +23,18 @@ {{ title|default("슬라이드") }} + diff --git a/templates/styles/base/slide-base.css b/templates/styles/base/slide-base.css new file mode 100644 index 0000000..61299e8 --- /dev/null +++ b/templates/styles/base/slide-base.css @@ -0,0 +1,133 @@ +/* ══════════════════════════════════════ + slide-base.css + ══════════════════════════════════════ + 슬라이드 공통 프레임 스타일. + slide-base.html에서 분리. + 직접값 대신 토큰 변수 참조. + ══════════════════════════════════════ */ + +* { margin: 0; padding: 0; box-sizing: border-box; } + +body { + font-family: 'Noto Sans KR', sans-serif; + background: var(--color-bg-page); + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + word-break: keep-all; +} + +/* ── 16:9 슬라이드 컨테이너 ── */ +.slide { + width: 1280px; + height: 720px; + position: relative; + overflow: hidden; + background: var(--color-bg); + box-shadow: 0 4px 20px rgba(0,0,0,0.15); +} + +/* ── 배경 ── */ +.slide-bg { + position: absolute; + inset: 0; + background: linear-gradient(180deg, #f0f0f0 0%, #ffffff 100%); + z-index: 0; +} +.slide-bg-texture { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; + opacity: 0.02; + pointer-events: none; +} + +/* ── 대목차 제목 ── */ +.slide-title { + position: absolute; + left: var(--slide-title-left); + top: var(--slide-title-top); + width: calc(100% - 104px); + font-weight: var(--weight-slide-title); + font-size: var(--font-slide-title); + line-height: var(--lh-slide-title); + background-image: linear-gradient(180deg, var(--color-slide-title-from) 0%, var(--color-slide-title-to) 100%); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + text-shadow: 0 0 2px #322c1e; + z-index: 2; +} + +/* ── 구분선 ── */ +.slide-divider { + position: absolute; + left: 50px; + top: var(--slide-divider-top); + width: calc(100% - 100px); + height: 2px; + z-index: 2; +} +.slide-divider img { + width: 100%; + height: 100%; + display: block; +} + +/* ── 본문 영역 ── */ +.slide-body { + position: absolute; + left: var(--slide-padding-x); + top: var(--slide-body-top); + width: calc(100% - var(--slide-padding-x) * 2); + height: var(--slide-body-height); + z-index: 1; + overflow: hidden; +} + +/* ── 하단 footer pill ── */ +.slide-footer { + position: absolute; + left: 50px; + bottom: var(--slide-footer-bottom); + width: calc(100% - 100px); + height: var(--slide-footer-height); + border-radius: 999px; + overflow: hidden; + z-index: 2; + display: flex; + align-items: center; + justify-content: center; +} +.slide-footer-bg { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 999px; + z-index: 0; +} +.slide-footer--css { + background: linear-gradient(90deg, var(--color-footer-bg-from) 5%, var(--color-footer-bg-mid) 50%, var(--color-footer-bg-to) 95%); +} + +/* ── footer 텍스트 ── */ +.slide-footer-text { + position: relative; + z-index: 1; + font-size: var(--font-footer); + font-weight: var(--weight-footer); + line-height: var(--lh-footer); + color: var(--color-footer-text); + text-align: center; + text-shadow: 0 0 4px rgba(0,0,0,0.5); + letter-spacing: -0.01em; +} +.slide-footer-text em { + color: var(--color-footer-accent); + font-style: normal; +} diff --git a/templates/styles/tokens/colors.css b/templates/styles/tokens/colors.css new file mode 100644 index 0000000..e176c80 --- /dev/null +++ b/templates/styles/tokens/colors.css @@ -0,0 +1,76 @@ +/* ══════════════════════════════════════ + Color Tokens v1 + ══════════════════════════════════════ + 2층 구조: 공통 테마색 + 블록 의미색. + ══════════════════════════════════════ */ + +:root { + /* ══ 1층: 공통 테마색 ══ */ + /* 모든 블록이 공유하는 기본 팔레트 */ + + /* 텍스트 */ + --color-title: #1e293b; /* 제목 (진한 남색) */ + --color-zone-title: #1a365d; /* zone 중목차 제목 */ + --color-body: #475569; /* 본문 */ + --color-body-strong: #1e293b; /* 본문 강조 */ + --color-caption: #94a3b8; /* 캡션, 보조 */ + --color-muted: #64748b; /* 약한 텍스트 */ + + /* 배경 */ + --color-bg: #ffffff; /* 슬라이드 배경 */ + --color-bg-subtle: #f8fafc; /* 카드/영역 배경 */ + --color-bg-page: #e8ecf0; /* 페이지 배경 (슬라이드 바깥) */ + + /* 보더 */ + --color-border: #e2e8f0; /* 기본 보더 */ + --color-border-strong: #cbd5e1; /* 강한 보더 */ + + /* 강조 */ + --color-accent: #2563eb; /* 링크, 강조 (파랑) */ + --color-accent-hover: #eff6ff; /* 링크 hover 배경 */ + --color-danger: #dc2626; /* 경고, 에러 */ + + /* slide-base 전용 */ + --color-slide-title-from: #296b55; /* 대목차 gradient 시작 */ + --color-slide-title-to: #123328; /* 대목차 gradient 끝 */ + --color-footer-bg-from: #3b3523; /* footer gradient 시작 */ + --color-footer-bg-mid: #263a2a; /* footer gradient 중간 */ + --color-footer-bg-to: #113f31; /* footer gradient 끝 */ + --color-footer-text: #ffffff; /* footer 텍스트 */ + --color-footer-accent: #ffee33; /* footer 강조 (em) */ + + /* ══ 2층: 블록 의미색 ══ */ + /* 특정 블록 유형에서 의미를 가지는 색 */ + + /* 3열 비교 (prerequisites-3col) */ + --color-col-1-from: #0D78D0; /* 1열 gradient 시작 */ + --color-col-1-to: #023056; /* 1열 gradient 끝 */ + --color-col-2-from: #FF9A23; /* 2열 gradient 시작 */ + --color-col-2-to: #CC5200; /* 2열 gradient 끝 */ + --color-col-3-from: #39BE49; /* 3열 gradient 시작 */ + --color-col-3-to: #23742C; /* 3열 gradient 끝 */ + + /* 비교 블록 (compare) */ + --color-compare-left: #5c3714; /* 좌측 (AS-IS 계열) */ + --color-compare-right: #285b4a; /* 우측 (TO-BE 계열) */ + --color-compare-badge: #ae3607; /* VS 뱃지 */ + + /* 다크 카드 (direct render) */ + --color-dark-card-1: #1a365d; /* 카드 1 배경 */ + --color-dark-card-2: #1e3a2f; /* 카드 2 배경 */ + --color-dark-card-3: #3b1f2b; /* 카드 3 배경 */ + --color-dark-card-title: #fbbf24; /* 다크카드 제목 (금색) */ + --color-dark-card-body: #e2e8f0; /* 다크카드 본문 */ + + /* 표 */ + --color-table-header-bg: #64748b; /* 표 헤더 배경 */ + --color-table-header-text: #ffffff; /* 표 헤더 텍스트 */ + + /* 블록 제목 gradient (Figma 원본) */ + --color-block-title-from: #CC5200; /* 주황 gradient 시작 */ + --color-block-title-to: #883700; /* 주황 gradient 끝 */ + + /* pill */ + --color-pill-bg: #1e293b; /* pill 배경 */ + --color-pill-text: #ffffff; /* pill 텍스트 */ +} diff --git a/templates/styles/tokens/spacing.css b/templates/styles/tokens/spacing.css new file mode 100644 index 0000000..5f1afab --- /dev/null +++ b/templates/styles/tokens/spacing.css @@ -0,0 +1,47 @@ +/* ══════════════════════════════════════ + Spacing Tokens v1 + ══════════════════════════════════════ + 여백/간격/들여쓰기 공통 기준. + ══════════════════════════════════════ */ + +:root { + /* ── 기본 스케일 ── */ + --space-xs: 4px; + --space-sm: 8px; + --space-md: 12px; + --space-lg: 16px; + --space-xl: 24px; + + /* ── slide-base 레이아웃 ── */ + --slide-padding-x: 40px; /* 본문 좌우 여백 */ + --slide-title-left: 52px; /* 대목차 시작 */ + --slide-title-top: 22px; /* 대목차 위치 */ + --slide-divider-top: 58px; /* 구분선 위치 */ + --slide-body-top: 65px; /* 본문 시작 */ + --slide-body-height: 575px; /* 본문 높이 */ + --slide-footer-bottom: 18px; /* footer 하단 여백 */ + --slide-footer-height: 41px; /* footer 높이 */ + + /* ── zone ── */ + --zone-gap: 8px; /* zone 간 간격 */ + --zone-padding-left: 12px; /* zone 안쪽 좌측 여백 */ + --zone-padding-right: 8px; /* zone 안쪽 우측 여백 */ + --zone-title-mb: 8px; /* zone 제목 아래 여백 */ + + /* ── 블록 공통 ── */ + --card-padding: 16px; /* 카드 내부 여백 */ + --card-padding-sm: 8px; /* 작은 카드 내부 여백 */ + --card-gap: 12px; /* 카드 간 간격 */ + --card-radius: 6px; /* 카드 모서리 */ + + /* ── 블릿/텍스트 ── */ + --bullet-indent: 14px; /* 불릿 hanging indent */ + --bullet-gap: 2px; /* 불릿 항목 간 간격 */ + --heading-gap: 4px; /* 소제목 아래 간격 */ + --section-gap: 10px; /* 섹션 간 간격 */ + + /* ── flex/grid 기본 ── */ + --flex-gap: 12px; /* 기본 flex gap */ + --flex-gap-sm: 6px; /* 좁은 flex gap */ + --grid-gap: 16px; /* 기본 grid gap */ +} diff --git a/templates/styles/tokens/typography.css b/templates/styles/tokens/typography.css new file mode 100644 index 0000000..36ab36a --- /dev/null +++ b/templates/styles/tokens/typography.css @@ -0,0 +1,77 @@ +/* ══════════════════════════════════════ + Typography Tokens v1 + ══════════════════════════════════════ + 슬라이드 전체의 공통 글자 위계. + 모든 블록은 이 토큰을 참조한다. + 직접값(font-size: 11px) 금지 → var(--font-body) 사용. + ══════════════════════════════════════ */ + +:root { + /* ── 1층: Global Hierarchy ── */ + + /* 대목차: 슬라이드 상단 제목 */ + --font-slide-title: 22px; + --weight-slide-title: 700; + --lh-slide-title: 1.4; + + /* 중목차: zone 제목 */ + --font-zone-title: 13px; + --weight-zone-title: 700; + --lh-zone-title: 1.4; + + /* 소목차: 블록 내 소제목, 카드 제목 */ + --font-sub-title: 12px; + --weight-sub-title: 700; + --lh-sub-title: 1.45; + + /* 본문: 블릿, 설명 텍스트 */ + --font-body: 11px; + --weight-body: 500; + --lh-body: 1.55; + + /* 캡션: 각주, 출처, 보조 텍스트 */ + --font-caption: 10px; + --weight-caption: 400; + --lh-caption: 1.4; + + /* footer: 핵심 인사이트 pill */ + --font-footer: 20px; + --weight-footer: 700; + --lh-footer: 1.2; + + /* ── 2층: Component Semantic Tokens ── */ + /* 가까운 위계에서 기본값, 필요시 override */ + + /* 본문 강조 (인라인 heading) */ + --font-body-strong: var(--font-body); + --weight-body-strong: 600; + + /* 자세히보기 링크 */ + --font-detail-link: var(--font-caption); + --weight-detail-link: 500; + + /* pill/badge 라벨 */ + --font-pill-label: var(--font-sub-title); + --weight-pill-label: 700; + + /* 표 헤더 */ + --font-table-header: var(--font-sub-title); + --weight-table-header: 700; + + /* 표 셀 */ + --font-table-cell: var(--font-body); + --weight-table-cell: 400; + + /* 비교 뱃지 */ + --font-compare-badge: var(--font-sub-title); + --weight-compare-badge: 700; + + /* 강조 인용 */ + --font-callout: var(--font-sub-title); + --weight-callout: 700; + + /* 상단 라벨 (overline) */ + --font-overline: var(--font-caption); + --weight-overline: 600; + --ls-overline: 0.05em; +}