【図解】ちゃんと理解するフレックスボックス【CSS】

## はじめに
### CSSによるページレイアウト
Webページのレイアウトを作るとき、要素の位置を操作するCSSはいくつかあります。

代表的なものとしては下記です。

- 通常フロー
- フレックスボックス
- グリッド
- フロート（ほとんど使わない）
- 位置指定（position）
- 表レイアウト(table)
など

### 通常フロー
CSSが何も適用していない場合、Webページ上の要素はもともとHTMLが持つルールで表示されます。

ブロックレベル要素のコンテンツは、親要素の幅の100%で、高さがコンテンツに準じます。

インライン要素は、コンテンツと同じ幅・高さを持ちます。

視覚的にみると以下のような感じです。

![sample01.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample01.png)

```html
<div class="container">
    <div>ブロック要素です。</div>
    <span>インライン要素です。</span>
</div>
```

```css
.container{
    height: 200px;
    background-color: #ccc;
    color: #fff;
}
.container div{
    background-color: green;
}

.container span{
    background-color: blue;
}
```

インライン要素のサイズを変更したい場合は、`display: block;` を使用してブロックレベル要素に変更する必要があります。
※`display: inline-block;`というのもあります。

しかし、これだけでレイアウトを表現するには限界があります。

そこで、displayプロパティに特別な値を指定することで、より複雑なレイアウトを実現することが可能になります。

特別な値の中でも重要なのが`display:flex`と`display:grid`の2つです。

今回は、`display:flex;`についてまとめようと思います。

## フレックスボックス
フレックスボックスは、**要素を行もしくは列の1次元にレイアウトするための方法**です。

1次元とは「行」または「列」のことです。どちらかひとつしか扱うことができません。

フレックスボックスを使うには、`display:flex;`をレイアウトしたい親要素に適用します。

こうすることで、その直下の子要素はすべて`flex-item`というものになります。

フレックスボックスは、要素を行もしくは列の1次元で扱うと書きましたが、デフォルトでは行として表示される（`flex-direction:row;`）ので、横並びになります。

※`flex-item`や`flex-direction:row;`については後述

![sample02.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample02.png)

```html
<div class="wrapper">
    <div>1つめのブロック要素</div>
    <div>2つめのブロック要素</div>
    <div>3つめのブロック要素</div>
</div>
```

```css
.wrapper{
    display: flex;
}

.wrapper div{
    height: 100px;
    border-radius: 10px;
    border: green 1px solid;
    background-color: lightgreen;
}
```

### フレックスボックスの構造
フレックスボックスを理解するには、構造を知ることが欠かせません。

下記が構造図です。

![flex_terms](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox/flex_terms.png "flex_terms")

