TECH · 日本語 フォント
欧文の既定値のまま組まない
日本語のWeb組版サンプル集。行間と字間の決め方、webフォントを載せずに済ませるフォントスタック、折り返しと禁則の指定、縦書きと縦中横を、悪い例と並べて比較できる形で収録します。
サンプルとコード
スライダーで値を動かすと、どこで読みにくくなるかが分かります。欧文の既定値・字間を詰めた例・和文の推奨値を同じ文章で並べました。
CSS
/* 和文の本文はこの2つで決まる。
line-height: 1.7〜1.9
CSSの既定値 1.2 は欧文の値。ラテン文字は小文字の高さが低く、
行が詰まっても字面が重ならない。和文は正方形の枠いっぱいに
字面があるため、1.2 では上下の行が接触して読めなくなる。
letter-spacing: 0
和文のフォントは、字と字の間隔をすでに含んだ設計になっている。
ここをマイナスにすると設計を壊すだけで、読みやすくはならない。
見出しでも 0 から下げない。 */
.prose {
line-height: 1.85;
letter-spacing: 0;
font-size: 1rem;
}
/* 1行の長さも読みやすさを決める。日本語は 35〜45 文字くらいが目安で、
em で指定すると文字サイズを変えても比率が保たれる。 */
.prose p { max-width: 40em; margin: 0 0 1em; }
.prose p:last-child { margin-bottom: 0; }
/* 見出しは本文より詰める。文字が大きいぶん、同じ比率だと
行間が開きすぎて塊に見えなくなる。それでも 1.3 は下回らない。 */
.prose h2 {
font-size: 1.35rem;
line-height: 1.5;
letter-spacing: 0;
margin: 0 0 .5em;
}
/* 短い標識(ボタン・タブ・ラベル)だけは、少し開けると締まる。
読ませる文ではないので、ここは例外にしてよい。 */
.tag {
display: inline-block;
font-size: .74rem;
line-height: 1.8;
letter-spacing: .12em;
} AIへの指示文
日本語の本文の行間と字間を比較できるページを作ってください。
要件:
- line-height と letter-spacing をスライダーで変えられるようにし、
同じ文章に「欧文の既定値(1.2)」「字間を詰めた例(-0.05em)」
「和文の推奨値(1.85 / 0)」を並べて比較させる。
- 和文の本文は line-height 1.7〜1.9、letter-spacing 0 にする。
既定の1.2は欧文の値で、ラテン文字は小文字の高さが低いから行が
詰まっても重ならないが、和文は字面が枠いっぱいなので接触する、
という理由をコメントで書く。
- 和文フォントは字と字の間隔を含めて設計されているので
letter-spacing をマイナスにしても読みやすくならない、と書く。
- 見出しは 1.5 程度まで詰めてよいが 1.3 は下回らない、と書く。
- 短い標識(ボタン・タブ・ラベル)だけは字間を開けてよい例外だ、と書く。
- 1行の長さは max-width を em で指定し、日本語は35〜45文字が目安と書く。
- 現在の値が推奨範囲を外れたら、その旨を画面に表示する。
webフォントは1つも読み込んでいません。指定ごとに「実際にどの書体が当たったか」をcanvasで測って表示しているので、自分の端末での結果が分かります。
HTML
<div class="card">
<p class="card-cap">推奨のスタック — <code>var(--font-sans)</code></p>
<p class="spec" data-probe>春はあけぼの。Web Typography 2026。</p>
</div>
<div class="card">
<p class="card-cap">OSのUIフォント — <code>system-ui</code></p>
<p class="spec ui-font" data-probe>春はあけぼの。Web Typography 2026。</p>
</div>
<div class="card">
<p class="card-cap">指定なし(ブラウザ既定) — <code>初期値</code></p>
<p class="spec" style="font-family: initial" data-probe>春はあけぼの。Web Typography 2026。</p>
</div>
<div class="card">
<p class="card-cap">
Yu Gothic を Medium 抜きで指定 — <code>"Yu Gothic", sans-serif</code>(Windowsで細くなる)
</p>
<p class="spec" style='font-family: "Yu Gothic", sans-serif' data-probe>春はあけぼの。Web Typography 2026。</p>
</div> CSS
/* 和文のwebフォントは1書体で 2〜5MB になる。1文字ずつ字形を
持つ必要があるためで、欧文の 20〜100KB とは桁が2つ違う。
サブセット分割で改善はするが、それでも重い。
「本文は OS 標準、必要ならロゴや見出しだけ webフォント」が
現実的な落としどころになる。 */
:root {
/* 欧文と和文を1つのスタックに並べる。ブラウザは1文字ずつ
前から順に探すので、欧文書体を先に書けば英数字はそちらが当たり、
かなと漢字は後ろの和文書体が当たる。1行で混植ができる。 */
--font-sans:
/* 1. 欧文(英数字だけをここで拾わせる) */
"Helvetica Neue", Arial,
/* 2. macOS / iOS */
"Hiragino Kaku Gothic ProN", "Hiragino Sans",
/* 3. Windows。Yu Gothic は素の指定だと細すぎるので
Medium を先に置く。ここを外すと Windows だけ薄く見える。 */
"Yu Gothic Medium", "Yu Gothic", "Meiryo",
/* 4. Android / Linux */
"Noto Sans JP", sans-serif;
/* 等幅は数字とコードのため。和文は等幅にしない
(和文の等幅は全角幅になり、英数字と揃わない)。 */
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
body { font-family: var(--font-sans); }
/* system-ui は「OSのUIフォント」を指す。ブラウザのUIと同じ見え方に
なるので、アプリ的な画面では選択肢になる。ただし何が当たるかを
こちらで決められないため、読み物の本文には向かない。 */
.ui-font { font-family: system-ui, sans-serif; } JavaScript
// 指定したスタックのうち、実際にどの書体が使われたかを調べる。
// getComputedStyle は「指定した文字列」を返すだけなので、描画して測る。
//
// 和文でここが難しくなる理由:
// 欧文なら「幅が変われば別の書体」で判定できる。しかし和文の字は
// どれも同じ全角幅なので、書体が変わっても width は 1px も動かない。
// そこで width だけでなく、実際に墨が乗る範囲(actualBoundingBox)
// まで含めた署名で比べる。書体が違えば字の高さや張り出しが変わる。
const GENERIC = ["monospace", "serif", "sans-serif"];
// 欧文と和文を別々に測る。同じスタックでも当たる書体が違うからで、
// それを見せるのがこのサンプルの目的。
const SAMPLES = { 欧文: "Web Typography 2026", 和文: "春はあけぼの年鬱" };
function signature(font, text, cv) {
cv.font = font;
const m = cv.measureText(text);
return [
m.width,
m.actualBoundingBoxAscent,
m.actualBoundingBoxDescent,
m.actualBoundingBoxLeft,
m.actualBoundingBoxRight,
]
.map((v) => Math.round((v || 0) * 100) / 100)
.join("/");
}
// 「その書体 → 総称ファミリ」の署名が、総称ファミリ単体と違えば、
// その書体が実在して使われている。総称ファミリ3種のどれか1つでも
// 違えば有りとする(1種類だけだと偶然の一致で取りこぼす)。
function fontCovers(name, size, cv, sample) {
return GENERIC.some(
(g) =>
signature(`${size} "${name}", ${g}`, sample, cv) !==
signature(`${size} ${g}`, sample, cv),
);
}
function actualFont(el, sample) {
const cs = getComputedStyle(el);
const size = cs.fontSize;
const cv = document.createElement("canvas").getContext("2d");
const stack = cs.fontFamily
.split(",")
.map((s) => s.trim().replace(/^["']|["']$/g, ""));
for (const name of stack) {
// 総称ファミリ(sans-serif / system-ui など)は書体名ではなく
// 「ブラウザに選ばせる」という指示。ここに到達したら、
// それ以降は実際の書体名を特定できない。
if (GENERIC.includes(name) || name.startsWith("ui-") || name === "system-ui") {
return `${name}(ブラウザ任せ)`;
}
if (fontCovers(name, size, cv, sample)) return name;
}
// この方法の限界。当たっている書体が、総称ファミリの既定と
// まったく同じ書体だった場合、測っても差が出ないので区別できない。
return "判別できず(総称ファミリと同じ字形)";
}
for (const el of document.querySelectorAll("[data-probe]")) {
const p = document.createElement("p");
p.className = "actual";
p.textContent = Object.entries(SAMPLES)
.map(([label, sample]) => `${label}: ${actualFont(el, sample)}`)
.join(" / ");
el.after(p);
} AIへの指示文
和文のフォントスタックを比較できるページを作ってください。
要件:
- 欧文書体を先、和文書体を後ろに並べた1本のスタックを定義する。
ブラウザは1文字ずつ前から探すので、英数字は欧文書体、かなと漢字は
和文書体が当たり、1行で混植ができる、という仕組みをコメントで書く。
- Windows 向けには "Yu Gothic Medium" を "Yu Gothic" より先に置く。
素の Yu Gothic は細すぎてWindowsだけ薄く見える、と理由を書く。
- macOS は Hiragino、Android/Linux は Noto Sans JP を含める。
- system-ui は OS の UI フォントを指し、何が当たるかを制御できないので
読み物の本文には向かない、と書く。
- 推奨スタック / system-ui / 指定なし / Yu Gothic を Medium 抜きで指定、
の4パターンを並べる。
- 各パターンで「実際に使われた書体名」を JavaScript で判定して表示する。
getComputedStyle は指定した文字列を返すだけなので、canvas の
measureText で1書体ずつ描画幅を測って比較する、と方法をコメントで書く。
- 和文webフォントは1ウェイト2〜5MB、欧文は20〜100KBという容量差を
表で示す。判断は「まずOS標準で組み、足りない理由を言えるときだけ
webフォントを検討する」順序だと書く。
幅を変えると折り返し位置の違いが見えます。word-break や text-wrap の指定を変えた3つを並べたので、どれが和欧混植で使えないかがそのまま分かります。
HTML
<div class="card">
<p class="card-cap"><i>推奨</i> — line-break: strict / overflow-wrap: break-word / text-wrap</p>
<div class="prose demo">
<h2>見出しは各行の長さを均す</h2>
<p>
仕様書は
<span class="nowrap">2026年9月1日</span>
に更新しました。詳細は
<span class="nowrap">https://example.com/docs/typography</span>
を参照してください。処理は、およそ
<span class="nowrap">120ms</span>
で完了します。
</p>
</div>
</div>
<div class="card">
<p class="card-cap"><b>word-break: break-all</b> — 和文の行末は揃うが、英単語が途中で切れる</p>
<div class="prose demo bad-break">
<h2>見出しは各行の長さを均す</h2>
<p>
仕様書は 2026年9月1日 に更新しました。詳細は https://example.com/docs/typography を参照してください。処理は、およそ 120ms で完了します。
</p>
</div>
</div>
<div class="card">
<p class="card-cap"><b>指定なし</b> — 小書きのかなが行頭に来ることがある</p>
<div class="demo no-linebreak no-wrap-style">
<h2>見出しは各行の長さを均す</h2>
<p>
しゃっくりがちょうど止まったところで、きゅうに雨が降ってきました。仕様書は 2026年9月1日 に更新しています。
</p>
</div>
</div> CSS
/* 和文は単語の切れ目が無いので、既定でどこでも折り返す。
そのままだと「行頭に句読点が来る」「英単語が途中で切れる」
といった読みにくさが出る。指定すべきは次の4つ。 */
/* 1. 禁則処理の強さ。
normal は小書きのかな(っゃゅょ)や長音を行頭に許すが、
strict にすると許さなくなる。読み物では strict のほうが整う。
句読点や閉じ括弧が行頭に来ないのは、どちらでも既定で守られる。 */
.prose { line-break: strict; }
/* 2. 長い英数字やURLのはみ出し対策。
anywhere ではなく break-word にする。anywhere は
はみ出しの計算方法まで変わり、隣の要素の幅に影響する。 */
.prose { overflow-wrap: break-word; }
/* 3. 英単語を途中で切らない。
word-break: break-all は和文の行末は揃うが、英単語が
無意味な位置で切れる。和欧混植では使わない。 */
.prose { word-break: normal; }
/* 4. 行の分け方。
見出しは balance で各行の長さを均す(最大4行程度まで)。
本文は pretty で、最終行が1文字だけ残る状態を避ける。 */
.prose h2 { text-wrap: balance; }
.prose p { text-wrap: pretty; }
/* 切りたくない語のまとまりは、その範囲だけ nowrap にする。
製品名・数値と単位・日付などが対象。 */
.nowrap { white-space: nowrap; } AIへの指示文
日本語の折り返しと禁則を比較できるページを作ってください。
要件:
- 表示幅をスライダーで変えられるようにする。
- line-break: strict を指定する。句読点や閉じ括弧が行頭に来ないことは
指定しなくても守られ、strict が変えるのは小書きのかな(っゃゅょ)と
長音を行頭に許すかどうかだ、と正確にコメントで書く。
- overflow-wrap: break-word を指定する。anywhere は
はみ出しの計算方法まで変わって隣の要素の幅に影響する、と書く。
- word-break: break-all は使わない。和文の行末は揃うが英単語が
無意味な位置で切れる、という理由を書き、悪い例として並べて見せる。
- 見出しに text-wrap: balance、本文に text-wrap: pretty を当てる。
- 切りたくない語(製品名・日付・数値と単位・URL)は
span で囲んで white-space: nowrap にする。
- 日本語の本文なので line-height は 1.85、letter-spacing は 0 にする。
writing-mode の1行で始まりますが、その先で height の意味も line-height の向きもスクロール方向も入れ替わります。ルビと圏点、縦中横まで入れました。
HTML
<p class="cap">縦書き(writing-mode: vertical-rl)</p>
<div class="tategaki">
<p>
<ruby>春<rt>はる</rt></ruby>はあけぼの。やうやう白くなりゆく山ぎは、すこしあかりて、むらさきだちたる雲のほそくたなびきたる。
</p>
<p>
この文章は<span class="kenten">圏点</span>で強調しています。日本語の強調は、太字や下線ではなく点を打つのが古くからの方法です。
</p>
<p>
この記事は<span class="tcy">26</span>日に公開しました。行間は横書きより<span class="tcy">20</span>%ほど広く取ってあります。2桁の数字は縦中横で1マスに収めますが、4桁の年号のような長い数字には使いません。潰れて読めなくなるからです。
</p>
</div>
<p class="cap">同じ指定を横書きで(比較用)</p>
<div class="yoko">
<p>
<ruby>春<rt>はる</rt></ruby>はあけぼの。この文章は<span class="kenten">圏点</span>で強調しています。ルビも圏点も、書き方は縦横で変わりません。
</p>
</div> CSS
.tategaki {
/* 縦書きの本体。右の行から左へ流れる。 */
writing-mode: vertical-rl;
/* 英数字の向き。既定(mixed)は英数字が横倒しになる。
upright にすると1文字ずつ立つが、単語が読みにくくなるので
本文では mixed のままにして、必要な箇所だけ縦中横にする。 */
text-orientation: mixed;
/* 縦書きでは line-height が「行と行の左右の間隔」になる。
横書きより広めに取ると読みやすい。 */
line-height: 2;
/* 高さを決めないと1行に全部入ってしまう。縦書きでは
height が「1行の長さ」にあたる。 */
height: 17em;
padding: 1.2rem 1.4rem;
border: 1px solid var(--line);
border-radius: 12px;
background: #fff;
font-family: var(--serif);
font-size: 1rem;
/* はみ出したら横方向にスクロールする。縦書きでは
行が増える方向が「左」なので、これは横スクロールになる。 */
overflow-x: auto;
overscroll-behavior-x: contain;
}
.tategaki p { margin: 0 0 1em; }
.tategaki p:last-child { margin-bottom: 0; }
/* 縦中横。2桁までの数字を1マスに収めて横向きに組む。
「26日」「20%」のような箇所に使う。3桁以上や4桁の年号に
当てると潰れて読めなくなるので、対象は2桁までにとどめる。 */
.tcy { text-combine-upright: all; }
/* 圏点(傍点)。日本語の強調は、太字や下線ではなく点を打つのが
伝統的な方法で、縦書きとの相性がよい。 */
.kenten {
text-emphasis: filled dot;
text-emphasis-position: over right;
}
/* ルビは ruby / rt。縦書きでも横書きでも同じ書き方で通る。 */
ruby { ruby-position: over; }
rt { font-size: .5em; line-height: 1; } AIへの指示文
縦書きの本文を作ってください。
要件:
- writing-mode: vertical-rl で縦書きにする。
- text-orientation は mixed のままにする。upright は1文字ずつ立つが
単語が読みにくくなるので、必要な箇所だけ縦中横にする、と書く。
- 縦書きでは height が「1行の長さ」、line-height が「行同士の左右の
間隔」になる。横書きの感覚のまま値を決めると必ずずれる、と
コメントで書く。height を指定しないと1行に全部入ってしまう点も書く。
- 2桁の数字に text-combine-upright: all(縦中横)を当てる。
3桁以上に当てると潰れて読めないので2桁までにとどめる、と書く。
- 強調は太字や下線ではなく圏点(text-emphasis: filled dot)を使う。
日本語の伝統的な強調方法だと書く。
- ルビは ruby と rt で組む。書き方は縦横で変わらない。
- はみ出しは overflow-x: auto で受ける。縦書きでは行が増える方向が
左なので横スクロールになる、と書く。
- 比較用に、同じ指定を横書きにしたものを下に並べる。
この技術について
CSSの既定値は欧文を前提にしています。行間 1.2、字間 0、単語の切れ目での折り返し。
この設定のまま日本語を組むと、行が接触して読みにくく、小書きのかなが行頭に来て、英数字が混じった箇所で不自然に改行されます。どれも「そういうもの」ではなく、値を決めていないだけです。
line-height 1.2 が欧文の値である理由
ラテン文字は、小文字の高さ(x-height)が全体の半分ほどしかありません。行が詰まっても、上の行の下端と下の行の上端の間には余白が残ります。
日本語は違います。ひらがなも漢字も、正方形の枠いっぱいに字面があります。1.2 では上下の行が物理的に接触します。
本文は 1.7〜1.9。見出しは文字が大きいぶん詰めてよく、1.3〜1.5。それより下げると、行がひとかたまりに潰れます。
このサイトでは、この範囲をビルド時に機械検査しています。プレビュー用のHTMLを実際に描画し、和文を含む要素の実効 line-height が範囲外ならビルドを失敗させます。目視のレビューでは必ず漏れるからです。
字間は詰めない
欧文には「少し詰めて締まって見せる」手法があります。和文には移植できません。
和文のフォントは、字と字の間隔をすでに含めて設計されています。そこから削ると、設計を壊すだけで読みやすくはなりません。letter-spacing は 0 にしてください。
例外は、ボタン・タブ・ラベルのような短い標識です。読ませる文ではないので、0.1〜0.5em ほど開けると締まって見えます。
webフォントは容量の桁が違う
欧文のwebフォントは1ウェイト20〜100KB。和文は2〜5MBです。漢字だけで数千字ぶんの字形を持つ必要があるからで、サブセット分割で改善しても欧文より重いままです。
判断の順序が大事です。まずOS標準フォントで組み、それで足りない理由を言えるときだけwebフォントを検討する。逆順にすると、容量を払ってから理由を探すことになります。
スタックは、欧文書体を先、和文書体を後ろに並べます。ブラウザは1文字ずつ前から探すので、英数字は欧文書体、かなと漢字は和文書体が当たり、1行で混植ができます。
font-family:
"Helvetica Neue", Arial,
"Hiragino Kaku Gothic ProN", "Hiragino Sans",
"Yu Gothic Medium", "Yu Gothic", "Meiryo",
"Noto Sans JP", sans-serif;
"Yu Gothic Medium" を落とさないでください。素の "Yu Gothic" は Regular が当たり、Windows でだけ細く薄く見えます。「Windowsだと文字が細い」の原因はほぼこれです。
折り返しは4つ指定する
和文には単語の切れ目がないので、既定ではどこでも折り返します。
line-break: strict— 小書きのかな(っゃゅょ)と長音を行頭に許さなくなります。句読点や閉じ括弧が行頭に来ないことは、指定しなくても守られますoverflow-wrap: break-word— 長いURLのはみ出し対策。anywhereは幅の計算方法まで変わるので使いませんword-break: normal—break-allは和文の行末が揃う代わりに、英単語が無意味な位置で切れます。和欧混植では選べませんtext-wrap: balance/pretty— 見出しは各行の長さを均し、本文は最終行に1文字だけ残るのを避けます
切りたくないまとまり(製品名・日付・数値と単位)は、その範囲だけ white-space: nowrap で囲みます。
縦書きは、値を決め直すところから
writing-mode: vertical-rl の1行で縦書きになります。難しいのはその先です。
height が「1行の長さ」になります。指定しないと1行に全部入ってしまう。line-height は行同士の左右の間隔になり、横書きより広めに取ると読みやすい。はみ出しは横スクロールです。行が増える方向が左だからです。
英数字は text-orientation: mixed のままにして、必要な箇所だけ text-combine-upright: all(縦中横)で1マスに収めます。対象は2桁までにしてください。3桁以上を1マスに押し込むと潰れて読めません。
強調は太字や下線ではなく、圏点(text-emphasis: filled dot)が日本語の作法です。ルビ(ruby / rt)とあわせて、縦横どちらでも同じ書き方で通ります。
使いどころとつまずきどころ
向いている場面
- 日本語の本文を長く読ませる記事やブログ
- webフォントを載せずに見た目を整えたい案件
- 見出しの折り返し位置を制御したい画面
- 縦書きを使う和風・出版系のデザイン
つまずきやすい点
- 既定の line-height 1.2 は欧文の値。和文には狭すぎる
- 和文で letter-spacing をマイナスにすると読みにくくなる
- 和文webフォントは1書体で数MB。安易に載せない
- 英数字が混じると行の途中で不自然に折り返される
よくある質問
和文の line-height はいくつが適切ですか。
本文は 1.7〜1.9 の範囲にしてください。CSSの既定値 1.2 は欧文の値です。ラテン文字は小文字の高さが低いため行が詰まっても字面が重なりませんが、日本語は正方形の枠いっぱいに字面があるので、1.2 では上下の行が接触して読めなくなります。見出しは文字が大きいぶん詰めてよく、1.3〜1.5 が目安です。
和文で letter-spacing を詰めてはいけませんか。
読ませる文では詰めないでください。和文のフォントは字と字の間隔をすでに含んだ設計になっているので、削っても締まって見えるだけで読みにくくなります。欧文で使われる「少し詰めて締める」手法は和文には移植できません。例外は、ボタンやタブのような短い標識で、ここは 0.1〜0.5em ほど開けると締まって見えます。
和文のwebフォントは使わないほうがよいですか。
使ってはいけないわけではありませんが、容量が桁違いだという前提で判断してください。欧文が1ウェイト20〜100KBなのに対し、和文は2〜5MBです。漢字だけで数千字ぶんの字形を持つためで、サブセット分割しても欧文より重いままです。現実的な落としどころは「本文はOS標準フォント、ロゴや見出しだけwebフォント」です。
Windowsだけ文字が細く見えます。
フォントスタックに "Yu Gothic Medium" が入っていない可能性が高いです。素の "Yu Gothic" を指定すると Regular が当たり、Windows の描画では細く薄く見えます。"Yu Gothic Medium" を "Yu Gothic" より先に書いてください。1行足すだけで解決します。
word-break と overflow-wrap はどちらを使いますか。
和欧混植では overflow-wrap: break-word です。word-break: break-all は和文の行末がきれいに揃う一方、英単語やURLが意味のない位置で切れます。overflow-wrap: break-word なら、はみ出す長い文字列だけを折り返し、通常の英単語は保たれます。anywhere は避けてください。はみ出しの幅計算まで変わり、隣の要素の幅に影響します。
line-break: strict は何を変えるのですか。
小書きのかな(っ・ゃ・ゅ・ょ)と長音記号を行頭に許すかどうかです。句読点や閉じ括弧が行頭に来ないことは、指定しなくてもブラウザが守ります。読み物では strict にすると行頭が整いますが、1行が極端に短い場合は行末の空きが目立つので、幅とあわせて判断してください。
縦書きは実務で使えますか。
主要ブラウザで実装は揃っているので、技術的には使えます。ただし横書きの感覚がほぼ通用しません。height が1行の長さになり、line-height が行同士の左右の間隔になり、はみ出しは横スクロールになります。既存の横書き用CSSを流用せず、縦書きに切り替えてから値を決め直してください。