CSSのtab-sizeで、コードのタブの幅を変える方法
タブでインデントしたコードを <pre> に入れると、ブラウザーはタブ1つを空白8文字分の幅で表示します。エディターで4文字分のつもりで書いたコードが、ブログでは横に間延びして見えるのはこのためです。pre に tab-size: 4 を1行足すと、タブは空白4文字分の幅になります。
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。等幅フォントは DejaVu Sans Mono(16px)を使い、タブのあとの文字が、要素の内容の左端から何pxの位置に来るかを測りました。Firefox と Safari での表示は試していません。各ブラウザーの対応バージョンは、互換性データ(@mdn/browser-compat-data 8.1.8)の値です。
pre に tab-size を付ける
コードを表示する要素に指定します。値は、タブの幅を空白何文字分にするかを表す数値です。
pre {
tab-size: 4;
}
検証環境で、行の先頭にタブを1つ入れた pre を比べました。DejaVu Sans Mono の空白1文字の幅は 9.63px です。
| 指定 | タブのあとの文字の位置 |
|---|---|
| 指定なし | 77.06px(空白8文字分) |
tab-size: 4 |
38.53px(空白4文字分) |
tab-size: 2 |
19.27px(空白2文字分) |
指定なしのときの計算値は 8 でした。CSS の仕様でも、初期値は 8 と定められています。

tab-size は継承されるプロパティです。body に tab-size: 4 を書くと、中の pre の計算値は 4 になり、タブは空白4文字分で表示されました。サイト全体のコードブロックをそろえたいなら、body か pre にまとめて書けば足ります。
コードの色分けでは、タブが span に入ることがあります。pre に tab-size: 4 を付け、タブを中の span に入れても、span の計算値は 4 になり、タブのあとの文字は 38.53px(空白4文字分)の位置に来ました。
タブは「次のタブ位置まで進む」
tab-size: 4 は「タブ1つを空白4文字にする」という意味ではありません。4文字ごとに目に見えない区切り(タブ位置)があり、タブは次のタブ位置まで進みます。タブの前に何文字あるかで、タブ1つの幅が変わります。