引用 : MDN Web Docs -フレックスボックス[https://developer.mozilla.org/ja/docs/Learn/CSS/CSS_layout/Flexbox](https://developer.mozilla.org/ja/docs/Learn/CSS/CSS_layout/Flexbox)

**大事なフレックスボックスの構成要素**
- **flex-container**：flex-itemを囲む親要素です。`display:flex;`を指定した要素。
- **flex-item**：flex-containerの直下の子要素です。行か列の1次元でレイアウトされます。
- **主軸(main-axis)**：flex-itemが配置される方向に走る軸です。
- **主始点(main-start)**：主軸の始点です。
- **主終点(main-end)**：主軸の終点です。
- **交差軸(cross-axis)**：flex-itemが配置される方向に対して垂直に走る軸です。
- **交差始点(cross-start)**：主軸の始点です。
- **交差終点(cross-end)**：主軸の終点です。
- **main-size**：主軸からみたときの、flex-itemの（横）幅です。

フレックスボックスでは上記の構成要素を基準として、CSSプロパティを適用していくことでレイアウトを柔軟に変更することができるのです。

## 主軸(main-axis)の方向を指定する : `flex-direction`
`flex-direction`というプロパティを使うと、主軸(main-axis)が走る方向を指定できます。

デフォルトだと、`row`に設定されているため、行並び（横並び）となります。

列並び（縦並び）にするには`column`を指定します。

![sample03.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample03.png)

```html
<div class="wrapper">
    <div>1つめのブロック要素</div>
    <div>2つめのブロック要素</div>
    <div>3つめのブロック要素</div>
</div>
```

```css
.wrapper{
    display: flex;
    flex-direction: column;/*追記*/
}
.wrapper div{
    height: 100px;
    border-radius: 10px;
    border: green 1px solid;
    background-color: lightgreen;
}
```

なお、フレックスボックスは横並びにさせるCSSという認識がある人は、この時点で新しい発見に気づいているとおもいます。

<span style="color: #d61b09">**フレックスボックスはデフォルト値が横並びなだけで、決して横並びにさせるためだけのCSSではありません。**</span>

<span style="color: #d61b09">**あくまで、行か列の1次元におけるレイアウトを表現するためのプロパティなのです。**</span>

`flex-direction`には、他にも`flex-direction: row-reverse;`や`flex-direction: column-reverse;`があります。

参考リンクは[こちら](https://developer.mozilla.org/ja/docs/Web/CSS/flex-direction)です。

## 主軸(main-axis)に沿ったflex-itemの配置方法 : `justify-content`
`flex-direction`で主軸(main-axis)の方向を決めることができたので、次は`flex-item`の配置方法を指定します。

`flex-item`が主軸上のどこに配置されるかを指定するには`justify-content`を使います。

デフォルト値は`flex-start`となっていて、主軸の始点（`main start`）に配置されます。

逆に、主軸の終点(`main end`)に配置するには`flex-end`を使います。

![sample04.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample04.png)

```css
.wrapper{
    display: flex;
    justify-content: flex-end;
}
```

`justify-content`には、他にも`justify-content: space-between;`や`justify-content: space-around;`、`justify-content: space-evenly;`などがあります。

参考リンクは[こちら](https://developer.mozilla.org/ja/docs/Web/CSS/justify-content)です。

## 交差軸(cross-axis)に沿ったflex-itemの配置方法 : `aligin-items`
`flex-item`が交差軸上のどこに配置されるかを指定するには`aligin-items`を使います。

デフォルト値は、`stretch`です。

これは、すべての`flex-items`を親要素の高さに合わせて交差軸の方向に引き伸ばします。

なお、親要素が交差軸方向に固定幅（高さ）を持っていない場合、すべての`flex-item`の高さは、1番高い`flex-item`と同じになります。

例えば下記のようなフレックスボックスがあります。
子要素には何も高さを指定していないのに、親の高さ300pxに`flex-item`が引き伸ばされていることがわかります。

![sample05.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample05.png)


```html
<div class="wrapper">
    <div>1つめのブロック要素</div>
    <div>2つめのブロック要素</div>
    <div>3つめのブロック要素</div>
</div>
```

```css
.wrapper{
    display: flex;
    height: 300px;
}
.wrapper div{
    border-radius: 10px;
    border: green 1px solid;
    background-color: lightgreen;
}
```

次に、交差軸の中央に配置するプロパティを見てみます。

中央配置するには、`aligin-items:center;`を指定します。

先程のCSSに追加してみます。

```css
.wrapper{
    display: flex;
    height: 300px;
    align-items: center;/*追記*/
}
```

結果は以下のようになります。strechが指定されていないので、flex-itemの引き伸ばしがなくなったのと、交差軸に対して中央に配置されているのがわかります。

![sample06.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample06.png)

`align-items`には、他にも`align-items: start;`や`align-items: end;`、があります。

参考リンクは[こちら](https://developer.mozilla.org/ja/docs/Web/CSS/align-items)です。

## flex-itemのサイズ変更 : `flex-grow / flex-shrink / flex-basis`
ここはちょっと難しいかもしれません。公式ドキュメントや記事下部の参考記事なども併せて確認してください。

flex-itemのサイズを変更するには以下3つのプロパティを使います。

- flex-grow
- flex-shrink
- flex-basis

### flex-basis
順番が前後しますが、flex-growとflex-shrinkのまえにflex-basisから理解します。

`flex-basis`は`flex-item`の<span style="color: #d61b09">**初期の幅**</span>（`main-size`）を指定するプロパティです。

`flex-basis`のデフォルト値は`auto`になっているので、それぞれの幅はコンテンツに応じます。`display:inline-bliock`に似ています。

`display:flex;`を指定すると、子要素がぺちゃんと潰れてしまうのはこれが原因です。

![sample07.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample07.png)

```html
<div class="wrapper">
    <div class="item01">1つめのブロック要素</div>
    <div class="item02">2つめのブロック要素</div>
    <div class="item03">3つめのブロック要素</div>
</div>
```

```css
.wrapper{
    display: flex;
    height: 100px;
}

.wrapper div{
    border-radius: 10px;
    background-color: lightgreen;
}
```

次に`.item01`の`flex-basis`を400pxに変更します。

`.item01`の幅が400pxになって広がったのがわかります。

![sample08.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample08.png)


```css
.wrapper .item01{
    flex-basis: 400px;
}
```

だけど、ここで大事なのが、flex-basisはあくまで<span style="color: #d61b09">**初期の幅**</span>ということです。

初期？（ここが結構難しい）

ためしに親要素の幅を縮めてみます。

![sample09.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample09.png)


`.item01`の幅が400px以下になってしまいました。

フレックスボックスは親要素（フレックスコンテナ）の大きさを基準として、子要素であるflex-itemがうまく収まるように

**「きみはこれだけ幅を取っていいよ」**

と計算するようになっています。

そのとき、参考にするのが`flex-basis`です。

`flex-item`がそれぞれ

**「ぼくはこれだけの幅が欲しいです！」**

と主張を`flex-basis`として形にするわけです。

例えば上記の例で言えば、

`.item01`は`flex-basis:400px;`なので

**「ぼくはできれば400pxは幅が欲しいです！」**

と主張していて、残りの`.item02`と`.item03`は`flex-basis:auto;`なので

**「ぼくらはまぁ、それなりの幅があればいいです」**

と主張しています。
<span style="color: #d61b09">**※あくまでイメージです。**</span>

それを聞いた、親要素のフレックスコンテナが

<span style="color: #d61b09">**「じゃあ、`.item01`には400pxあげて、そのほかの子たちにはコンテンツ分の幅だけあげよう」**</span>

というふうになるわけです。

これが親要素の幅に余裕があるときはいいのですが、flex-itemたちの主張する幅の合計が親要素の幅を超えてしまったときはどうでしょうか？

イメージとしては以下のような感じになります。

<span style="color: #d61b09">**「幅が足りんから、`.item01`に400pxもあげられんけど、他の子たちよりは多めに幅やるわ。」**</span>

となるわけです。
<span style="color: #d61b09">**※あくまでイメージです。**</span>

### flex-grow
`flex-grow`は親要素の空間が余った場合の、子要素達の伸び具合を調整するプロパティです。

`flex-grow`のデフォルト値は0です。

例えば、下記のようなフレックスボックスの場合、紫色のところが親要素の余った空間になっています。

![sample07.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample07.png)

```html
<div class="wrapper">
    <div class="item01">1つめのブロック要素</div>
    <div class="item02">2つめのブロック要素</div>
    <div class="item03">3つめのブロック要素</div>
</div>
```

```css
.wrapper{
    display: flex;
    height: 100px;
}
.wrapper div{
    border-radius: 10px;
    background-color: lightgreen;
}
```

この余った空間をなくしたいときがあります。

そんなときは`.item03`に`flex-grow`を指定します。

```css
.wrapper .item03{
    flex-grow: 1;
}
```

![sample10.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample10.png)

`.item03`が伸びて余った空間がなくなりました。

これは親要素の余った空間を、子要素たちで分けるときにどのくらいの割合にするかと指定したからです。

つまり、何も指定しなければ、.item01とitem02と.item03の余った空間の割合は**0:0:0**になるわけです。（`flex-grow`のデフォルト値は0でした）

しかし、.item03に`flex-grow: 1;`を指定したことで、割合が**0:0:1**になったので、.item03に余った空間をぜんぶ分け与えることになったのです。

## flex-shrink
`flex-shrink`は親要素の空間が足りない場合の、子要素達の縮み具合を調整するプロパティです。

`flex-shrink`のデフォルト値は1です。

例えば、下記のように`flex-basis`を400pxに指定した.item01という`flex-item`があるとします。

![sample08.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample08.png)

```css
.wrapper .item01{
    flex-basis:400px;
}
```

親の空間に余裕がある場合は、400pxまるまる確保されますが、、、

あくまでflex-basisは初期の幅なので、親要素の空間に余裕がなくなれば

![sample09.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample09.png)

400px以下になってしまいます。

ここまでは、flex-basisのときに説明しました。

だけど、時には縮んでほしくない場合もあるでしょう。
そんな時には、その要素に`flex-shrink:0;`を指定します。

```css
.wrapper .item01{
    flex-basis:400px;
    flex-shrink: 0;
}
```

![sample11.png](https://raw.githubusercontent.com/Naughty1029/TIL/main/Images/CSS/フレックスボックスについて/sample11.png)


こんな感じで、400pxが保たれたままになります。

## おわりに
とりあえず、ひととおりフレックスボックスについて理解しておきたいことをまとめてみました。

このほかにも、`wrapプロパティ`など、コーディングに大事なプロパティがいくつかありますが、ここに書いたことが前提知識としてあれば理解に苦しむことも少ないかなと思っています。

また、改めてフレックスボックスについてまとめてみたのは、個人的にグリッドレイアウトをちゃんと理解したいなと思ったからです。

フレックスボックスとグリッドレイアウトの違いをしっかり理解することで、グリッドの学習も捗るかなと考えた次第です。

## 参考記事
- [CSS レイアウト入門](https://developer.mozilla.org/ja/docs/Learn/CSS/CSS_layout/Introduction)
- [フレックスボックス](https://developer.mozilla.org/ja/docs/Learn/CSS/CSS_layout/Flexbox)
- [6歳娘「パパ、flexにしたら幅が余っちゃったよ・・・」](https://qiita.com/Yametaro/items/a04032349c46dddf8cda)
- [主軸に沿ったフレックスアイテムの比率の制御](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Controlling_Ratios_of_Flex_Items_Along_the_Main_Ax)