# このブログの記事はデスクトップで本文が狭く小さすぎた — dev.to に合わせて直したら、CJKの行長バグも見つかった

> Astro の記事ページは 68ch/17px で狭く小さかった。dev.to の実測値に合わせて拡大したら、ch 単位が隠していた CJK の行長バグも直った。

- Source: https://oharu121.com/ja/blog/astro-measure-font-size-devto-cjk-ch-unit/
- Published: 2026-08-31T01:52:46+09:00
- Tags: Astro, CSS, 国際化

---
**要点**

- このブログの `68ch` の本文カラムはすでに Zenn と近く、英語として標準的な **45〜75文字** の範囲に収まっていました。本当のバグは別のところにありました。
- `ch` はラテン文字の数字の幅を測る単位で、全角のCJK文字のおよそ半分の幅しかありません。そのため同じ `68ch` でも、日本語と繁体字中国語の記事は**1行あたり34文字程度**しか入っておらず、CJKを意識せず設定されたトークンのせいで文字数が名目の半分になっていました。
- dev.to を実際に(スクリーンショットではなく Playwright で)計測し、新しい共通の基準値を決めました。**`47rem`(752px)と `19px` の本文サイズ**です。
- この基準値なら日本語や繁体字中国語に専用のフォントサイズは不要でした。**1行およそ37.5文字**に収まり、広く知られた実例のベンチマークとも、WCAG 1.4.8 の CJK 向け上限とも一致します。
- 計算では気づけなかった不具合をコードレビューが見つけました。本文が大きくなった結果、見出しの固定サイズを超えてしまい、見出しが本文より小さく表示されていたのです。

## はじめに

自分の記事をZennの記事と同じウィンドウ幅で並べて読んでいたとき、違いは一目瞭然でした。相手の段落は画面をより広く使っているのに、自分の記事は左右に余白を残したまま狭いカラムに収まっていたのです。私はエージェントに、このブログの本文カラムは本当に狭いのか、それとも見慣れていないだけなのか、率直な意見を尋ねました。

エージェントの最初の答えは「カラムは問題ない」というものでした。`68ch` はラテン文字の記事に typography のガイドがよく挙げる45〜75文字の範囲に収まっており、Zenn自身のカラムとも近い値だったからです。その答えは英語に関しては正しく、このブログが公開しているもう二つの言語には静かに間違っていました。カラムの幅を決めている単位が、日本語や繁体字中国語が使う文字体系とは何の関係もないものだったからです。この不具合を直したことで、もう一つ別の不具合も表面化しました。修正そのものが、見出しを本文より小さく表示させてしまっていたのです。

この記事では、英語のチェックがなぜ通ったのか、同じ measure が CJK では英語のおよそ半分の文字数しか許していなかった理由、dev.to の実測値をもとに新しい幅とフォントサイズをどう決めたか、そして公開前にコードレビューが見つけた見出しの階層バグについて順に説明します。

## 本文カラムはすでに Zenn と同じくらいだった、英語については

このブログは英語・日本語・繁体字中国語の3言語で、1つのMDXソースから記事を公開しています。本文カラムの幅は `--measure: 68ch` という1つのCSSカスタムプロパティで決まっていました。

`ch` は実在するCSS単位で、いま使われているフォントで数字の「0」がどれだけの幅になるかを表します。ウェブタイポグラフィの慣習では `N`ch はおおよそ `N` 文字分と見なされ、だからこそ `max-width: 65ch` が「読みやすい行長」のレシピとしてあちこちに登場します。`68ch` の時点で、このブログのカラムはすでに **45〜75文字の標準的な範囲** に収まっており、Zenn自身のカラムと見比べても違いが分からないほど近い値でした。この事実だけを根拠に幅を広げていたら、それはスクリーンショットを追いかけただけの見た目の変更であり、修正ではなかったはずです。

## 同じ measure でも、日本語と中国語は行の文字数がほぼ半分だった

