CSSでレイアウトしていたら予想外のカラム落ちが起きてしまった話

## 何が起きたのか？

今回、図の左側のようなレイアウトにしたかったのですが、右側のようなレイアウト（いわゆる**カラム落ち**）になってしまい「ん？何で？？」となりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/3af8a9da-40eb-c979-c27c-d55ce351b98a.png)

## 最初に書いたコード

```html: HTML
<div class="container">
  <div class="item">item1</div>
  <div class="item">item2</div>
  <div class="item">item3</div>
  <div class="item">item4</div>
</div>
```

```css: CSS
.container {
  width: 500px;
  height: 500px;
  background-color: lightcyan;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
}
.item {
  width: 250px;
  height: 50px;
  background-color: bisque;
}
```

親要素 `.container` の **width** を 500px としています。
その内側に4つの子要素 `.item` を配置しており、親要素の半分の 250px としています。
結果、子要素は2つ並んで下に落ちていくようなレイアウトになります。
ここまでは、想定通りのレイアウトになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/0123e006-3472-6b43-3509-cc34fcc6952c.png)

## 子要素に枠線を付けてみた

次に、子要素に対して枠線を付けてみました。

```css
.item {
  width: 250px;
  height: 50px;
  background-color: bisque;
  border: 1px solid brown; /* ここを追加 */
}
```

すると、冒頭で紹介した図の右側のレイアウトになってしまいました。
想定では（枠線を付ける前と同じように）item2 は item1 の右側に配置されると考えていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/67a62623-f2b3-4738-ec92-7466e1643de0.png)

## なぜカラム落ちが発生したのか？

今回のカラム落ちを説明するにあたり、**ボックスモデル**を理解しておく必要があります。

全ての HTML 要素は**ボックス**と呼ばれる四角形の表示領域を持っています。内側から「①テキストや画像などの内容が入る**コンテンツ**」「②**パディング**」「③**ボーダー**」「④**マージン**」という構造になっており、これを**ボックスモデル**と呼びます。

ブラウザの**開発者ツール**を使えば簡単に確認することができ、1度はご覧になられた経験がある方も多いのではないでしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/0fbd9f31-61dc-cf6d-7e37-be9a8d507e98.png)


実は、デフォルトでは、**width:100px** と指定した場合、**①コンテンツのボックス幅**が 100px になります。そこに、②パディングのボックス幅や③ボーダーのボックス幅が乗っかってきて、その合計値が**最終的な**その要素の幅となります。

これは「width:100px と指定しても、最終的な要素の幅は 100px を**超える**場合がある」という事を意味します。 

今回のケースでは、子要素1つ分の最終的な幅は 250px + 2px（左右の border）で 252px となります。子要素を横に2つ並べるならば 504px となり、親要素の 500px を超えるためカラム落ちが発生します。

## どうすれば良いか？

今回のケースでカラム落ちを解消する方法としてパッと思いつくのは、**子要素の width を 248px に変更する**ことです。250px から ボーダーの 2px を引き算するわけです。

たしかに、この方法でカラム落ちは解消できます。
ですが、padding や border の値を変更する度に、それに連動して、width 値を（計算して）再設定しないとカラム落ちが発生してしまいます。

## box-sizing で解決！

こういった場合に便利なのが **box-sizing** というプロパティです。

https://developer.mozilla.org/ja/docs/Web/CSS/box-sizing

box-sizing プロパティで、要素の幅（と高さ）を**どこのブロックまでにするのか**を指定することができ、設定値は `content-box` と `border-box` があります。

- **content-box**
  - width（とheight）は①コンテンツのボックスのみを含み、その他のボックスは含まない
  - こちらが**既定値**
- **border-box**
  - width（とheight）は①コンテンツのボックスに加えて、②パディングと③ボーダーのボックスも含む

図で表現すると次のようになります。

<details><summary>content-box</summary>

![content-box.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/210b92db-27c4-0aa4-48cb-261af2f5dd5a.png)
</details>

<details><summary>border-box</summary>

![border-box.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/2943990a-f9fc-a7cb-50b0-867de23260a8.png)
</details>

今回作成した CSS ファイルに `box-sizing: border-box` を指定すると、「①コンテンツのボックス幅」＋「②パディングのボックス幅＋③ボーダーのボックス幅」が250pxになるように「①コンテンツのボックス幅」が**自動的に縮小**されるようになります。
結果、カラム落ちが解消されます。

```css
.item {
  width: 250px;
  height: 50px;
  background-color: bisque;
  border: 1px solid brown;
  box-sizing: border-box; /* ここを追加 */
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/142dbb84-bb21-c629-88af-5f24e19e3283.png)

このようにしておけば、border が 2px, 3px, ... と変化したとしても、①コンテンツのボックス幅が自動的に縮小されていくので、自分で計算して width 値を再設定する必要がなくなります。

## 参考にした記事

https://qiita.com/ritukiii/items/8da5d23afd84b1385285

## さいごに

業務でも box-sizing プロパティを見かけたことはあったのですが、深くは気にしたことがなくスルーしていたように思います。
分からないことはその場で調べるクセを付けていきたいものですね。