注意
この記事はAIの補助を受けて編集しています。
目次
- 1. 問題: なぜ width:100% なのに要素がはみ出すのか?
- 2. 本質: CSS Box Model の構造
- 3. ブラウザが実際の幅を計算する仕組み
- 4.
box-sizing: 解決策の本質 - 5. React + TypeScript での実践例
- 6.
marginとブロックレベル要素の落とし穴 - 7.
min-width/max-widthと Box Model - 8. Flexbox・Grid と
min-width: autoの注意点 - 9. パフォーマンスとデバッグ
- 10. 実践チェックリスト
- 11. まとめ
- 12. 参考資料
- 次回予告
1. 問題: なぜ width:100% なのに要素がはみ出すのか?
次のコードを考えてみましょう。
<div class="parent">
<div class="child">
ここにコンテンツ
</div>
</div>
.parent {
width: 300px;
background: #eee;
}
.child {
width: 100%;
padding: 20px;
background: #3498db;
}
多くの人は .child が .parent にぴったり収まる(300px)と考えるでしょう。しかし実際には、.child は 親からはみ出して しまいます。
なぜでしょうか? デフォルトでは、width: 100% は 「コンテンツ領域 (content area) の幅を親のコンテンツ領域の幅の100%にする」 という意味です。padding や border を追加すると、それらは width の 外側 に追加されるため、実際の表示幅が親を超えてしまいます。
これはCSSで最もよくある落とし穴の一つです。
2. 本質: CSS Box Model の構造
すべてのHTML要素は ボックス (box) です。Box Model はそのボックスの構造を定義します。
-
Content area: 実際のテキストや画像などが入る領域。
widthとheightはこのサイズを指定する。 - Padding area: コンテンツとボーダーの間の余白。背景色・背景画像はここまで広がる。
- Border area: 枠線。
- Margin area: ボーダーの外側の余白。他の要素との間隔を空ける。
単純に言うと、デフォルトでは width はコンテンツ領域の幅だけを意味し、padding や border はその外側に追加される ということです。
2.1. content-box – デフォルトの動作
content-box は box-sizing のデフォルト値です。この場合:
実際の幅 = width + padding-left + padding-right + border-left + border-right
例:
.box {
width: 200px;
padding: 20px;
border: 5px solid black;
}
実際の幅 = 200 + 40 + 10 = 250px。
2.2. border-box – 実用的な解決策
box-sizing: border-box を設定すると、計算方法が変わります。
width = コンテンツ + padding + border
ブラウザはコンテンツ領域を自動的に縮小し、合計が指定した width に収まるようにします。
同じ例で:
.box {
width: 200px;
padding: 20px;
border: 5px solid black;
box-sizing: border-box;
}
コンテンツ幅 = 200px - 40px(padding) - 10px(border) = 150px。
3. ブラウザが実際の幅を計算する仕組み
重要なポイント: width: 100% は、親の コンテンツ領域 (content width) を基準に計算されます。親が box-sizing のどちらを使っていても同じです。
例:
.parent {
width: 300px;
padding: 20px;
box-sizing: border-box; /* 親は border-box */
}
.child {
width: 100%;
padding: 10px;
box-sizing: content-box; /* 子はデフォルト */
}
段階を追って計算:
-
.parentはwidth: 300px、padding: 20px。border-boxなので、親のコンテンツ幅 = 300 - 40 = 260px。 -
.childのwidth: 100%→ 親のコンテンツ幅 260px の100% = 260px(子のコンテンツ幅)。 - 子には
padding: 10pxずつ → 子の実際の幅 = 260 + 20 = 280px。
結論: 子の幅は 280px ですが、親のコンテンツ領域は 260px しかありません。その結果、子要素は親の content 領域に収まらず、padding 領域まで入り込みます。ただし、親の border-box 全体は 300px なので、親のパディングが十分に大きければ、外側から見てはみ出しているとは限りません。しかし、親のパディングが小さい場合や子にさらにボーダーがあると、完全にはみ出す可能性があります。
width: 100% + 子の border-box が必ずはみ出さないわけではありません。 子のパディングやボーダーは子の合計幅を増やしませんが、それでも子が親のコンテンツ領域からはみ出してパディング領域に入り込むことがあります。
4. box-sizing: 解決策の本質
| 値 | 意味 |
width: 100% への影響 |
|---|---|---|
content-box |
width はコンテンツ領域のみ。パディング・ボーダーは外側に追加される。 |
パディングやボーダーがあると簡単にはみ出す。手動計算が必要。 |
border-box |
width はコンテンツ+パディング+ボーダーを含む。パディング・ボーダーは内側。 |
安全: パディング/ボーダーが合計幅を増やさない。ただし margin や Flexbox/Grid には注意。 |
推奨される解決策:
/* 方法1: 全要素に直接設定 (一般的) */
*,
*::before,
*::after {
box-sizing: border-box;
}
全体設定の注意点: 一部のサードパーティ製コンポーネントやライブラリは content-box を前提としている場合があります。ただし、主要なフレームワーク(Bootstrap, Tailwind, MUI)は border-box または互換性のあるリセットを提供しています。より安全にしたい場合は、inherit パターンを使うこともできます。
/* 方法2: 継承を使った制御可能な全体設定 */
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
この方法を使えば、特定のコンポーネントだけ content-box に戻すことが可能です。
5. React + TypeScript での実践例
5.1 NG例 – padding + width:100% ではみ出す
// BadCard.tsx
import React from 'react';
import './BadCard.css';
export const BadCard: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return <div className="bad-card">{children}</div>;
};
/* BadCard.css */
.bad-card {
width: 100%;
background: #f8f9fa;
padding: 24px;
/* box-sizing はデフォルトの content-box */
border: 1px solid #dee2e6;
}
このカードを幅600pxのコンテナに入れると、width: 100% = 600px、パディング48pxとボーダーを加えると合計が600pxを超え、はみ出します。
5.2 改善例 – box-sizing: border-box で修正
// GoodCard.tsx
import React from 'react';
import './GoodCard.css';
export const GoodCard: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return <div className="good-card">{children}</div>;
};
/* GoodCard.css */
.good-card {
box-sizing: border-box;
width: 100%;
background: #f8f9fa;
padding: 24px;
border: 1px solid #dee2e6;
}
これでパディングとボーダーが width: 100% の内側に収まり、カードは親にぴったり収まります。
5.3 box-sizing の全体設定
/* global.css */
*,
*::before,
*::after {
box-sizing: border-box;
}
この全体設定を適用すれば、パディングやボーダーによる width: 100% のはみ出しはほぼ気にしなくてよくなります。ただし、margin、overflow、white-space、そして Flexbox/Grid などの特別なレイアウトルールには依然として注意が必要です(セクション8参照)。
6. margin とブロックレベル要素の落とし穴
margin は width に含まれませんが、横方向のはみ出しを引き起こす可能性があります。
例:
.parent {
width: 300px;
background: lightgray;
}
.child {
width: 100%;
margin-left: 50px;
}
この場合、要素が占有する横方向のスペースは 300px + 50px となり、結果として親要素からはみ出します。
対処法:
-
width: 100%を持つ要素に横方向のmarginを使うのは避ける。 -
calc()を使う:width: calc(100% - 50px); margin-left: 50px; - あるいは
width: 100%を使わない – ブロックレベルの要素はデフォルトでwidth: autoになっており、これで十分な場合が多い。
width: autoとwidth: 100%の違いを明確に:
CSS2.1 によれば、ブロックレベル要素のwidth: autoは次のように計算されます:
包含ブロックの幅 – margin-left – margin-right – padding-left – padding-right – border-left – border-right。
つまり、要素は利用可能なスペースを自動的に埋めるように広がりますが、パディングやボーダーが追加で外側に付くことはありません。
例:.child { display: block; margin: 0 20px; /* width: auto (デフォルト) => width = 包含ブロックの幅 - 40px */ }このように、
width: autoを使えばwidth: 100%は不要で、マージンによるはみ出しも起こりません。
結論:width: autoとwidth: 100%は計算方法が異なります。多くの場合、autoの方が安全です。
7. min-width / max-width と Box Model
min-width や max-width も box-sizing の影響を受けます。
-
content-box:min-widthはコンテンツ領域に適用。 -
border-box:min-widthはパディングとボーダーを含む。
パーセント値を使う場合、min-width や max-width の基準は 包含ブロックの幅 です(親の box-sizing は関係ありません)。ただし、box-sizing はその要素自身のどの部分が min/max の制限を受けるかを決めます。
画像の安全な例:
img {
max-width: 100%;
height: auto;
box-sizing: border-box;
}
border-box を忘れると、パディングやボーダーがある画像ではみ出す可能性があります。
8. Flexbox・Grid と min-width: auto の注意点
Box Model だけですべてのはみ出し問題が解決するわけではありません
Flexbox や Grid の中では、border-box を使っていても要素がはみ出すことがあります。
よくある原因の一つ は、Flexbox/Grid アイテムのデフォルト値 min-width: auto です。仕様では、min-width: auto は 「内容の内在サイズ (intrinsic size)」 を最小幅とします。つまり、要素は自分の内容より小さくならないということです。これを上書きしない限り、長いテキストなどがあると親からはみ出します。
例:
<div class="flex-parent">
<div class="child">とても長い長い長い長いテキストで改行されない</div>
</div>
.flex-parent {
display: flex;
width: 200px;
background: #eee;
}
.child {
box-sizing: border-box;
width: 100%;
padding: 16px;
background: #3498db;
/* white-space: nowrap; があるともっと悪化 */
}
border-box と width: 100% があっても、内容が長くて改行されなければはみ出します。
対処法:
- Flexbox では子要素に
min-width: 0を設定する(内容より小さくすることを許可)。 -
overflow: hiddenやword-break: break-word/overflow-wrap: anywhereを使う。
.child {
min-width: 0; /* 縮小を許可 */
word-break: break-word; /* 長い単語を折り返す */
}
また、white-space: nowrap はナビゲーションバーやタグリストなどではみ出しの原因になりがちなので注意しましょう。
Grid に関する補足: Grid でも
min-width: autoは同様に影響しますが、minmax()、固定トラック、内在サイズ、grid-template-columnsなどもはみ出しの原因になります。いずれにしても、min-width: 0は有効な対策です。
9. パフォーマンスとデバッグ
-
box-sizingはレンダリングパフォーマンスに影響しません。 - レイアウトのはみ出しをデバッグするには、DevTools を開き、対象要素を選択して Computed タブの Box Model を確認します。
-
margin、padding、border、min-width、white-spaceなどが原因かどうか調べます。
10. 実践チェックリスト
- □ すべての要素に
box-sizing: border-boxを設定したか?(inheritパターンも検討) - □
width: 100%とpadding/borderを併用する場合、その要素にもborder-boxを設定したか? - □ 親要素が
content-boxかborder-boxかを理解しているか?(親がborder-boxでなくても問題ないが、計算方法は理解すべき) - □
width: 100%とmarginを併用する場合、calc()で調整するか、width: autoに変更することを検討したか? - □ 画像・動画・iframe には
max-width: 100%とbox-sizing: border-boxを設定したか? - □ Flexbox/Grid の中で、子要素に
min-width: 0が必要か確認したか?white-space: nowrapが原因になっていないか? - □ DevTools で実際のサイズを確認したか?
- □ 小さいビューポートでのレスポンシブ動作、特に Flex/Grid コンテナをテストしたか?
11. まとめ
-
問題:
padding/borderがwidthの外側に追加される(content-boxの仕様)ため、width: 100%がはみ出す。 -
本質: Box Model は content・padding・border・margin からなる。デフォルトでは
widthは content 領域のみ。 -
解決策:
box-sizing: border-boxを使うことでwidthに padding と border を含める。全体設定が推奨される。 -
落とし穴:
margin、white-space: nowrap、Flexbox/Grid のmin-width: autoなどは依然としてはみ出しを引き起こす可能性がある。 - 常に DevTools で確認し、複数の画面サイズでテストすること。
12. 参考資料
- MDN: Box model
- MDN: box-sizing
- CSS Box Model Module Level 3
- Web.dev: Learn CSS – Box Model
- CSS Tricks: Box Sizing
- Flexbox min-width issue (CSS Tricks)
- MDN: min-width (Flexbox の項)
- Intrinsic sizing 仕様
次回予告
👉 【Frontend CSS – パート5】position はブラウザでどう動くのか?Containing Block と座標系を理解する