`68ch` を3言語すべてに一律で適用するのは一見無害に見えましたが、実際に確かめる価値があったのはまさにそこでした。`ch` はラテン文字の数字グリフを基準にした単位であり、全角のCJK文字はフォントの設計上、1em、つまりその2倍の幅を占めるよう定義されています。実際のサイトを(スクリーンショットではなく、稼働中の開発サーバーに対して)Playwrightで計測すると、それがそのまま裏付けられました。`--measure: 68ch` は、このブログの `17px` の本文サイズにおいて `583px` のカラムに解決されており、しかも3言語すべてでまったく同じ値でした。当時はどの言語もまだ独自のフォントサイズを持っていなかったからです。

CJKの読者が見ていたのは同じ `583px` の箱でしたが、そこに入る一文字はおよそ `17px` の幅があり、同じサイズでラテン文字の「0」が測る `8.5px` ほどとは大きく違います。これはほぼちょうど半emで、ブラウザがグリフの幅を直接測れないときにCSS仕様自身が定めるフォールバック値と偶然一致します。実際にCJKに当てはまる文字幅で箱を割ると、1行に入るのは68文字ではなく **34文字程度** になります。実際の日本語のウェブタイポグラフィでは、快適な範囲はおよそ **37〜47文字** とされ、37文字(Yahoo!ニュース)が具体的な目安としてよく引用されます。また[WCAG 1.4.8](https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html)は、CJKの本文を1行40文字以内、ラテン文字は80文字以内と定めています。このブログの日本語と繁体字中国語の記事は、CJKを意識して設定されたことのないトークンの上で、その範囲全体を下回ったまま表示されていたのです。

*Figure — LatinVsCjkGlyph: ラテン文字の「0」の幅はおよそ半emです。全角のCJK文字はその2倍、1emを占めます。同じ ch 基準のカラムでも、片方の文字体系はもう片方のおよそ2倍の文字数を1行に収められることになります。*

## dev.to 自身の数値を、実測して使う

CJKだけを直すのであれば、日本語と中国語にだけ専用のフォントサイズを与え、英語はそのままにするという方法もありました。私はそれよりも修正の範囲を広げることを選びました。dev.toの記事はこのブログよりも明らかに大きく、幅も広く読めたので、新しい基準値は勘ではなく、その比較から実際に選びたいと考えたのです。フォントサイズそのものを、1つの共通のカラム幅の中で各言語の行の長さを適正な範囲に収めるためのレバーとして使うことにしました。

このセッションの作業中、Chrome DevTools MCPは使えない状態でした。そこでエージェントは、このリポジトリにすでに依存関係として入っている `playwright` ライブラリを使い、使い捨てのスクリプトを書きました。`scripts/check-figure-fit.ts` がスクリーンショットではなく実際にレンダリングされたテキストを測るのに使っているのと同じライブラリです。このスクリプトはdev.toの実際の記事を読み込み、`document.fonts.ready` を待ってから、実際の段落要素のコンピューテッドスタイルを読み取りました。**本文は `20px`、カラムは `724px`** でした。同じスクリプトで、このブログ自身の開発サーバーを旧設定のまま測ると `17px` で `583px` でした。

```ts
const result = await page.evaluate(() => {
  const p = document.querySelector(".crayons-article__body p");
  const cs = getComputedStyle(p);
  return { fontSize: cs.fontSize, width: p.getBoundingClientRect().width };
});
```

この2つの実測値をもとに(スクリーンショットではなく)、新しい共通の基準値は `--font-size-5: 1.1875rem`(`19px`)になり、`--measure` は `ch` を完全にやめて、dev.to自身のカラムに近い固定の `rem` 値に変わりました。

## 最初のバージョンは、パディング分2remを見落としていてCJKを少なく見積もっていた

最初に試した値は `--measure: 45rem`(`720px`)でした。実際に計測すると **1行35.8文字** となり、手計算で見込んでいたおよそ38文字より少ない結果でした。その手計算は `--measure` の生の値をそのままカラム幅として扱っていましたが、実際の段落は `.page` 自身の `1.25rem` のパディングの内側にあります。実際のコンテンツ幅は `720px` から `40px` を引いた値であり、`720px` そのものではありません。実際のDOMを測り直したことで、この見落としはすぐに見つかりました。