検証環境での位置は次のとおりです。
| 行の中身 | タブのあとの X の位置 |
|---|---|
a とタブと X |
38.53px(空白4文字分) |
abc とタブと X |
38.53px(空白4文字分) |
abcd とタブと X |
77.06px(空白8文字分) |
abcde とタブと X |
77.06px(空白8文字分) |
a のあとのタブは3文字分、abc のあとのタブは1文字分の幅になり、どちらも X は空白4文字分の位置にそろいました。abcd はちょうど空白4文字分のタブ位置で終わりますが、タブは幅0にならず、空白8文字分の位置まで進みました。表のように列をそろえるためのタブも、この動きで縦にそろいます。
タブ位置は、タブを含むブロック(ここでは pre)の内容の左端から数えます。ab を span で包んでも X の位置は 38.53px のままで、pre に padding-left: 20px を付けても、内容の左端から 38.53px でした。
長さで指定する
tab-size には 40px や 2em のような長さも書けます。検証環境では、tab-size: 40px でタブのあとの文字が 40px の位置に、tab-size: 2em(文字の大きさ 16px)で 32px の位置に来ました。2em の計算値は 32px でした。
長さで指定したときも、タブ位置の考え方は同じです。ab のあとのタブでも、X は 40px の位置にそろいました。
CSS Text の仕様の、タブの表示を定めた節には、次のタブ位置までの距離が 0.5ch より短いときは、その次のタブ位置まで進む、という決まりもあります。tab-size: 40px で abcd(幅 38.53px)のあとにタブを置くと、40px のタブ位置までは 1.47px しかなく、0.5ch(4.82px)より短いので、X は 80px の位置に来ました。前の節の abcd のあとのタブも、この決まりに当てはめると、次のタブ位置までの距離が0で 0.5ch より短いので、その次のタブ位置まで進むことになります。
数値の基準は、そのフォントの空白の幅
数値で指定したときの基準は、タブを含むブロック(pre など)のフォントの空白(U+0020)の幅です。等幅フォントの「1文字分」とは限りません。
検証環境で、tab-size: 4 の pre のフォントを Noto Sans CJK JP に変えると、空白1文字の幅は 3.58px で、タブのあとの文字は 14.34px の位置に来ました。DejaVu Sans Mono の 38.53px と比べると、4割ほどの幅です。Noto Sans CJK JP のように空白の幅が狭いフォントをコードブロックに使うと、tab-size: 4 でもインデントがかなり狭く見えます。
letter-spacing も幅に入ります。DejaVu Sans Mono に letter-spacing: 2px を付けると、位置は 46.53px(空白1文字 9.63px に 2px を足した幅の4つ分)になりました。仕様の tab-size の節にも、空白の幅には letter-spacing と word-spacing を含めると書かれています。
0 や小数、効かない値
tab-size: 0 にすると、タブは表示されなくなりました。a とタブと X を並べると、X は a のすぐ後ろ(9.64px)に来ます。MDN の tab-size のページでは、インデントを消す例として紹介されています。
数値には小数も書けます。tab-size: 4.5 は計算値が 4.5 になり、タブのあとの文字は 43.36px(空白4.5文字分)の位置に来ました。
負の数、パーセント、2つ以上の値は書けません。-1、50%、4px 2 は、どれも CSS.supports('tab-size', ...) が false になり、指定は無視されて計算値は 8 のままでした。
空白をまとめる要素では効かない
tab-size が効くのは、タブがそのまま残る要素だけです。white-space: normal の要素では、タブは空白といっしょに1つにまとめられ、行の先頭では消えます。
検証環境で、行の先頭にタブを入れ、tab-size: 4 を付けた要素を white-space の値ごとに比べました。
| white-space | タブのあとの文字の位置 |
|---|---|
pre |
38.53px |
pre-wrap |
38.53px |
break-spaces |
38.53px |
normal(初期値) |
0px |
nowrap |
0px |
pre-line |
0px |
pre、pre-wrap、break-spaces ではタブが残り、tab-size のとおりの幅になりました。normal、nowrap、pre-line では、タブが消えて文字が左端から始まりました。仕様の空白の処理を定めた節でも、まとめる対象のタブは空白に変えると定められています。
<pre> 以外の要素でコードを表示しているなら、white-space: pre か pre-wrap もいっしょに付けます。
.code {
white-space: pre-wrap;
tab-size: 4;
}
textarea は、検証環境では white-space の計算値が pre-wrap(タブが残る値)でした。tab-size: 4 を付けると、計算値も 4 になりました。
tab-size が変えるのは表示の幅だけです。tab-size: 2 の pre の中身を選択すると、選択範囲の文字列(getSelection().toString())はタブのままでした。
対応しているブラウザー
互換性データでは、数値の指定と長さの指定で、対応したバージョンが違います。
| ブラウザー | 数値(tab-size: 4) |
長さ(tab-size: 40px) |
|---|---|---|
| Chrome | 21 | 42 |
| Edge | 79 | 79 |
| Firefox | 91(4 から -moz-tab-size) |
53 |
| Safari | 7 | 13.1 |
| Safari(iOS) | 7 | 13.4 |
Firefox は、互換性データでは、接頭辞付きの -moz-tab-size が 4 から、接頭辞なしの tab-size が 91 からの対応です。長さの値が、接頭辞なしの対応より前の 53 からとなっている理由は、互換性データからは分かりません。
Firefox も接頭辞付きの -moz-tab-size を含めれば、どのブラウザーも数値の指定には早いバージョンから対応しています。コードブロックのインデントをそろえるだけなら、tab-size: 4 のような数値で書けば足ります。





