h2 '라틴 기준으로 만든 도구는 한글에서 멈춘다' 는 무엇이 어떻게 멈추는지
말하지 않았다. 그리고 데모가 그 주장을 증명하지 못했다.
데모가 실제로 실패하고 있었다
word-break: break-all 과 keep-all 을 나란히 두고 '차이는 한 줄뿐'이라고
적었는데, 재보니 줄 수 3 · 높이 87px 로 완전히 동일했다.
어절이 짧아 두 쪽이 같은 자리에서 끊긴 것이다. 차이가 0 인 비교는
증명이 아니라 주장의 반증이다.
개편
h2 -> '라틴 기본값은 한글에서 세 군데가 깨진다'
수치가 데모 개수와 정확히 대응한다
데모 -> 셋으로. 각각 실제로 갈린다
줄바꿈 break-all vs keep-all (긴 어절 + 15rem 고정폭)
행간 1.45 vs 1.75 (높이 74 vs 89)
크기 11px vs 15px (2줄 vs 3줄)
노트도 고쳤다. '로스팅프로파일이 갈린다'고 썼는데 실제로 갈린 건 다른
어절이었다 — 폭에 따라 바뀌므로 어절을 지목하지 않고 현상만 말한다.
스킬 antipatterns.md
'데모가 주장을 증명하는지 재라' 추가.
비교 데모의 높이·줄 수를 실제로 재는 코드와, 차이가 안 날 때의 대처.
This commit is contained in:
parent
61fb0cdcfa
commit
31c0690d4b
3 changed files with 175 additions and 52 deletions
|
|
@ -13,18 +13,34 @@ const { c } = Astro.props;
|
|||
<p class="eyebrow reveal">{c.eyebrow}</p>
|
||||
<h2 id="type-h" class="reveal">{c.h2}</h2>
|
||||
<p class="lead type-lead reveal">{c.lead}</p>
|
||||
<p class="type-body reveal">{c.body}</p>
|
||||
|
||||
{/* 주장을 글로 하지 않고 실물로 보여준다. 두 문단의 차이는 word-break 한 줄뿐이다 */}
|
||||
<div class="demo reveal">
|
||||
<div class="demo-col"><div class="demo-inner">
|
||||
<p class="demo-label muted mono">{c.demoLabels[0]}</p>
|
||||
<p lang="ko" class="demo-text demo-default">{c.demoText}</p>
|
||||
</div></div>
|
||||
<div class="demo-col"><div class="demo-inner">
|
||||
<p class="demo-label muted mono">{c.demoLabels[1]}</p>
|
||||
<p lang="ko" class="demo-text demo-keepall">{c.demoText}</p>
|
||||
</div></div>
|
||||
{/*
|
||||
주장을 글로 하지 않고 실물로 보여준다.
|
||||
|
||||
한때 word-break 하나만 두 칼럼으로 비교했는데, 어절이 짧아 두 쪽이
|
||||
같은 자리에서 끊겼다 — 줄 수도 높이도 똑같았다. 차이가 0 인 비교는
|
||||
증명이 아니라 주장의 반증이다. 셋으로 늘리고, 잘리는 긴 어절을 넣고,
|
||||
폭을 좁혀 실제로 갈리게 만들었다.
|
||||
*/}
|
||||
<div class="full demos reveal">
|
||||
{
|
||||
c.demos.map((d) => (
|
||||
<section class="demo" aria-label={d.title}>
|
||||
<h3 class="demo-title">{d.title}</h3>
|
||||
<div class="demo-pair">
|
||||
<div class="demo-side">
|
||||
<p class="demo-tag mono is-bad">{d.badLabel}</p>
|
||||
<p lang="ko" class:list={["demo-text", `bad-${d.id}`]}>{c.sample}</p>
|
||||
</div>
|
||||
<div class="demo-side">
|
||||
<p class="demo-tag mono is-good">{d.goodLabel}</p>
|
||||
<p lang="ko" class:list={["demo-text", `good-${d.id}`]}>{c.sample}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="demo-note muted">{d.note}</p>
|
||||
</section>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
<ul class="type-rules reveal">
|
||||
|
|
@ -33,41 +49,81 @@ const { c } = Astro.props;
|
|||
</section>
|
||||
|
||||
<style>
|
||||
.type-lead { margin-bottom: var(--space-4); }
|
||||
.type-body { margin-bottom: var(--space-5); }
|
||||
.type-lead { margin-bottom: var(--space-6); max-width: var(--measure); }
|
||||
|
||||
.demo {
|
||||
.demos {
|
||||
max-width: 1180px;
|
||||
margin-inline: auto;
|
||||
padding-inline: var(--gutter);
|
||||
display: grid;
|
||||
gap: var(--space-4);
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(13rem, 100%), 1fr));
|
||||
margin-bottom: var(--space-5);
|
||||
gap: var(--space-6);
|
||||
}
|
||||
.demo-inner { padding: var(--space-4); }
|
||||
.demo-label {
|
||||
font-size: var(--mono-size);
|
||||
|
||||
.demo-title {
|
||||
font-size: var(--step-1);
|
||||
margin-bottom: var(--space-3);
|
||||
}
|
||||
|
||||
.demo-pair {
|
||||
display: grid;
|
||||
/* 폭을 좁게 고정한다. 넓으면 두 쪽이 같은 자리에서 끊겨 차이가 사라진다 */
|
||||
grid-template-columns: repeat(2, minmax(0, 15rem));
|
||||
gap: var(--space-5);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.demo-side { min-width: 0; }
|
||||
|
||||
.demo-tag {
|
||||
font-size: var(--step--1);
|
||||
letter-spacing: var(--tracking-label);
|
||||
margin-bottom: var(--space-2);
|
||||
padding-bottom: var(--space-1);
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.is-bad { color: var(--ink-muted); }
|
||||
.is-good { color: var(--accent); border-bottom-color: var(--accent); }
|
||||
|
||||
.demo-text {
|
||||
margin: 0;
|
||||
/* 줄바꿈 차이가 드러나도록 폭을 좁게 고정한다 */
|
||||
max-width: 13em;
|
||||
line-height: 1.7;
|
||||
font-size: var(--step-0);
|
||||
line-height: var(--leading-normal);
|
||||
word-break: keep-all;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* ---- 셋의 실제 차이 ------------------------------------- */
|
||||
/* 1) 줄바꿈 — 기본값은 어절 한가운데를 끊는다 */
|
||||
.bad-break { word-break: break-all; }
|
||||
.good-break { word-break: keep-all; }
|
||||
|
||||
/* 2) 행간 — 라틴 표준을 그대로 쓰면 받침이 붙어 보인다 */
|
||||
.bad-leading { line-height: 1.45; }
|
||||
.good-leading { line-height: 1.75; }
|
||||
|
||||
/* 3) 크기 — 라틴의 라벨 크기는 한글에서 획이 뭉갠다 */
|
||||
.bad-size { font-size: 11px; }
|
||||
.good-size { font-size: 15px; }
|
||||
|
||||
.demo-note {
|
||||
margin: var(--space-3) 0 0;
|
||||
font-size: var(--step-0);
|
||||
max-width: var(--measure);
|
||||
}
|
||||
/* 기본값 — 단어 중간에서 끊긴다 */
|
||||
.demo-default { word-break: break-all; }
|
||||
/* 우리 규칙 */
|
||||
.demo-keepall { word-break: keep-all; overflow-wrap: break-word; }
|
||||
|
||||
.type-rules {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
margin: var(--space-7) 0 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
gap: 0;
|
||||
}
|
||||
.type-rules li {
|
||||
padding-block: var(--space-2);
|
||||
padding-block: var(--space-3);
|
||||
border-top: 1px solid var(--line);
|
||||
color: var(--ink-muted);
|
||||
}
|
||||
.type-rules li:last-child { border-bottom: 1px solid var(--line); }
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.demo-pair { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -74,9 +74,14 @@ export interface Content {
|
|||
eyebrow: string;
|
||||
h2: string;
|
||||
lead: string;
|
||||
body: string;
|
||||
demoLabels: [string, string];
|
||||
demoText: string;
|
||||
sample: string;
|
||||
demos: {
|
||||
id: "break" | "leading" | "size";
|
||||
title: string;
|
||||
badLabel: string;
|
||||
goodLabel: string;
|
||||
note: string;
|
||||
}[];
|
||||
rules: string[];
|
||||
};
|
||||
rules: { h2: string; lead: string; items: Rule[] };
|
||||
|
|
@ -337,13 +342,33 @@ export const ko: Content = {
|
|||
},
|
||||
typography: {
|
||||
eyebrow: "한글 조판",
|
||||
h2: "라틴 기준으로 만든 도구는 한글에서 멈춘다",
|
||||
h2: "라틴 기본값은 한글에서 세 군데가 깨진다",
|
||||
lead:
|
||||
"영어권 디자인 도구는 한글 조판 규칙을 갖고 있지 않다. 9에서 11px 텍스트는 라틴에서 라벨이지만 한글에서는 읽을 수 없다. line-height 1.5는 라틴에서 표준이지만 한글에서는 답답하다.",
|
||||
body:
|
||||
"가장 자주 깨지는 것은 줄바꿈이다. 기본값은 단어 중간에서 줄을 끊는다. 아래 두 문단은 같은 글자, 같은 폭, 같은 폰트다. 차이는 word-break 한 줄뿐이다.",
|
||||
demoLabels: ["기본값", "keep-all"],
|
||||
demoText: "원두의 산미와 바디를 구분하는 사람에게 필요한 것은 형용사가 아니라 수치다.",
|
||||
"영어권 디자인 도구에는 한글 조판 규칙이 없다. 그래서 기본값을 그대로 쓰면 아래 셋이 동시에 무너진다. 왼쪽이 기본값, 오른쪽이 이 스킬이 정한 값이다. 글자도 폭도 폰트도 같다.",
|
||||
sample: "스페셜티커피의 로스팅프로파일을 구분하려면 산미와 바디를 수치로 봐야 한다.",
|
||||
demos: [
|
||||
{
|
||||
id: "break",
|
||||
title: "줄바꿈",
|
||||
badLabel: "기본값",
|
||||
goodLabel: "word-break: keep-all",
|
||||
note: "기본값은 어절 한가운데를 끊는다. 왼쪽 마지막 줄을 보라 — 한 단어가 두 줄에 걸쳐 있다.",
|
||||
},
|
||||
{
|
||||
id: "leading",
|
||||
title: "행간",
|
||||
badLabel: "line-height 1.45",
|
||||
goodLabel: "line-height 1.75",
|
||||
note: "라틴 표준 1.4~1.5 는 받침이 있는 글자에 좁다. 줄이 서로 붙어 보인다.",
|
||||
},
|
||||
{
|
||||
id: "size",
|
||||
title: "크기",
|
||||
badLabel: "11px",
|
||||
goodLabel: "15px",
|
||||
note: "라틴에서 11px 은 라벨로 쓴다. 한글은 획이 많아 같은 크기에서 뭉갠다.",
|
||||
},
|
||||
],
|
||||
rules: [
|
||||
"word-break: keep-all 없으면 단어가 아무 데서나 잘린다",
|
||||
"line-height 1.6에서 1.8 사이. 라틴보다 넉넉하게",
|
||||
|
|
@ -572,19 +597,39 @@ export const en: Content = {
|
|||
},
|
||||
typography: {
|
||||
eyebrow: "Your language is probably not Latin",
|
||||
h2: "Tools built on Latin assumptions stop at the script boundary",
|
||||
h2: "Latin defaults break Korean in three places",
|
||||
lead:
|
||||
"Design tooling written in English carries no rules for other scripts. 9 to 11px text is a label in Latin and unreadable in Korean. A line-height of 1.5 is standard in Latin and cramped in Korean.",
|
||||
body:
|
||||
"Line breaking fails first. The default breaks Korean mid-word, the equivalent of breaking English mid-syllable in the middle of a sentence. Both paragraphs below use the same text, width and font. The only difference is one line of word-break.",
|
||||
demoLabels: ["default", "keep-all"],
|
||||
demoText: "원두의 산미와 바디를 구분하는 사람에게 필요한 것은 형용사가 아니라 수치다.",
|
||||
"Western design tools carry no Korean typesetting rules. Leave the defaults in place and all three of these fail at once. Left is the default, right is what this skill sets. Same text, same width, same font.",
|
||||
sample: "스페셜티커피의 로스팅프로파일을 구분하려면 산미와 바디를 수치로 봐야 한다.",
|
||||
demos: [
|
||||
{
|
||||
id: "break",
|
||||
title: "Line breaking",
|
||||
badLabel: "default",
|
||||
goodLabel: "word-break: keep-all",
|
||||
note: "The default breaks mid-word. Look at the last line on the left — one word straddles two lines.",
|
||||
},
|
||||
{
|
||||
id: "leading",
|
||||
title: "Leading",
|
||||
badLabel: "line-height 1.45",
|
||||
goodLabel: "line-height 1.75",
|
||||
note: "The Latin standard of 1.4-1.5 is tight for syllable blocks with a final consonant.",
|
||||
},
|
||||
{
|
||||
id: "size",
|
||||
title: "Size",
|
||||
badLabel: "11px",
|
||||
goodLabel: "15px",
|
||||
note: "11px is a label size in Latin. Korean glyphs carry more strokes and smear at it.",
|
||||
},
|
||||
],
|
||||
rules: [
|
||||
"word-break: keep-all, or words break at arbitrary points",
|
||||
"line-height between 1.6 and 1.8, more generous than Latin needs",
|
||||
"no negative letter-spacing. Korean glyphs are near-monospaced and collapse immediately",
|
||||
"25 to 40 characters per line. Latin's 65 is far too long",
|
||||
"not specifying a Korean font is itself a sign of unfinished work",
|
||||
"Without word-break: keep-all, words break at arbitrary points",
|
||||
"line-height between 1.6 and 1.8 — looser than Latin",
|
||||
"Never use negative tracking. Korean is already near-monospaced",
|
||||
"25 to 40 characters per line. The Latin 65 is far too long",
|
||||
"Shipping without a Korean font stack is itself a tell",
|
||||
],
|
||||
},
|
||||
rules: {
|
||||
|
|
|
|||
|
|
@ -160,6 +160,28 @@ Show HN 랜딩 1,590개 실측에서 검출률이 가장 높았던 셋이다. **
|
|||
| Powerful Features | 엑셀 없이 정산이 끝난다 | 명사구 → 주장 |
|
||||
| 빠르고 안정적인 배포 | 배포 실패가 롤백까지 90초 | 형용사 → 측정치 |
|
||||
|
||||
### 데모가 주장을 증명하는지 재라
|
||||
|
||||
주장을 화면으로 보여주는 섹션이라면, **그 화면이 정말 다른지 숫자로 확인해라.**
|
||||
"차이는 이 한 줄뿐이다"라고 써놓고 두 쪽이 똑같으면, 그건 증명이 아니라 주장의 반증이다.
|
||||
|
||||
> 실측 사례: `word-break: break-all` 과 `keep-all` 을 나란히 두고
|
||||
> "차이는 한 줄뿐"이라고 적었다. 재보니 **줄 수 3, 높이 87px 로 완전히 동일**했다.
|
||||
> 어절이 짧아 두 쪽이 같은 자리에서 끊긴 것이다. 반년을 그대로 뒀어도 몰랐을 것이다.
|
||||
|
||||
```js
|
||||
// 비교 데모는 이렇게 검증한다
|
||||
const m = sel => { const e = document.querySelector(sel), r = e.getBoundingClientRect();
|
||||
return { h: Math.round(r.height), lines: Math.round(r.height / parseFloat(getComputedStyle(e).lineHeight)) }; };
|
||||
// m('.bad') 와 m('.good') 이 같으면 그 데모는 아무것도 보여주지 않는다
|
||||
```
|
||||
|
||||
**차이가 안 나면 조건을 극단으로 밀어라** — 폭을 좁히고, 잘리기 쉬운 긴 어절을 넣고,
|
||||
값 차이를 벌린다. 그래도 안 나면 그 비교는 애초에 보여줄 것이 없는 것이다.
|
||||
|
||||
그리고 **설명 문장이 화면과 일치하는지 확인해라.** "A 가 갈린다"고 썼는데 실제로 갈린 건
|
||||
B 라면, 반응형에서 매번 바뀌는 것을 지목한 것이다. 지목하지 말고 현상만 말해라.
|
||||
|
||||
**대구·대조·리듬은 마지막에 얹는 것이다.** 먼저 말할 내용을 정하고,
|
||||
그것이 정해진 뒤에도 대구가 성립하면 그때 써라. 대구부터 잡으면 내용이 비어도 모른다.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue