- 10단계 변환 프로세스 (PROCESS.md) - 수학 공식 레퍼런스 (MATH.md, gradient_math.py) - CSS 보정 규칙 R1~R16 (RULES.md) - 작업 규율 7개 규칙 (PROCESS-CONTROL.md) - 8개 Figma 프레임 1:1 HTML 변환물 (block-tests/) - 8개 Jinja2 템플릿 staging (templates_staging/) - 변환 완료 도서관 + 디자인 인사이트 (blocks_index.md) - 사용법 가이드 (README.md) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
350 lines
12 KiB
Django/Jinja
350 lines
12 KiB
Django/Jinja
<!DOCTYPE html>
|
||
<html lang="ko">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=1280">
|
||
<title>{{ title|default("cycle-3way-intersect") }}</title>
|
||
<!--
|
||
============================================================
|
||
Pattern: cycle-3way-intersect
|
||
Source: figma_to_html_agent/block-tests/bim-goals-3circles.html (1:1 reference)
|
||
Origin frame: 66:310 / Frame 1171281211
|
||
|
||
STRUCTURE: 3개의 큰 원이 교차 (Venn-like)
|
||
- 3 main circles at FIXED positions (top, bot-left, bot-right)
|
||
- 6 accent circles at corners (top-left, top-right, left-top, left-bot, right-top, right-bot)
|
||
- 12 side labels at 6 positions (4 per circle context)
|
||
- 3 connecting arcs (PNG, optional)
|
||
- Background texture (optional)
|
||
- Decoration gradient (optional)
|
||
|
||
POSITION 고정 이유: 3-way intersect는 기하학적으로 결정되는 패턴.
|
||
2-way나 4-way는 별도 패턴 (cycle-2way-intersect, cycle-4way-intersect)으로 분리.
|
||
이 템플릿은 콘텐츠만 변수화 (라벨, 색상, 한자, 설명 등).
|
||
|
||
PRINCIPLES:
|
||
- I-3: 모든 슬롯은 기본 optional (bg, deco, arcs, side_labels 등)
|
||
- 1:1 reference의 transform: scale 전략 유지
|
||
============================================================
|
||
-->
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@500;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
* { margin:0; padding:0; box-sizing:border-box; }
|
||
|
||
:root {
|
||
--block-w: {{ block_w|default(1280) }}px;
|
||
--block-h: {{ block_h|default(594) }}px; /* 957 × 0.620694 */
|
||
--inner-w: 2062.205px;
|
||
--inner-h: 956.998px;
|
||
--scale: {{ scale|default(0.620694) }};
|
||
}
|
||
|
||
body {
|
||
font-family: 'Noto Sans KR', sans-serif;
|
||
background: #e8ecf0;
|
||
display: flex; justify-content: center; align-items: center;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.slide {
|
||
width: var(--block-w);
|
||
height: 720px;
|
||
background: #ffffff;
|
||
position: relative;
|
||
display: flex; justify-content: center; align-items: center;
|
||
box-shadow: 0 4px 20px rgba(0,0,0,.15);
|
||
}
|
||
|
||
.block {
|
||
width: var(--block-w);
|
||
height: var(--block-h);
|
||
background: #ffffff;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.inner {
|
||
position: absolute;
|
||
left: 0; top: 0;
|
||
width: var(--inner-w);
|
||
height: var(--inner-h);
|
||
transform: scale(var(--scale));
|
||
transform-origin: top left;
|
||
}
|
||
|
||
/* ─── 배경 텍스처 (optional) ─── */
|
||
.bg-texture {
|
||
position: absolute;
|
||
left: 139px; top: 683px;
|
||
width: 1768px; height: 274px;
|
||
mix-blend-mode: multiply;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
}
|
||
.bg-texture img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||
|
||
/* ─── 회전 그라데이션 데코 (optional) ─── */
|
||
.deco-wrap {
|
||
position: absolute;
|
||
left: 71px; top: 38px;
|
||
width: 685.475px; height: 836.277px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
}
|
||
.deco-rot { flex: none; transform: rotate(-120deg); }
|
||
.deco-flip { transform: scaleY(-1); }
|
||
.deco-rect {
|
||
width: 763px; height: 351px;
|
||
border-radius: 175.5px 0 0 175.5px;
|
||
background: linear-gradient(235.162deg,
|
||
rgba(115,115,115,0) 14.18%,
|
||
rgba(213,170,137,0.33) 66.964%);
|
||
}
|
||
|
||
/* ─── 연결 아크 (optional) ─── */
|
||
.arc { position: absolute; z-index: 2; display: flex; align-items: center; justify-content: center; }
|
||
.arc img { display: block; flex: none; }
|
||
.arc--top { left: 926px; top: 134px; width: 209px; height: 99px; }
|
||
.arc--top img { width: 209px; height: 99px; transform: rotate(180deg); }
|
||
.arc--left { left: 627px; top: 549px; width: 99px; height: 209px; }
|
||
.arc--left img { width: 209px; height: 99px; transform: rotate(90deg); }
|
||
.arc--right { left: 1329px; top: 549px; width: 99px; height: 209px; }
|
||
.arc--right img { width: 209px; height: 99px; transform: rotate(-90deg); }
|
||
|
||
/* ─── 메인 3원 outer (350×350) ─── */
|
||
.big-outer {
|
||
position: absolute;
|
||
width: 350px; height: 350px;
|
||
border-radius: 50%;
|
||
mix-blend-mode: multiply;
|
||
z-index: 3;
|
||
}
|
||
.big-outer.top { left: 853px; top: 206px; }
|
||
.big-outer.bot-left { left: 694px; top: 480px; }
|
||
.big-outer.bot-right{ left: 1009px; top: 480px; }
|
||
|
||
/* ─── 메인 3원 ring (280 또는 290 with stroke) ─── */
|
||
.big-ring {
|
||
position: absolute;
|
||
border-radius: 50%;
|
||
box-shadow: 0 0 10px 0 rgba(0,0,0,1);
|
||
z-index: 4;
|
||
}
|
||
.big-ring.top {
|
||
left: 882.06px; top: 235.06px;
|
||
width: 290px; height: 290px;
|
||
border: 5px solid #FFFFFF;
|
||
}
|
||
.big-ring.bot-left {
|
||
left: 728.06px; top: 514.06px;
|
||
width: 280px; height: 280px;
|
||
border: 5px solid #FFFFFF;
|
||
background-origin: border-box;
|
||
background-clip: border-box;
|
||
}
|
||
.big-ring.bot-right {
|
||
left: 1043.06px; top: 514.06px;
|
||
width: 280px; height: 280px;
|
||
border: 5px solid #FFFFFF;
|
||
background-origin: border-box;
|
||
background-clip: border-box;
|
||
}
|
||
|
||
/* ─── 메인 원 라벨 (50px Bold) ─── */
|
||
.big-title {
|
||
position: absolute;
|
||
width: 262.923px; height: 114.078px;
|
||
font-weight: 700;
|
||
font-size: 50px;
|
||
line-height: 50px;
|
||
color: #ffffff;
|
||
text-align: center;
|
||
letter-spacing: -2.5px;
|
||
display: flex; flex-direction: column; justify-content: center;
|
||
z-index: 5;
|
||
}
|
||
.big-title p { line-height: 50px; }
|
||
.big-title.top { left: 896.46px; top: 330.49px; }
|
||
.big-title.bot-left { left: 737.46px; top: 604.49px; }
|
||
.big-title.bot-right{ left: 1053.63px; top: 597.45px; }
|
||
|
||
/* ─── 액센트 6원 (130.9 outer + 82.965 inner with border) ─── */
|
||
.acc-outer {
|
||
position: absolute;
|
||
width: 130.901px; height: 130.901px;
|
||
border-radius: 50%;
|
||
opacity: 0.3;
|
||
mix-blend-mode: multiply;
|
||
z-index: 6;
|
||
}
|
||
.acc-outer.top-left { left: 859px; top: 22px; }
|
||
.acc-outer.top-right { left: 1071px; top: 22px; }
|
||
.acc-outer.left-top { left: 555px; top: 483px; }
|
||
.acc-outer.left-bot { left: 555px; top: 689px; }
|
||
.acc-outer.right-top { left: 1372px; top: 483px; }
|
||
.acc-outer.right-bot { left: 1372px; top: 689px; }
|
||
|
||
.acc-inner {
|
||
position: absolute;
|
||
width: 82.965px; height: 82.965px;
|
||
border: 2px solid #FFFFFF;
|
||
border-radius: 50%;
|
||
box-shadow: 0 0 10px 0 rgba(0,0,0,1);
|
||
z-index: 7;
|
||
}
|
||
.acc-inner.top-left { left: 882.47px; top: 45.47px; }
|
||
.acc-inner.top-right { left: 1094.47px; top: 45.47px; }
|
||
.acc-inner.left-top { left: 578.47px; top: 506.47px; }
|
||
.acc-inner.left-bot { left: 578.47px; top: 712.47px; }
|
||
.acc-inner.right-top { left: 1395.47px; top: 506.47px; }
|
||
.acc-inner.right-bot { left: 1395.47px; top: 712.47px; }
|
||
|
||
.acc-text {
|
||
position: absolute;
|
||
width: 98.334px; height: 42.666px;
|
||
font-weight: 700;
|
||
font-size: 45px;
|
||
line-height: 50px;
|
||
color: #ffffff;
|
||
text-align: center;
|
||
letter-spacing: -2.25px;
|
||
text-shadow: 0 0 5px #cc5200;
|
||
display: flex; align-items: center; justify-content: center;
|
||
z-index: 8;
|
||
}
|
||
.acc-text.top-left { left: 875.25px; top: 63.60px; }
|
||
.acc-text.top-right { left: 1087.25px; top: 63.60px; }
|
||
.acc-text.left-top { left: 571.25px; top: 524.60px; }
|
||
.acc-text.left-bot { left: 571.25px; top: 730.60px; }
|
||
.acc-text.right-top { left: 1388.25px; top: 524.60px; }
|
||
.acc-text.right-bot { left: 1388.25px; top: 730.60px; }
|
||
|
||
/* ─── 사이드 텍스트 라벨 (12개, 6 위치 × 2 (title+desc)) ─── */
|
||
.side-title {
|
||
position: absolute;
|
||
font-weight: 700;
|
||
font-size: 40px;
|
||
line-height: 95px;
|
||
white-space: nowrap;
|
||
z-index: 5;
|
||
}
|
||
.side-desc {
|
||
position: absolute;
|
||
font-weight: 500;
|
||
font-size: 30px;
|
||
color: #525151;
|
||
z-index: 5;
|
||
}
|
||
.side-desc p { line-height: 35px; padding-bottom: 5px; }
|
||
|
||
/* 상단 (top) — 안전성 제고 / 품질 향상 */
|
||
.side-title.top-left { left: 645px; top: 0; width: 194px; text-align: right; }
|
||
.side-title.top-right { left: 1233px; top: 0; width: 157px; text-align: right; }
|
||
.side-desc.top-left { left: 331px; top: 78px; width: 508.478px; text-align: right; }
|
||
.side-desc.top-right { left: 1233px; top: 78px; width: 508.478px; }
|
||
|
||
/* 좌측 (mid-left) — 신속·정확성 / 비용저감 */
|
||
.side-title.mid-left { left: 0; top: 456px; width: 531.393px; height: 95.345px; text-align: right; white-space: nowrap; }
|
||
.side-title.bot-left {
|
||
left: 84.67px; top: 661.79px; width: 446.049px; height: 96.418px;
|
||
display: flex; flex-direction: column; justify-content: center;
|
||
text-align: right;
|
||
}
|
||
.side-desc.mid-left { left: 52.01px; top: 546px; width: 478.731px; height: 70.561px; text-align: right; }
|
||
.side-desc.bot-left {
|
||
left: 102.02px; top: 734px; width: 428.315px; height: 91.343px;
|
||
display: flex; flex-direction: column; justify-content: center;
|
||
text-align: right;
|
||
}
|
||
|
||
/* 우측 (mid-right) — 소통·이해 / 신뢰·투명성 */
|
||
.side-title.mid-right { left: 1518px; top: 456px; width: 267.016px; }
|
||
.side-title.bot-right { left: 1518px; top: 664px; width: 304.016px; }
|
||
.side-desc.mid-right { left: 1518px; top: 541px; width: 429.012px; white-space: nowrap; }
|
||
.side-desc.bot-right {
|
||
left: 1518px; top: 754.21px; width: 544.198px; height: 70.561px;
|
||
display: flex; flex-direction: column; justify-content: center;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="slide">
|
||
<div class="block">
|
||
<div class="inner">
|
||
|
||
{# ─── Optional 배경 텍스처 ─── #}
|
||
{% if bg_texture %}
|
||
<div class="bg-texture"><img src="{{ bg_texture }}" alt=""></div>
|
||
{% endif %}
|
||
|
||
{# ─── Optional 데코 그라데이션 ─── #}
|
||
{% if decoration %}
|
||
<div class="deco-wrap">
|
||
<div class="deco-rot"><div class="deco-flip"><div class="deco-rect"></div></div></div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# ─── Optional 연결 아크 3개 ─── #}
|
||
{% if arcs %}
|
||
<div class="arc arc--top"><img src="{{ arcs.top }}" alt=""></div>
|
||
<div class="arc arc--left"><img src="{{ arcs.left }}" alt=""></div>
|
||
<div class="arc arc--right"><img src="{{ arcs.right }}" alt=""></div>
|
||
{% endif %}
|
||
|
||
{# ─── 메인 3원 outer ─── #}
|
||
{% for pos, c in [('top', circles.top), ('bot-left', circles.bot_left), ('bot-right', circles.bot_right)] %}
|
||
<div class="big-outer {{ pos }}"
|
||
style="background: linear-gradient({{ c.fill.angle|default('145deg') }}, {{ c.fill.from }} {{ c.fill.from_pct|default('15%') }}, {{ c.fill.to }} {{ c.fill.to_pct|default('60%') }});">
|
||
</div>
|
||
<div class="big-ring {{ pos }}"
|
||
style="background: linear-gradient({{ c.ring.angle|default('150deg') }}, {{ c.ring.from }} {{ c.ring.from_pct|default('15%') }}, {{ c.ring.to }} {{ c.ring.to_pct|default('60%') }});">
|
||
</div>
|
||
<div class="big-title {{ pos }}"
|
||
{% if c.text_shadow %}style="text-shadow: 0 0 5px {{ c.text_shadow }};"{% endif %}>
|
||
<p>{{ c.label[0] }}</p><p>{{ c.label[1] }}</p>
|
||
</div>
|
||
{% endfor %}
|
||
|
||
{# ─── 액센트 6원 (3 main circles × 2 corners each) ─── #}
|
||
{% for pos, a in [
|
||
('top-left', accents.top_left), ('top-right', accents.top_right),
|
||
('left-top', accents.left_top), ('left-bot', accents.left_bot),
|
||
('right-top', accents.right_top), ('right-bot', accents.right_bot)] %}
|
||
{% if a %}
|
||
<div class="acc-outer {{ pos }}"
|
||
style="background: linear-gradient({{ a.outer.angle|default('145deg') }}, {{ a.outer.from }} {{ a.outer.from_pct|default('10%') }}, {{ a.outer.to }} {{ a.outer.to_pct|default('85%') }});">
|
||
</div>
|
||
<div class="acc-inner {{ pos }}"
|
||
style="background: linear-gradient({{ a.inner.angle|default('150deg') }}, {{ a.inner.from }} {{ a.inner.from_pct|default('15%') }}, {{ a.inner.to }} {{ a.inner.to_pct|default('60%') }});">
|
||
</div>
|
||
<div class="acc-text {{ pos }}">{{ a.char }}</div>
|
||
{% endif %}
|
||
{% endfor %}
|
||
|
||
{# ─── 사이드 라벨 6 위치 × (title + desc) = 최대 12개 ─── #}
|
||
{% for pos, lbl in [
|
||
('top-left', side_labels.top_left), ('top-right', side_labels.top_right),
|
||
('mid-left', side_labels.mid_left), ('mid-right', side_labels.mid_right),
|
||
('bot-left', side_labels.bot_left), ('bot-right', side_labels.bot_right)] %}
|
||
{% if lbl %}
|
||
{% if lbl.title %}
|
||
<div class="side-title {{ pos }}" style="color: {{ lbl.color|default('#000') }};">{{ lbl.title }}</div>
|
||
{% endif %}
|
||
{% if lbl.desc %}
|
||
<div class="side-desc {{ pos }}">
|
||
{% for line in lbl.desc %}<p>{{ line }}</p>{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
{% endif %}
|
||
{% endfor %}
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</body>
|
||
</html>
|