CSS で要素に width: 200px と指定したのに、padding や border を足したら見た目が 200px より大きくなってレイアウトが崩れた、という経験はないでしょうか。これは要素の幅をどう計算するかを決める box-sizing プロパティが関係しています。この記事では box-sizing の既定値である content-box と、現場でよく使われる border-box の違い、padding や border を含めて幅を計算する仕組み、そしてレイアウトが崩れる原因と border-box による解決、最後に定番のリセット指定までを、表示を見比べながら解説します。
目次
box-sizing は「width に何を含めるか」を決めるプロパティ
ブラウザは要素を、内容(コンテンツ)・padding(内側の余白)・border(枠線)・margin(外側の余白)という4つの層が重なった箱として扱います。これをボックスモデルと呼びます。このとき width や height で指定したサイズが「どこまでの範囲を指すのか」を切り替えるのが box-sizing です。
値は主に2つで、初期値の content-box は width をコンテンツ領域だけの幅として扱い、border-box は padding と border までを含めた幅として扱います。なお margin はどちらの値でも width には含まれず、常に箱の外側に置かれます。この違いを押さえると、サイズ指定が思った通りにならない原因の多くが理解できるようになります。
content-box(既定)の計算方法
box-sizing を指定しないとき、要素は content-box として扱われます。このモードでは width はコンテンツ領域だけの幅を表し、padding と border はその外側に追加されます。つまり画面に表示される実際の横幅は、指定した width に左右の padding と border を足した値になります。
.box {
box-sizing: content-box; /* 既定値。指定しなくても同じ挙動 */
width: 200px; /* これはコンテンツ領域だけの幅 */
padding: 20px; /* 左右に 20px ずつ外側へ追加 */
border: 5px solid #007bff; /* 左右に 5px ずつ外側へ追加 */
}
/* 実際の表示幅 = 200 + (20 + 20) + (5 + 5) = 250px */
このように content-box では、コンテンツの幅(200px)に左右の padding 合計 40px と左右の border 合計 10px が加わり、要素全体の幅は 250px になります。width に書いた数字と実際の見た目の幅が一致しないのが、このモードの分かりにくさです。
border-box の計算方法
border-box を指定すると、width は padding と border まで含めた箱全体の幅を表すようになります。padding や border を増やすと、その分だけコンテンツ領域が内側に削られていき、要素全体の幅は width に指定した値のまま変わりません。
.box {
box-sizing: border-box; /* width に padding と border を含める */
width: 200px; /* これが要素全体の幅になる */
padding: 20px;
border: 5px solid #007bff;
}
/* 実際の表示幅 = 200px のまま */
/* コンテンツ領域 = 200 - (20 + 20) - (5 + 5) = 150px に縮む */
同じ width: 200px・padding: 20px・border: 5px でも、border-box なら要素全体の幅はぴったり 200px に収まります。代わりにコンテンツが入る領域が 150px に縮みます。「指定した幅がそのまま見た目の幅になる」という直感的な挙動が、border-box が好まれる理由です。次の対応表で2つのモードの違いを整理します。
| 値 | width が指す範囲 | 同じ指定での実際の表示幅 |
|---|---|---|
content-box | コンテンツ領域だけ。padding・border は外側に加算される(初期値) | 250px(200 + padding 40 + border 10) |
border-box | コンテンツ+padding+border を含めた箱全体 | 200px(コンテンツが 150px に縮む) |
2つのモードを並べて見比べる
言葉だけでは差がつかみにくいので、まったく同じ width・padding・border を指定した2つの箱に、content-box と border-box をそれぞれ当てて見比べてみましょう。下のデモで、上の箱(content-box)の方が横に大きく広がっているのが確認できます。CSS タブの width や padding の値を書き換えると、それぞれのモードで幅がどう変化するかを試せます。
<div class="demo">
<div class="box content-box">
<span class="label">box-sizing: content-box(既定)</span>
<p>width: 200px に padding と border が外側に足され、実際の見た目の幅は 250px になります。</p>
</div>
<div class="box border-box">
<span class="label">box-sizing: border-box</span>
<p>width: 200px の中に padding と border が含まれ、見た目の幅はちょうど 200px に収まります。</p>
</div>
</div>
.demo {
font-family: sans-serif;
background: #eef1f5;
padding: 16px;
}
.box {
width: 200px; /* 2つとも同じ width を指定 */
padding: 20px; /* 上下左右に 20px の余白 */
border: 5px solid #007bff; /* 5px の枠線 */
margin-bottom: 16px;
background: #fff;
}
.label {
display: block;
font-size: 13px;
color: #007bff;
font-weight: bold;
margin-bottom: 6px;
}
.box p {
margin: 0;
font-size: 14px;
}
/* 既定の計算方法。width に padding と border が加算される */
.content-box {
box-sizing: content-box;
}
/* width の中に padding と border を含めて計算する */
.border-box {
box-sizing: border-box;
}
2つの箱には同じ width: 200px を指定していますが、content-box の箱は padding と border が外側に足されて 250px に広がり、border-box の箱は 200px に収まっています。指定が同じでも、box-sizing の値しだいで見た目の幅が変わることがはっきり分かります。
width を指定するとレイアウトが崩れる原因
初期値の content-box が原因でレイアウトが崩れる典型例が、横並びの2カラムです。親要素を半分ずつに分けようと、子要素を width: 50% で2つ並べる場面を考えてみましょう。
.column {
width: 50%; /* 親の半分の幅にしたい */
padding: 16px; /* ところが padding が外側に加算される… */
float: left;
}
/* 実際の幅 = 親の50% + 左右 padding 32px */
/* 2つ合わせると 100% + 64px となり、横幅を超えて折り返してしまう */
content-box では width: 50% はコンテンツ領域だけの幅なので、そこに左右の padding 32px が外側へ加わります。1カラムの実際の幅は「親の50%+32px」となり、2つ横に並べると合計が親の幅(100%)を超えてしまいます。その結果、2つ目のカラムが入りきらずに下へ折り返し、意図した2カラムが崩れてしまうのです。
同じことは、% 指定の入力フォームでも起こります。input に width: 100% と padding を同時に指定すると、要素が親の幅をはみ出して横スクロールが出る、という不具合の多くがこの計算方法に由来します。
border-box でレイアウト崩れを防ぐ
この問題は、対象の要素に box-sizing: border-box を指定するだけで解決します。border-box では padding や border が width の内側に含まれるため、width: 50% が padding 込みでちょうど親の半分を意味するようになります。
.column {
box-sizing: border-box; /* padding を width の内側に収める */
width: 50%; /* padding 込みで親の半分になる */
padding: 16px;
float: left;
}
/* 実際の幅 = 親の50% のまま。2つ並べてもちょうど 100% に収まる */
border-box にすると、padding を足してもカラムの幅は親の50%を保ちます。2つ並べても合計はちょうど100%に収まるため、折り返しは起きません。width と padding を同時に使うレイアウトでは、border-box にしておくと計算がシンプルになり、サイズの管理がぐっと楽になります。
* セレクタで border-box をまとめて適用する
要素ごとに border-box を書くのは手間ですし、書き忘れも起こります。そこで多くのプロジェクトでは、スタイルシートの先頭で全要素にまとめて border-box を適用してしまうのが定番です。これはリセット CSS の一部としてよく使われる書き方です。
* {
box-sizing: border-box;
}
* はすべての要素を選ぶ全称セレクタです。これで全要素が border-box で計算されるようになり、width に書いた値がそのまま見た目の幅になる、という直感的な状態を作れます。さらに堅牢にしたい場合は、擬似要素も対象に含め、継承を使った次の書き方がよく知られています。
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit; /* html の border-box を全要素・擬似要素が継承する */
}
この書き方では html に border-box を指定し、ほかの要素には box-sizing: inherit で継承させます。一部のコンポーネントだけ content-box に戻したいときに、その要素で値を変えればその配下にも継承が伝わる、という柔軟さが利点です。どちらの書き方でも狙いは同じで、まず全体を border-box に統一しておく、というのが現在の一般的なスタイルです。
box-sizing を指定したのに幅が変わらないとき
そもそも width や padding を指定していない
box-sizing は width・height と padding・border の関係を切り替えるプロパティです。そのため width を明示していない要素や、padding・border がない要素では、値を border-box に変えても見た目はほとんど変わりません。差が出るのは、サイズ指定と内側の余白・枠線が両方そろっているときだけだと意識すると、検証がしやすくなります。
margin は box-sizing の対象外
border-box にしたのに要素同士の間隔のせいでまだはみ出す、という場合は margin を疑ってください。box-sizing がまとめてくれるのは padding と border までで、margin は常に箱の外側に置かれ、width には含まれません。width: 50% の要素に左右の margin を付ければ、その分だけ全体の占有幅は親を超えていきます。横並びで余白を取りたいときは、margin ではなく padding で内側に確保するか、Flexbox の gap を使うと計算が崩れにくくなります。
まとめ
box-sizing は width と height が要素のどこまでを指すかを決めるプロパティです。要点を振り返ります。
content-box(初期値)はwidthがコンテンツ領域だけを指し、padding・borderは外側に加算されるborder-boxはwidthにpadding・borderを含め、指定した幅がそのまま見た目の幅になるwidth: 50%や100%とpaddingを併用するとき、content-boxははみ出しの原因になりやすいborder-boxにすれば計算が直感的になり、レイアウト崩れを防げる* { box-sizing: border-box }で全体に適用するのが定番のリセットmarginはbox-sizingの対象外で、常に箱の外側に置かれる
まずはスタイルシートの先頭に * { box-sizing: border-box } を1行加えるところから始めてみてください。それだけで、サイズ指定にまつわる多くの「なぜか崩れる」を防げるようになります。