CSSで、表の数字の幅をそろえて読みやすくする方法

​​CSS​HTML,フォント

表の数字の桁を縦にそろえるには、CSS の font-variant-numeric: tabular-nums で数字をすべて同じ幅にし、数字の列を text-align: right で右寄せにします。この2つがそろうと、一の位、十の位、カンマの位置がどの行でも同じ場所に来ます。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。フォントは Inter 4.0、Noto Sans CJK JP、DejaVu Sans を使い、検証用のページを Playwright で開いて、文字ごとの位置と幅を測りました。Firefox と Safari、Windows や macOS に入っているフォントでの表示は試していません。各ブラウザの対応バージョンは、互換性データ(@mdn/browser-compat-data 8.1.8)の値です。

2つの表を縦に並べた画面。どちらも月、売上(円)、伸び率(%)の3列で、売上の列は 111,111、888,888、47,210、1,019 の4行。上の「指定なし(右寄せだけ)」では、1行目のカンマに引いたピンクの破線が、ほかの行のカンマと重ならず、行ごとに数字の位置がずれている。下の「font-variant-numeric: tabular-nums を追加」では、4行ともカンマが破線の上に並んでいる
上は右寄せだけ、下は tabular-nums を足した表。ピンクの破線は1行目のカンマの位置

書き方

数字の列のセルに num というクラスを付け、表全体に tabular-nums を指定します。

html
<table>
  <thead>
    <tr><th>月</th><th class="num">売上(円)</th><th class="num">伸び率(%)</th></tr>
  </thead>
  <tbody>
    <tr><td>4月</td><td class="num">111,111</td><td class="num">8.50</td></tr>
    <tr><td>5月</td><td class="num">888,888</td><td class="num">12.25</td></tr>
    <tr><td>6月</td><td class="num">47,210</td><td class="num">3.00</td></tr>
    <tr><td>7月</td><td class="num">1,019</td><td class="num">10.75</td></tr>
  </tbody>
</table>
css
table {
  font-variant-numeric: tabular-nums;
}

.num {
  text-align: right;
}

font-variant-numeric は子要素に継承されます(CSS Fonts Level 4 の仕様)。検証環境でも、table に指定すると、中の td の値は tabular-nums になりました。表ごとに1回書けば足ります。見出しの th にも num を付けておくと、上の画像のように、列の見出しも数字と同じ右側にそろいます。

数字の幅がそろっていないと、右寄せでも桁がずれる

欧文フォントには、数字ごとに幅が違うものがあります。検証環境で、Inter の数字1文字ずつの幅を測ると、次のようになりました。単位は em(文字の大きさに対する割合)です。

数字 指定なし tabular-nums
0 0.631 0.648
1 0.407 0.648
4 0.646 0.648
7 0.566 0.648
8 0.619 0.648

指定がないと、「1」は「4」より 0.24em ほど細くなります。tabular-nums を指定すると、0〜9 のすべてが 0.648em になりました。MDN の font-variant-numeric の説明では、tabular-nums は数字を同じ大きさ(幅)で表示して表の中でそろえやすくする指定で、OpenType の tnum の機能に対応するとされています。

この幅の違いは、右寄せにしても消えません。検証環境で、20px の Inter で 111,111、888,888、47,210、1,019 を右寄せにして、右から数えて同じ位置にある文字(一の位、十の位…)の右端が、行どうしでどれだけずれるかを測りました。

右から数えた文字 指定なし tabular-nums
1文字目(一の位) 0.00px 0.00px
2文字目(十の位) 4.48px 0.00px
4文字目(カンマ) 12.72px 0.00px
7文字目(十万の位) 22.03px 0.00px

右寄せなので一の位の右端はそろいますが、左の桁ほどずれが大きくなります。1 だけの 111,111 と、8 だけの 888,888 では、文字列全体の幅が 52.84px と 79.11px で、26px あまり違いました。tabular-nums を指定すると、どちらも 83.19px になり、すべての桁でずれが 0 になりました。

左寄せのままだと、幅をそろえても桁はそろわない

tabular-nums を指定しても、セルが左寄せのままだと桁はそろいません。表のセル(td)の text-align の初期値は start で、左から右に書くページでは左寄せになります。

検証環境で、tabular-nums だけを指定し、text-align を指定しないセルに 111,111、47,210、1,019 を入れると、text-align の値は start のままで、一の位の右端の位置は 94.19px、81.22px、68.25px でした。1桁少ないごとに、数字1文字分(12.97px)ずつ左にずれています。数字の幅がそろっていても、桁数の違う数字は、右端を合わせないと桁が並びません。

小数は、小数点以下の桁数をそろえる

小数を含む列では、右寄せと tabular-nums だけでは小数点の位置がそろいません。検証環境で、12.5、3.25、100、0.8 を並べると、小数点の左端は、12.5 と 0.8 では 36.94px、3.25 では 23.97px で、100 には小数点がありません。

小数点以下の桁数を、表示する前に同じにしておくと、小数点が1つの縦の線に並びます。JavaScript で値を作るなら、Intl.NumberFormat の minimumFractionDigits と maximumFractionDigits を同じにします。

js
const format = new Intl.NumberFormat('ja-JP', {
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});
['12.5', '3.25', '100', '0.8'].map((v) => format.format(Number(v)));
// → 12.50, 3.25, 100.00, 0.80

検証環境で、この 12.50、3.25、100.00、0.80 を表示すると、小数点の左端は4行とも 49.91px にそろいました。

CSS Text Level 4 の仕様には、text-align: "." のように文字を指定して、その文字の位置で列をそろえる書き方があります。ただし、検証環境の Chromium 153 では CSS.supports('text-align', '"."') が false で、使えませんでした。互換性データの text-align の値にも、この書き方は入っていません。

もともと数字の幅がそろっているフォントもある

数字が最初から同じ幅で作られているフォントでは、tabular-nums を指定しても見た目は変わりません。検証環境では、Noto Sans CJK JP の数字はすべて 0.555em、DejaVu Sans の数字はすべて 0.636em で、指定の有無で幅は同じでした。

あとで表のフォントを Inter のような数字の幅が違うフォントに変えても、指定が残っていれば桁はそろったままです。

font-feature-settings でも同じ結果になる

フォントの tnum の機能を直接オンにする font-feature-settings: "tnum" でも、検証環境では tabular-nums と同じ幅(83.19px)になり、桁のずれも 0 でした。

ただ、MDN の font-feature-settings の説明では、できる限り font-variant-numeric などの個別のプロパティを使うべきだとされています。数字の幅をそろえるだけなら、font-variant-numeric: tabular-nums を使います。

主要なブラウザで使える

互換性データでは、font-variant-numeric の tabular-nums は、Chrome 52、Edge 79、Firefox 34、Safari 9.1、iOS の Safari 9.3、Android の Chrome 52 から使えます。

​Posted by 86