等幅フォント(モノスペースフォント)を指定しているのに、AI(Claude など)に書かせた枠がなぜかズレる。罫線素片(┌─┐│└┘)で描いた枠の右端がガタガタになる。日本語を混ぜるとさらにひどくなる。

自作ドキュメント(VitePress)に、罫線素片で描いた図(ASCIIアート、以下 AA)を載せていて、この問題にぶつかりました。「等幅フォント 罫線がズレる原因」「等幅フォント 罫線が揃わない原因」等で調べて、フォントサイズが奇数だとズレる、フォールバックで別のフォントが使われる、という話は見つかります。しかし、罫線素片が1マスか2マスか、1マスが 0.5em か 0.6em か という見方で整理した記事は、見つかりませんでした。

そこで、手元のフォントを実測して、原因と解決策をまとめました。


罫線がズレる原因

  • 原因1:罫線の幅がフォントで違う。 罫線は、1マスで描くフォントと、2マスで描くフォントがあります。
  • 原因2:1マスの幅がフォントで違う。 0.5em のフォントと、0.6em 前後のフォントがあります。日本語の全角(1em)と2:1にならないと、日本語を混ぜた行がズレます。
  • 原因3:フォントに罫線の字形がない。 別のフォントで表示されて、幅が変わります。
  • 解決策: 「罫線 = 1マス」「全角 = 英字の2倍」を満たすフォントを選ぶ。満たさなければ、罫線だけを別のフォントから借りる。

まず「1マス」を決める

AA を揃えるには、1文字分の幅(1マス)を決めます。ここでは、英字の M の幅を1マスとします。揃うための条件は、次の3つです。

文字 必要な幅
英字・数字(M など) 1マス
罫線(─ │ ┌ など) 1マス
日本語(全角) 2マス

この3つがそろうと、次のように、どの行も同じ長さに見えます。

12345678
あいうえ
────────

実測:フォントごとの罫線と全角の幅

フォントファイルから、英字 M、罫線、全角の「あ」の幅を測りました。数値は、M の幅を1マスとしたときの比率です。

フォント 1マス(M) 罫線の字形 罫線の幅 全角(あ)
Inconsolata 0.500em 128/128 1マス なし
BIZ UDGothic 0.500em 32/128 2マス 2マス
UDEV Gothic 0.500em 128/128 1マス 2マス
M PLUS 1 Code 0.500em 128/128 1マス 2マス
Google Sans Code 0.600em 128/128 1マス なし
HackGen 0.527em 128/128 2マス 2マス

※ 罫線の字形は、Unicode の Box Drawing(U+2500–257F)128字のうち、フォントに入っている数です。

手元のフォントを測りたい場合は、フォントメトリクス解析ツール で計測することができます。

フォントメトリクス解析ツール - 2つのフォントを比較して、size-adjustの最適値を計算 フォントメトリクス解析ツール - 2つのフォントを比較して、size-adjustの最適値を計算 2つのフォントを比較して、CSSの size-adjust プロパティの最適値を計算するオンラインツール。フォントメトリクス値・--font-top-offset の値も取得。  続きを読む

原因1:罫線が1マスのフォントと2マスのフォントがある

