Some checks are pending
ci / build (push) Waiting to run
Measured convergence before: split hero 13/19, top-bar nav 19/19, one-line notice footer 14/19, body Pretendard 17/21, 10 similarity pairs above 0.78. Each showcase got a pre-assigned hero family, nav form, footer form, type pair, background, accent hue, radius, motion grammar and material from outputs/rework-2026-10-06/plan.md; direction cards and the chosen reason are recorded in every design.md. After: 0 similarity warnings, 19 distinct display faces, 7 hero families, 5 nav forms, 5 footer forms, no scroll reveals, 5 showcases with SVG filter materials. Example contact values replace bracket placeholders under the existing fictional-brand notices. Thumbnails, OG images, card copy and three e2e assertions updated; dead CSS in dure-enrollment and hannam-jari removed; similarity.mjs learns the new font families.
235 lines
16 KiB
HTML
235 lines
16 KiB
HTML
<!doctype html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>한성활자연구소 — 한글 본문 서체 시험대</title>
|
|
<meta name="description" content="마루 부리와 Pretendard 두 한글 본문 서체를 글자 하나의 굵기 축에서 비교하고, 같은 문장을 글자 크기·자간·행간을 바꿔 가며 조판해 보는 가상 서체 연구소." />
|
|
<meta property="og:title" content="한성활자연구소 — 한글 본문 서체 시험대" />
|
|
<meta property="og:description" content="마루 부리와 Pretendard를 글자 하나의 굵기 축에서 비교하고, 같은 문장을 크기·자간·행간을 바꿔 조판해 봅니다." />
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:image" content="./og.png" />
|
|
<meta name="twitter:card" content="summary_large_image" />
|
|
<meta name="theme-color" content="#f1f2f4" />
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
|
<link rel="preload" href="./assets/fonts/MaruBuri-Regular.woff2" as="font" type="font/woff2" crossorigin />
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR&family=Noto+Serif+KR:wght@900&display=swap" />
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css" />
|
|
<link rel="stylesheet" href="./styles/fonts.css?v=20261006" />
|
|
<link rel="stylesheet" href="./styles/main.css?v=20261006" />
|
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%23f1f2f4'/%3E%3Crect x='7' y='7' width='18' height='18' fill='%2322235a'/%3E%3C/svg%3E" />
|
|
</head>
|
|
<body data-face="buri">
|
|
<a class="skip" href="#main">본문으로 건너뛰기</a>
|
|
<header class="site-head">
|
|
<div class="wrap head-inner">
|
|
<a class="wordmark" href="#main">한성활자연구소</a>
|
|
<nav class="face-nav" aria-label="서체 보기">
|
|
<div class="tabs" role="tablist" aria-label="보는 서체">
|
|
<button type="button" class="tab" role="tab" id="tab-buri" data-face="buri" aria-selected="true" aria-controls="face-panel" tabindex="0">마루 부리</button>
|
|
<button type="button" class="tab" role="tab" id="tab-godic" data-face="godic" aria-selected="false" aria-controls="face-panel" tabindex="-1">Pretendard</button>
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
<main id="main">
|
|
<section class="hero" aria-labelledby="hero-h">
|
|
<div class="wrap hero-grid">
|
|
<div class="hero-title">
|
|
<h1 id="hero-h">서체 시험대</h1>
|
|
<p class="hero-lead">한글 본문 서체 두 종을 글자 하나의 굵기 축에서 보고, 같은 문장을 크기·자간·행간을 바꿔 가며 조판합니다.</p>
|
|
</div>
|
|
<div class="stage" id="face-panel" role="tabpanel" aria-labelledby="tab-buri">
|
|
<p class="glyph" id="glyph" aria-hidden="true">활</p>
|
|
<div class="axis">
|
|
<div class="control">
|
|
<div class="control-head"><label for="ctl-weight">굵기</label><output id="out-weight" for="ctl-weight">400</output></div>
|
|
<input type="range" id="ctl-weight" name="weight" min="400" max="600" step="200" value="400" />
|
|
</div>
|
|
<div class="control control-glyph">
|
|
<label for="ctl-glyph">글자</label>
|
|
<input type="text" id="ctl-glyph" name="glyph" value="활" maxlength="1" autocomplete="off" spellcheck="false" />
|
|
</div>
|
|
<p id="type-status" class="type-status" aria-live="polite">마루 부리 400. 네이버가 배포한 부리 서체이며 정적 굵기 가운데 Regular와 SemiBold 두 종을 실었습니다.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="typefaces" class="section" aria-labelledby="typefaces-h">
|
|
<div class="wrap">
|
|
<div class="section-head">
|
|
<h2 id="typefaces-h">같은 문장을 22px, 행간 1.8, 같은 폭으로 두 서체에 조판했습니다.</h2>
|
|
<p class="section-note">서체마다 글자가 보이는 크기는 다를 수 있습니다. 줄 길이와 줄바꿈 자리를 비교해 보세요.</p>
|
|
</div>
|
|
<div class="comparison">
|
|
<article class="typeface" data-face="buri">
|
|
<header class="typeface-head">
|
|
<h3>마루 부리</h3>
|
|
<p class="muted">부리 계열. 네이버 배포, SIL 오픈 폰트 라이선스. 이 화면에는 정적 굵기 400·600 두 종을 실었습니다.</p>
|
|
</header>
|
|
<p class="specimen specimen-buri">아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 마을 어귀 느티나무 아래에서는 할머니 두 분이 멍석에 고추를 널고 있었다.</p>
|
|
</article>
|
|
<article class="typeface" data-face="godic">
|
|
<header class="typeface-head">
|
|
<h3>Pretendard</h3>
|
|
<p class="muted">민부리 계열. 가변 서체라 굵기 축을 45부터 920까지 연속으로 바꿀 수 있습니다. SIL 오픈 폰트 라이선스.</p>
|
|
</header>
|
|
<p class="specimen specimen-godic">아침 안개가 걷히기 전에 도랑물 소리가 먼저 들렸다. 마을 어귀 느티나무 아래에서는 할머니 두 분이 멍석에 고추를 널고 있었다.</p>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="bench" class="section band" aria-labelledby="bench-h">
|
|
<div class="wrap">
|
|
<div class="section-head">
|
|
<h2 id="bench-h">글자 크기 13~26px, 자간, 행간 1.4~2.4를 바꾸면 줄바꿈이 바로 달라집니다.</h2>
|
|
<p class="section-note">웹 본문은 보통 16~18px, 한글 행간은 1.6~1.8배에서 시작합니다. 기본값으로 되돌리면 17px, 자간 0em, 행간 1.8로 돌아갑니다.</p>
|
|
</div>
|
|
<div class="bench">
|
|
<form class="bench-controls" aria-label="조판 설정">
|
|
<div class="control"><label for="ctl-font">글꼴</label><select id="ctl-font" name="font"><option value="display">마루 부리</option><option value="body">Pretendard</option></select></div>
|
|
<div class="control"><div class="control-head"><label for="ctl-size">글자 크기</label><output id="out-size" for="ctl-size">17px</output></div><input type="range" id="ctl-size" name="size" min="13" max="26" step="1" value="17" /></div>
|
|
<div class="control"><div class="control-head"><label for="ctl-tracking">자간</label><output id="out-tracking" for="ctl-tracking">0em</output></div><input type="range" id="ctl-tracking" name="tracking" min="-5" max="10" step="1" value="0" /></div>
|
|
<div class="control"><div class="control-head"><label for="ctl-leading">행간</label><output id="out-leading" for="ctl-leading">1.8</output></div><input type="range" id="ctl-leading" name="leading" min="1.4" max="2.4" step="0.1" value="1.8" /></div>
|
|
<button type="button" class="btn-reset" id="ctl-reset">기본값으로</button>
|
|
</form>
|
|
<div class="bench-reading">
|
|
<p id="bench-caption" class="bench-caption">마루 부리, 17px, 자간 0em, 행간 1.8</p>
|
|
<p class="bench-disclaimer">아래 문단은 조판 견본글입니다.</p>
|
|
<div class="bench-sample" id="bench-sample" aria-live="polite">웹 본문 글자는 보통 16~18px로 정하고, 한글 행간은 글자 크기의 1.6~1.8배 정도로 잡습니다. 자간은 0을 기준으로 큰 제목에서는 조금 좁히고, 작은 글자에서는 조금 넓히기도 합니다. 같은 문단이라도 서체에 따라 한 줄에 들어가는 글자 수가 달라집니다. 자간을 0em에서 0.05em으로 바꾸면 줄 길이와 줄바꿈 위치가 어떻게 달라지는지 확인해 보세요.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="spacing" class="section" aria-labelledby="spacing-h">
|
|
<div class="wrap spacing-grid">
|
|
<div class="spacing-text">
|
|
<h2 id="spacing-h">납활자 옆면의 여백이 화면에서는 자간 값이 됩니다.</h2>
|
|
<p>납활자는 글자 몸통 양옆에 일정한 여백이 있어 찍힌 글자 사이 간격이 고르게 나왔습니다. 디지털 서체도 글자마다 옆면 여백을 갖고 있고, 자간은 그 여백에 더하거나 빼는 값입니다. 아래는 같은 네 글자를 자간 -0.05em과 0.1em으로 놓은 것입니다. 한글은 자간을 넓히면 낱글자로 흩어지기 쉬워 본문에서는 0 근처에서 시작합니다.</p>
|
|
</div>
|
|
<figure class="track-demo" aria-labelledby="track-caption">
|
|
<p class="track-line track-tight" aria-hidden="true">활자연구</p>
|
|
<p class="track-line track-loose" aria-hidden="true">활자연구</p>
|
|
<figcaption id="track-caption">위는 자간 -0.05em, 아래는 0.1em. 보는 서체의 굵기 400으로 그렸습니다.</figcaption>
|
|
</figure>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="order" class="section band" aria-labelledby="order-h">
|
|
<div class="wrap order-grid">
|
|
<div class="order-text">
|
|
<h2 id="order-h">라이선스 가격은 웨이트 전종 기준의 예시입니다.</h2>
|
|
<p class="muted">한성활자연구소는 가상 서체 기획입니다. 실제 주문·결제·문의로 이어지지 않으며, 화면의 서체는 네이버 마루 부리와 Pretendard로 구현했습니다.</p>
|
|
</div>
|
|
<div class="order-rows">
|
|
<div class="order-row"><div><h3>웹</h3><p>도메인당, 웨이트 전종, 자체 호스팅 WOFF2</p></div><p class="order-price">140,000원</p></div>
|
|
<div class="order-row"><div><h3>출판</h3><p>도서 권수당, 인쇄와 전자책 동시</p></div><p class="order-price">220,000원</p></div>
|
|
</div>
|
|
<a class="btn" href="#bench">조판해 보기</a>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
<footer class="site-foot">
|
|
<div class="wrap">
|
|
<p class="foot-mark" aria-hidden="true">한성활자연구소</p>
|
|
<p class="foot-note">한성활자연구소는 가상 서체 기획 쇼케이스입니다. 서체와 가격은 예시이며, 화면의 글꼴은 마루 부리와 Pretendard(둘 다 SIL 오픈 폰트 라이선스)로 구현했습니다.</p>
|
|
</div>
|
|
</footer>
|
|
<script>
|
|
const faces = {
|
|
buri: {
|
|
name: "마루 부리",
|
|
benchValue: "display",
|
|
axis: { min: 400, max: 600, step: 200, value: 400 },
|
|
note: "네이버가 배포한 부리 서체이며 정적 굵기 가운데 Regular와 SemiBold 두 종을 실었습니다.",
|
|
},
|
|
godic: {
|
|
name: "Pretendard",
|
|
benchValue: "body",
|
|
axis: { min: 100, max: 900, step: 10, value: 400 },
|
|
note: "가변 서체라 굵기 축을 100부터 900까지 10 단위로 움직입니다.",
|
|
},
|
|
};
|
|
const panel = document.getElementById("face-panel");
|
|
const glyph = document.getElementById("glyph");
|
|
const tabs = [...document.querySelectorAll('[role="tab"]')];
|
|
const weight = document.getElementById("ctl-weight");
|
|
const outWeight = document.getElementById("out-weight");
|
|
const glyphInput = document.getElementById("ctl-glyph");
|
|
const typeStatus = document.getElementById("type-status");
|
|
const reducedMotion = matchMedia("(prefers-reduced-motion: reduce)");
|
|
|
|
function transition(update) {
|
|
if (!document.startViewTransition || reducedMotion.matches) { update(); return; }
|
|
document.startViewTransition(update);
|
|
}
|
|
function currentFace() { return document.body.dataset.face; }
|
|
function describe() {
|
|
const face = faces[currentFace()];
|
|
typeStatus.textContent = `${face.name} ${weight.value}. ${face.note}`;
|
|
outWeight.textContent = weight.value;
|
|
}
|
|
function applyWeight() {
|
|
glyph.style.fontWeight = weight.value;
|
|
describe();
|
|
}
|
|
function setFace(key, { syncBench = true } = {}) {
|
|
const face = faces[key];
|
|
document.body.dataset.face = key;
|
|
tabs.forEach((tab) => {
|
|
const on = tab.dataset.face === key;
|
|
tab.setAttribute("aria-selected", String(on));
|
|
tab.tabIndex = on ? 0 : -1;
|
|
if (on) panel.setAttribute("aria-labelledby", tab.id);
|
|
});
|
|
weight.min = face.axis.min; weight.max = face.axis.max; weight.step = face.axis.step; weight.value = face.axis.value;
|
|
applyWeight();
|
|
if (syncBench) { ctl.font.value = face.benchValue; apply(); }
|
|
}
|
|
tabs.forEach((tab, index) => {
|
|
tab.addEventListener("click", () => { if (tab.dataset.face !== currentFace()) transition(() => setFace(tab.dataset.face)); });
|
|
tab.addEventListener("keydown", (event) => {
|
|
const delta = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
|
|
let next = null;
|
|
if (delta) next = (index + delta + tabs.length) % tabs.length;
|
|
if (event.key === "Home") next = 0;
|
|
if (event.key === "End") next = tabs.length - 1;
|
|
if (next === null) return;
|
|
event.preventDefault();
|
|
tabs[next].focus();
|
|
tabs[next].click();
|
|
});
|
|
});
|
|
weight.addEventListener("input", applyWeight);
|
|
glyphInput.addEventListener("input", () => {
|
|
const chars = [...glyphInput.value.trim()];
|
|
glyph.textContent = chars.length ? chars[0] : "활";
|
|
});
|
|
|
|
const sample = document.getElementById("bench-sample");
|
|
const ctl = { font: document.getElementById("ctl-font"), size: document.getElementById("ctl-size"), tracking: document.getElementById("ctl-tracking"), leading: document.getElementById("ctl-leading") };
|
|
const out = { size: document.getElementById("out-size"), tracking: document.getElementById("out-tracking"), leading: document.getElementById("out-leading") };
|
|
const defaults = { font: "display", size: 17, tracking: 0, leading: 1.8 };
|
|
const fontNames = { display: "마루 부리", body: "Pretendard" };
|
|
function apply() {
|
|
sample.style.fontFamily = ctl.font.value === "display" ? "var(--face-buri)" : "var(--face-godic)";
|
|
sample.style.fontSize = ctl.size.value + "px";
|
|
sample.style.letterSpacing = ctl.tracking.value / 100 + "em";
|
|
sample.style.lineHeight = ctl.leading.value;
|
|
out.size.textContent = ctl.size.value + "px";
|
|
out.tracking.textContent = ctl.tracking.value / 100 + "em";
|
|
out.leading.textContent = Number(ctl.leading.value).toFixed(1);
|
|
document.getElementById("bench-caption").textContent = `${fontNames[ctl.font.value]}, ${ctl.size.value}px, 자간 ${ctl.tracking.value / 100}em, 행간 ${Number(ctl.leading.value).toFixed(1)}`;
|
|
}
|
|
Object.values(ctl).forEach((input) => { input.addEventListener("input", apply); input.addEventListener("change", apply); });
|
|
document.getElementById("ctl-reset").addEventListener("click", () => { ctl.font.value = defaults.font; ctl.size.value = defaults.size; ctl.tracking.value = defaults.tracking; ctl.leading.value = defaults.leading; apply(); });
|
|
setFace("buri", { syncBench: false });
|
|
apply();
|
|
</script>
|
|
</body>
|
|
</html>
|