このパディング分を補正して計算し直した結果、`--measure: 47rem`(`752px`)に落ち着きました。コンテンツ幅は `712px` となり、`19px` のフォントサイズでは日本語と繁体字中国語の記事が **1行37.5文字** に収まります。これはYahoo!ニュースのベンチマークにほぼ一致し、WCAGの40文字上限にも余裕を持って収まっています。全角のCJKグリフはフォント設計上ちょうど1emなので、この割り算はほぼ正確です。一つ前のセクションのラテン文字の場合と違い、`ch` は文字幅を直接測るのではなく、あくまで近似にすぎません。日本語と繁体字中国語には、結局、専用のフォントサイズはまったく必要ありませんでした。dev.toとの整合のために選んだ共通の値が、そのままCJKの目標値の中に収まっていたのです。

| トークン | 変更前 | 変更後 |
| --- | --- | --- |
| `--measure` | `68ch` | `47rem`(`752px`) |
| `--font-size-5`(本文) | `1.0625rem`(`17px`) | `1.1875rem`(`19px`) |
| カラム幅(全言語共通) | `583px` | `752px` |
| CJKの1行の文字数 | 約34文字 | 約37.5文字 |

## コードレビューが、計算では気づけなかった問題を見つけた

トークンの変更自体は問題なく通りました。`pnpm check`、`pnpm figures:fit`、そして実測による1行の文字数チェック、すべて通過しました。しかしこれらのどれも、ページの他の部分が新しい大きな本文サイズのもとで見た目として成立しているかまではチェックしていませんでした。

この変更に対して実行されたコードレビュー(このブログ自身の `/release` パイプラインが、すべてのプルリクエストに対して実行しているものです)がそれを見つけました。`.prose h3` は `1.15rem`(`18.4px`)という固定値のままで、トークンの変更の影響を受けていなかったのです。そして新しい本文サイズの `19px` は、その値を追い越してしまっていました。**見出しレベル3が、その下にある段落の本文より小さく表示されるようになっていた**のです。見出しの階層としては正反対の状態です。`h2` は固定の `1.4rem`(`22.4px`)で影響を受けておらず、3つの中で最も大きいままでした。

*Figure — TypeScaleRegression: h3 は 1.15rem に固定されたまま本文テキストのトークンと連動しておらず、本文を大きくした結果、h3 を追い越して階層が逆転しました。その後 h3 も大きくして修正しました。*

修正はたった1行でした。`.prose h3` を `1.3125rem`(`21px`)に変更し、新しい本文サイズより上、`h2` より下という位置に戻したのです。型チェックもビルドも、1行の文字数の実測も、単独ではこの問題を見つけられませんでした。どれも、独立して設定された2つのフォントサイズを互いに比べてはいないからです。**その比較こそ、レビューという工程が存在する理由です。**

*Image: このブログの本文カラムが広がり、本文サイズも大きくなった様子。この修正を含む記事自身で実際にレンダリングしたもの*

*両方の変更を反映したあとのカラムと本文サイズ。目視ではなく実測で確認しています。*

## まとめ

本文カラムはすでにZennと同じくらいで、英語については問題ありませんでした。本当のバグは、ラテン文字の数字を基準にした単位で表現された同じmeasureが、日本語と繁体字中国語には英語のおよそ半分の文字数しか与えていなかったことでした。誰かが意図して決めたわけでも、誰かがチェックしたわけでもない差です。dev.toに合わせて共通の基準値を広げ、スクリーンショットの見比べではなく実測を使ったことで、両方が同時に直りました。新しい `47rem` / `19px` の組み合わせは、専用のオーバーライドなしで日本語と繁体字中国語を1行およそ37.5文字に収め、WCAG 1.4.8の上限にも収まり、実例のベンチマークにも一致します。**その計算がまったく気づけなかったのが、見出しが本文より小さく表示されるという問題であり**、それを見つけるのがレビューという工程の役目です。

## 参考リンク

- [WCAG 2.1: Understanding Success Criterion 1.4.8, Visual Presentation](https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html)
- [What is the CSS ch Unit?](https://meyerweb.com/eric/thoughts/2018/06/28/what-is-the-css-ch-unit/)
- [WEBメディアの記事の読みやすい1行の文字数はどれ位か調査してみた](https://web-kiwami.com/the-number-of-characters-in-one-line.html)
