ボックスモデル
CSS にはボックスという概念があります。ブラウザは文書をレイアウトする際に、それぞれの要素を CSS のボックスモデルに基づいた長方形の箱(ボックス)として表現します。つまりは、HTMLの個々の要素はすべてボックスにからできており、それらのボックスが組み合わさることによってページ全体が表現されます。 ボックスは CSS において最も基本的な概念であり、ボックスを理解することは CSS でレイアウトを構成したり要素同士を揃えるための手助けとなることでしょう。
CSS にはボックスという概念があります。ブラウザは文書をレイアウトする際に、それぞれの要素を CSS のボックスモデルに基づいた長方形の箱(ボックス)として表現します。つまりは、HTML の個々の要素はすべてボックスにからできており、それらのボックスが組み合わさることによってページ全体が表現されます。
ボックスは CSS においてもっとも基本的な概念であり、ボックスを理解することは CSS でレイアウトを構成したり要素同士を揃えるための手助けとなることでしょう。
ボックスの構成要素
ボックスは以下の 4 つの要素から構成されています。
- Content box
- Padding box
- Border box
- Margin box
Content box
Content box
は文字や画像などのコンテンツが表示される領域です。ボックスモデルの既定では width や height はコンテンツ領域のみに適用され、ボックスサイズを変更します。
Padding box
Paddig box
はコンテンツの周囲に表示される空白の領域であり border
より内側に表示されます。この領域は padding プロパティによってサイズが制御されます。
Border box
Border box
はパディング領域を取り囲み、コンテンツの境界を表します。boder プロパティによって要素の色やサイズを変更できます。
Margin box
margin box
はボックスのもっとも外側の領域で、主に他の要素との間の空白を表現します。この領域は margin プロパティによって制御されます。
Dev Tools のボックスモデル
実際の要素がどのようなボックスで構成されているのかは dev tools から確認できます。Elements
タブから要素を選択した後 Computed
タブを選択します。
Computed
タブは実施の要素に対していくつの margin
や padding
が適用されているか知ることができるので非常に役に立ちます。さらに dev tools 上のボックスモデルにカーソルを合わせると実際の要素に対してもハイライトが表示されます。
ボックスの width はどう決まる?
ボックスの基本的な構造を確認したところで実際にボックスを配置してみましょう。以下のように横ならびにボックスを配置します。
コードは以下のとおりです。
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<div class="container">
<div class="box1">I'm box1</div>
<div class="box2">I'm box2</div>
</div>
</body>
</html>
- style.css
.container {
max-width: 1080px;
margin: 0 auto;
}
.box1 {
float: left;
width: 70%;
padding: 20px;
border: 1px solid #808080;
background-color: aqua;
}
.box2 {
float: left;
width: 30%;
padding: 20px;
border: 1px solid #808080;
background-color: lightgoldenrodyellow;
}
.box1
には width: 70%
を設定し .box2
には width: 30%
を設定しているので、2 つの要素を合わせてぴったり 100%
になるはずです。しかし、実際に表示されるレイアウトは以下のように想定とはことなる表示となってしまいます。
2 つ目のボックスが 1 つ目のボックスの下側に配置されてしまいました。なぜこのようなことが起きてしまったのでしょう。結論としては 2 つのボックスの長さの合計が 100%
を超えてしまったためです。
Contents box
の説明で述べたとおり、ボックスモデルの既定では width や height はコンテンツ領域のみに適用されます。つまりは 70%
の width
に対して padding・border・margin のサイズを加えた値が実際の要素の長さとなるわけです。
今回の例ではそれぞれのボックスのサイズは実際には以下のとおりになります。
width | paddibg | border | 合計 | |
---|---|---|---|---|
box1 | 1080px * 70% = 756px | 20px + 20px | 1px + 1px | 798px |
box2 | 1080px * 30% = 324px | 20px + 20px | 1px + 1px | 366px |
2 つのボックスのサイズの合計は 798px + 366px = 1164px
で確かに親要素の 1080px
を超えてしまっています。
ですからボックスを 1 行に横並びさせたい場合には width
を設定するときに padding
と border
の長さを引く必要があります。
.box1 {
float: left;
- width: 70%;
+ width: calc(70% - 40px - 2px);
padding: 20px;
border: 1px solid #808080;
background-color: aqua;
}
.box2 {
float: left;
- width: 30%;
+ width: calc(30% - 40px - 2px);
padding: 20px;
border: 1px solid #808080;
background-color: lightgoldenrodyellow;
}
box-sizing プロパティ
上記の方法で問題は解決したもののなんだか納得がいかない方法です。普通に考えたら見た目通りにボーダーまでを含めて要素のサイズ計算してほしいはずです。実際のサイズを取得するために毎回 width
から padding
と border
のサイズを引かなければならないのはからり不便です。
この挙動は box-sizing
プロパティにより変更できます。デフォルトの box-sizing
のプロパティには content-box
が設定されてます。これはボックスモデルの既定通りコンテンツ領域のみに widht
と height
が設定されます。
box-sizing: border-box
はこの既定の挙動を変更し border
と padding
も含めて width
および height
が設定されるようになります。
.box1 {
box-sizing: border-box;
float: left;
width: 70%;
padding: 20px;
border: 1px solid #808080;
background-color: aqua;
}
box-sizing: border-box
を付与した際に dev tools からコンテンツ領域のサイズの計算方法が変更されていることが確認できます。
- box-sizing: content-box
- box-sizing: border-box
また box-sizing: border-box
は多くの開発者にとって好ましい挙動であり一般的な選択になっています。実際に Bootstrap や Vuetify などの UI フレームワークの多くは box-sizing: border-box
をデフォルトの挙動としています。
実際に自分ですべての要素に対して box-sizing: border-box
を適用したい場合には以下のようなコードが使われます。<html>
要素で box-sizing: border-box;
を設定しその他のすべての要素において継承します。
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
margin の相殺
まずは次のコードをご覧ください。単純にヘッダー要素とメインコンテンツ要素を配置しただけのものです。
<body>
<header class="header">I'm a header</header>
<main class="main">
<p>I'm a main contents</p>
</main>
</body>
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
body {
background-color: #f6f6f4;
}
.header {
padding: 10px;
margin-bottom: 30px;
background-color: white;
}
.main {
padding: 20px;
margin-top: 20px;
background-color: white;
}
ここで問題です。ヘッダー要素とメインコンテンツ要素の間は何 px 間が空いているでしょうか?
答えは .header
の margin-bottom: 30px
と .main
の margin-top: 20px;
を足した 50px… ではなく 30px です。
これは一般に マージンの相殺 と呼ばれる挙動でありブロックの先頭と末尾のマージンは、それぞれのマージンのもっとも大きい寸法(または等しい場合はいずれか 1 つ)の単一のマージンに結合されます。
実際に dev tools で確認すると、よりマージンのサイズの大きい .header
要素の 30px だけ適用されていることが確認できます。
マージンの相殺は以下の条件に当てはまるとき発生します。
- 隣接兄弟要素
- 垂直マージンのみ横方向のマージンの相殺は起こりません
またマージンの相殺には例外条件もあり、例えば float
プロパティや position: abosolute
が設定されている場合にはマージンの相殺は発生しません。
まとめ
- ボックスモデルは
contents
・padding
・border
・margin
の 4 つの要素で構成される - 要素のサイズを予測可能にするために常にすべての要素に対して
box-sizing: border-box
を設定する - 縦に並んだ要素のマージンはより大きなサイズに 1 つにまとめられる
- dev tools はすごい