罫線は、Unicode の East Asian Width(東アジアの文字幅、UAX #11)で、Ambiguous(曖昧) に分類されています。欧文の文脈では半角、東アジアの文脈では全角として扱われる文字です。

そのため、フォントによって、罫線の幅が変わります。

  • 罫線が1マス: Inconsolata、UDEV Gothic、M PLUS 1 Code など
  • 罫線が2マス: BIZ UDGothic、HackGen など(日本語フォントの流れで、全角で描いている)

罫線が2マスのフォントでは、英語だけの AA でも崩れます。英字は1マス、罫線は2マスなので、枠の横線が伸びてしまうためです。

font-family: 'BIZ UDGothic', monospace;   /* ❌ 罫線が2マス。英語だけでもズレる */
font-family: 'UDEV Gothic', monospace;    /* ✅ 罫線が1マス、全角が2マス */

'BIZ UDGothic' だけで表示した AA(罫線が2マスなのでズレる)

1234567890123456789012345678901234567890
あいうえおかきくけこさしすせそたちつてと
┌──────────────────────────────────────┐
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn
┌───────────────────────────────────────┐
│            日本語を含む箱             │
│            ABC あいう 123             │
└───────────────────────────────────────┘

AI が書いた罫線 AA がズレる
AI(Claude など)に罫線 AA を書かせると、ズレることがあります。原因は、AI が表示幅ではなく文字数で桁を数えていることです。全角を1桁として数えたり、長い枠で数え間違えたりすることで行ごとの桁数がバラバラになります。


原因2:1マスの幅が 0.5em のフォントと 0.6em のフォントがある

日本語の全角文字は、ほとんどのフォントで 1em の幅です。英字の幅(1マス)が 0.5em なら、全角はちょうど2マスになります。

ところが、英字を 0.6em で作っているフォントがあります(表の Google Sans Code 等)。この場合、全角 1em は 1em ÷ 0.6em = 1.67マスにしかならず、日本語を混ぜた行がズレます。

英字の幅 全角(1em)のマス数 日本語を混ぜた行
0.5em 2.00マス 揃う
0.6em 1.67マス ズレる

英字 0.6em のフォントと、全角 1em の日本語フォントを組み合わせると、2マス分(1.2em)に対して、日本語1文字ごとに 0.2em(約0.33マス)足りなくなります。日本語が多い行ほど、右端のズレが大きくなります。

'Google Sans Code', 'BIZ UDGothic' で表示した AA(1マスの幅が違うのでズレる)

1234567890123456789012345678901234567890
あいうえおかきくけこさしすせそたちつてと
┌──────────────────────────────────────┐
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn
┌───────────────────────────────────────┐
│            日本語を含む箱             │
│            ABC あいう 123             │
└───────────────────────────────────────┘

「35」系のフォント
なお、英字と全角の幅の比を 3:5 にした「35」系のフォント(HackGen35 など)もあります。これは日本語1文字が 1.67 マスなので、2:1 を前提にした AA には向きません。


原因3:フォントに罫線の字形がなく、別のフォントで表示される

font-family は、1文字ごとに、リストの先頭から「その文字の字形を持つ最初のフォント」を選びます。罫線の字形がないフォントを指定すると、その文字だけ、別のフォントで表示されます。

表の BIZ UDGothic と HackGen は、罫線 128字のうち 32字しか持っていません。足りない罫線は、別のフォントで表示されて、幅が不定になります。

この「文字ごとにフォントが切り替わる」仕組みは、次の解決策でも使います。


解決策1:罫線が1マス、全角が2マスのフォントを使う

最も簡単なのは、条件を最初から満たすフォントを選ぶことです。

font-family: 'UDEV Gothic', monospace;
font-family: 'M PLUS 1 Code', monospace;

筆者の環境では、どちらも英語・日本語・罫線が揃いました。M PLUS 1 Code と UDEV Gothic は、実測でも「英字 0.5em、罫線 1マス、全角 2マス」でした。

Web サイトで使う場合は、Web フォントとして配信します。

  • M PLUS 1 Code:Google Fonts から読み込めます。Google Fonts の配信分にも、罫線128字がすべて含まれていました。
  • UDEV Gothic:Google Fonts にはないので、自分で配信して、CDN から読み込みます。
<!-- M PLUS 1 Code(Google Fonts) -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=M+PLUS+1+Code&display=swap">

<!-- UDEV Gothic(自前の npm パッケージ) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@sarap422/font-udevgothic@1.0.0/font-face.css">

'M PLUS 1 Code' だけで表示した AA

1234567890123456789012345678901234567890
あいうえおかきくけこさしすせそたちつてと
┌──────────────────────────────────────┐
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn
┌───────────────────────────────────────┐
│            日本語を含む箱             │
│            ABC あいう 123             │
└───────────────────────────────────────┘

'UDEV Gothic'だけで表示した AA

1234567890123456789012345678901234567890
あいうえおかきくけこさしすせそたちつてと
┌──────────────────────────────────────┐
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn
┌───────────────────────────────────────┐
│            日本語を含む箱             │
│            ABC あいう 123             │
└───────────────────────────────────────┘

解決策2:罫線を持つ 0.5em のフォントを先頭に置く

罫線が2マスの日本語フォント(BIZ UDGothic)を使いたい場合は、罫線が1マスで、英字が 0.5em のフォントを先頭に置きます。

font-family: 'Inconsolata', 'BIZ UDGothic', monospace;

罫線は、先頭の Inconsolata から出て1マスになります。日本語は、Inconsolata にないので、BIZ UDGothic から出て2マスになります。すべてが 1:2 になって、揃います。

順番を逆にすると、先に BIZ UDGothic の罫線(2マス)が使われて、ズレます。

Web サイトで使うときの注意

Google Fonts から配信される Inconsolata は、unicode-range が latin / latin-ext / vietnamese の3つだけで、罫線素片(U+2500–257F)が含まれていません。Web フォントとして 'Inconsolata', 'BIZ UDGothic' を指定すると、罫線は Inconsolata からではなく、BIZ UDGothic から出て、2マスになります。結果として、ズレます。

Web サイトで揃えるには、罫線を持つフォントを、自分で配信します。Inconsolata の罫線だけを取り出した、Inconsolata Rule を先頭に置きます。

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@sarap422/font-inconsolata-rule@<version>/font-face.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inconsolata:wght@400;700&display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=BIZ+UDGothic:wght@400;700&display=swap">
font-family: 'Inconsolata Rule', 'Inconsolata', 'BIZ UDGothic', monospace;

'Inconsolata Rule', 'Inconsolata', 'BIZ UDGothic' で表示した AA(Web でも揃う)

1234567890123456789012345678901234567890
あいうえおかきくけこさしすせそたちつてと
┌──────────────────────────────────────┐
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn
┌───────────────────────────────────────┐
│            日本語を含む箱             │
│            ABC あいう 123             │
└───────────────────────────────────────┘

解決策3:罫線だけのフォントを作って揃える

HackGen は、英字が 0.527em、全角が 1.054em と、全角がちょうど英字の2倍です。しかし罫線が2マスなので、そのままでは AA が崩れます。

では、'Inconsolata', 'HackGen' ではどうでしょうか。これも ❌ です。Inconsolata の1マスは 0.5em なので、日本語2マス分が 1.0em になります。ところが HackGen の全角は 1.054em なので、1文字ごとに約5.5%足りず、日本語を含む行だけがズレます。

font-family: 'HackGen', monospace;              /* ❌ 罫線が2マス */
font-family: 'Inconsolata Rule', 'Inconsolata', 'HackGen', monospace;   /* ❌ 日本語の幅が合わない */

'Inconsolata Rule', 'Inconsolata', 'HackGen' で表示した AA(日本語を含む行だけズレる)

1234567890123456789012345678901234567890
あいうえおかきくけこさしすせそたちつてと
┌──────────────────────────────────────┐
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn
┌───────────────────────────────────────┐
│            日本語を含む箱             │
│            ABC あいう 123             │
└───────────────────────────────────────┘

罫線だけのフォントを作る

そこで、HackGen の1マス(0.527em)に合わせた、罫線だけのフォントを作ります。

作り方

  1. Inconsolata から、罫線(U+2500–257F)の128字だけを取り出す
  2. 倍率を、HackGen の1マスと Inconsolata の1マスの比で決める:0.52734375 ÷ 0.5 = 1.0546875(105.46875%)
  3. UPM を 1000 → 1024 にして、座標を ×1.08 にする(1.024 × 1.0546875)。これで、字形の幅が 500 → 540 になり、HackGen の半角(540)と同じになる
  4. 縦方向の寸法(ascent / descent)も、HackGen に合わせる

Inconsolata は SIL Open Font License 1.1 のフォントです。改変して配布する場合は、ライセンス表記を同梱します。

使い方

作ったフォントは、Inconsolata Rule x1.0546875 という名前で公開しています(Regular / Bold、woff2 で約2.5KB)。名前の x1.0546875 は、元の Inconsolata に対する倍率です。倍率を変えた版は、_x倍率(ファイル名)、 x倍率(フォント名)で区別します。

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@sarap422/font-inconsolata-rule@0.1.5/font-face.x1.0546875.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@sarap422/font-hackgen@1.0.0/font-face.css">
font-family: 'Inconsolata Rule x1.0546875', 'HackGen', monospace;

罫線だけがこのフォントから出て(1マス)、英字と日本語は HackGen から出ます。罫線の幅が、HackGen の1マスとぴったり同じなので、日本語を混ぜた枠も揃います。

Inconsolata Rule x1.0546875', 'HackGen' で表示した AA(揃う)

1234567890123456789012345678901234567890
あいうえおかきくけこさしすせそたちつてと
┌──────────────────────────────────────┐
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn
┌───────────────────────────────────────┐
│            日本語を含む箱             │
│            ABC あいう 123             │
└───────────────────────────────────────┘

VS Code でも、同じフォントを OS にインストールして、editor.fontFamily に同じ指定をすれば使えます。

"editor.fontFamily": "'Inconsolata Rule x1.0546875', 'HackGen', monospace"

罫線以外の記号(● ← ※)も幅がフォントで違う

罫線と同じ Ambiguous の記号(● ← → ■ ▲ …)も、フォントによって1マスか2マスかが違います。

文字 Inconsolata UDEV Gothic BIZ UDGothic HackGen
● 1マス 1マス 2マス 2マス
← 1マス 1マス 2マス 2マス
※ なし 2マス 2マス 2マス

HackGen では、● ○ ◆ ◇ ■ □ ▲ △ ▼ ▽ ◎ ★ ☆ ※ … × ± ÷ ← → ↑ ↓ が、すべて2マスです。また、箇条書きによく使う •(U+2022)と ·(U+00B7)は、字形がなく、別のフォントで表示されるので、幅が不定になります。

箱の中で1マスの点を使いたい場合は、HackGen では ・(U+FF65 半角中点)が1マスでした。デザイナーから記号を指定される場合は、箱の中では、罫線と ASCII 以外の記号を避けるのが無難です。


表示まわりの CSS

フォント以外にも、AA を崩さないための設定があります。

pre, code {
  font-variant-ligatures: none;   /* 合字を切る */
  letter-spacing: 0;
  line-height: 1.5;               /* 縦罫線をつなげる */
  /* アンチエイリアス */
  -webkit-font-smoothing: auto;
  -moz-osx-font-smoothing: auto;
}
  • line-height:罫線の縦線は、字形の高さに限りがあります。行間が広すぎると、縦線が点線のように途切れます。
  • アンチエイリアス:Mac では、細く掠れて見えることがあります。-webkit-font-smoothing: auto にすると、見やすくなります。

関連する、別の原因

罫線がズレる原因は、ここまでの3つだけではありません。次のような報告もあります。

  • フォントサイズが奇数だとズレる:全角と半角が混ざる行で、少しずつズレるという報告があります(Sublime Text や JetBrains 系エディタ)。偶数サイズにすると直る場合があります。
  • フォントフォールバック:使っているフォントに罫線の字形が十分になく、別のフォントで表示されて崩れる例があります。罫線を持つフォントを font-family の先頭に置くと直ります。
  • VS Code の Monaco Editor:一部の全角記号(α など)を、半角1文字の幅として判定します。インデントの位置がズレる原因になります。

まとめ

  • 等幅フォントでも、罫線が1マスか2マスか、1マスが 0.5em か 0.6em か は、フォントごとに違う。
  • AA が揃う条件は、「英字 = 1マス」「罫線 = 1マス」「全角 = 2マス」の3つ。
  • 満たすフォント(UDEV Gothic、M PLUS 1 Code)を使うか、罫線が1マスで 0.5em のフォント(Inconsolata)を先頭に置く。
  • HackGen のように、全角が 1.054em で罫線が2マスのフォントは、倍率を合わせた罫線だけのフォントを先頭に置くと揃う。
  • AI が書いた AA は、ソースの桁数を、表示幅を計算するプログラムで検証する。

参考