【CSS】フレックスボックスレイアウトの私的解釈

個人的にいつもフレックスコンテナの `justify-content` や `align-items` や `align-content` を試行錯誤的に記述してしまっていて、ほしい見た目にストレートフォワードに記述することができていなかったので、これらのプロパティを学んで自分なりに解釈しました。
私のようにフレックスボックスを何となく使っている人に参考になればという記事です。

:::note warn
この記事は、こう解釈すると CSS が書きやすいのではという記事であり、<INS>**実際の描画処理の解説記事ではない**</INS>です。解釈としても不正確な記述もあるかもしれません。取り上げているフレックスボックスのプロパティや値も網羅的ではありません。<br/><font color="mediumvioletred"> [2025/02/25] 一部不正確な記述がありました ( 交差軸方向に余白がない状態だと align-content で交差軸方向に重力をかけてもアイテムは動けないと書いていますが、アイテムにちぢむ余地があれば重力をかけるとちぢみます)。後で修正します。</font>
:::

:::note
[2025/02/25] どのプロパティをどうするとどうなるかのツールを作成しました。
https://cookiebox26.github.io/games/misc/sample-flex-interactive.html
:::

### 具体例に沿った説明

フレックスボックスは下図の 6 ステップの処理をしていると解釈できると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/177688/8d316148-baa1-4b1b-87a0-7f5bb7f9fd22.png)

#### 上図の説明

- 主軸方向が 4 パターンあるので 4 列描いています。主軸方向を「水色→ピンク色」、折り返して改行する方向を「明→暗」で表現しています (折り返しが逆の wrap-reverse は略)。
- 上図で、アイテム 006 だけはサイズを指定していて (`height: 30px; width: 30px;`)、アイテム 012 だけは主軸方向へ可能な限りふくらむよう指定しています (`flex: auto;`)。
- このとき、フレックスボックスは基本的には以下の処理をします (するようにみえます)。
  - **STEP1.** で、とりあえずアイテムを主軸方向 ＆ 折り返し方向に**自然に並べます**。
  - **STEP2.** で、**各アイテムの主軸方向へのふくらませを行います** (ただし、実際にふくらむのは、ふくらむよう指定されているアイテム 012 のみです)。
  - **STEP3.** で、**各アイテムの交差軸方向へのふくらませを行います** (ただ、サイズ指定がある アイテム 006 はふくらませた後にやっぱり元に戻します)。
- さらに、<INS>フレックスコンテナに `justify-content` や `align-items` や `align-content` プロパティが指定されているときは</INS>、以下の処理をします (するようにみえます)。
  - **STEP4.** で、`justify-content` の値にしたがって、**主軸方向に重力を発生させます** (ただ上図では値が `space-between` なので、重力というよりアイテム間斥力を発生させています)。<font color="mediumvioletred">なお、もしすべての行 (列) で主軸方向に余白がなかったら、この重力を発生させても意味がありません。</font>
  - **STEP5.** で、`align-items` の値にしたがって、**STEP3. での交差軸方向へのふくらませを解除し[^1]、行 (列) 内に交差軸方向の重力を発生させます [^2]** (上図では値が `center` なのでアイテムが行 (列) の中央のラインに寄っています)。<font color="mediumvioletred">なお、もしすべてのアイテムがふくらませを解除しても行 (列) 内で交差軸方向に余白ができなかったら、この重力を発生させても意味がありません。</font>
  - **STEP6.** で、`align-content` の値にしたがって、**交差軸方向の重力を発生させます** (上図では値が `end` なのでアイテムが暗い方に寄っています)。<font color="mediumvioletred">なお、もし交差軸方向に余白がなかったら、この重力を発生させても意味がありません。</font>
    - <font color="palevioletred">したがって、アイテムに交差軸方向のサイズを指定して STEP3. のふくらませを解除するか、コンテナに `align-items` を指定して STEP3. のふくらませを解除する必要があります (はず)。</font>

[^1]: ただし、値を `align-items: stretch;` に指定した場合はふくらませは解除されず、重力も発生しません (交差軸方向にふくらみきっているのでどのみち動けない)。もっとも、この値はフレックスコンテナの初期値なので、明示的に指定する必要はありません。

[^2]: アイテムに `align-self` プロパティを設定することでアイテムごとに重力の方向を変えることもできます (後述)。

なお、上図の HTML ファイルは以下です (解釈を説明するため恣意的に記述しています)。
https://cookiebox26.github.io/games/misc/sample-flex.html

### 一般的 (?) な説明

:::note
これ以降の文章による説明では、一般的といっても<INS>コンテナの主軸方向 `flex-direction` がデフォルト値 `row` であるものとします</INS>。`row-reverse`, `column`, `column-reverse` にあてはめる場合は、ところどころ読み替える必要があります。
:::

フレックスボックスは以下の 6 ステップの処理をしていると解釈できると思います。

- **STEP1. コンテナにアイテムたちを自然に並べる**
- **STEP2. アイテムたちの幅をふくらませる**
- **STEP3. アイテムたちの高さをふくらませる**
- **STEP4. コンテナ内に横方向の重力を発生させる (`justify-content`)**
- **STEP5. アイテムを自然な高さに戻し行内で縦方向の重力を発生させる (`align-items`)**
- **STEP6. コンテナ内に縦方向の重力を発生させる (`align-content`)**

#### STEP1. コンテナにアイテムたちを自然に並べる

まずはコンテナの右上端から右に自然に (サイズを伸縮させずに) アイテムたちを並べます。
アイテムがはみ出たときは、コンテナに `flex-wrap: wrap(-reverse);` が指定されていれば折り返します。指定されていなければはみ出します。

#### STEP2. アイテムたちの幅をふくらませる

個々のアイテムの中に `flex, flex-grow, flex-shrink, flex-basis` が指定されているものがあるときは、それにしたがってそのアイテムの横幅をふくらませ (ちぢませ) ます。

#### STEP3. アイテムたちの高さをふくらませる

次に、アイテムたちの高さを同じ行内で一番高いアイテムと同じ高さまでふくらませ、それでもまだコンテナの高さに余白があれば、すべての行がその余白を平等に分け合うようにさらにアイテムたちの高さをふくらませます。
<INS>ただし、高さが明示的に指定されたアイテムについては、いったん高さをふくらませた場合の配置にした後で、指定された高さにちぢめます</INS>。

#### STEP4. コンテナ内に横方向の重力を発生させる (`justify-content`)

もしコンテナに `justify-content` が指定されていたら、コンテナ内に横方向の重力を発生させてアイテムたちを移動します。`start` ならコンテナ左端への重力 [^3]、`end` ならコンテナ右端への重力、`center` ならコンテナ中央への重力、`space-between` ならアイテム間に斥力を発生させます。
<font color="mediumvioletred">※ すでにどの行もコンテナの幅に余白がないようなときは何も起きません。</font>

[^3]: `start` でも `flex-start` でもコンテナ左端への重力になります。`flex-direction` が `row-reverse` のときは後者 `flex-start` はコンテナ右端への重力になります。

#### STEP5. アイテムを自然な高さに戻し行内で縦方向の重力を発生させる (`align-items`)

もしコンテナに `align-items` が指定されていたら、高さをふくらませていたアイテムたちを行内で元の高さにちぢめると共に、`start` なら行の上端に向かって、`end` なら行の下端に向かって、`center` なら行の中央に向かって重力を発生させアイテムたちを移動します。ただし、個々のアイテムに `align-self` が指定されたものがあるときは、そのアイテムにだけはその値にしたがった重力を作用させます。
<font color="mediumvioletred">※ アイテムを自然な高さに戻しても行の高さに余白ができないときは何も起きません。</font>

#### STEP6. コンテナ内に縦方向の重力を発生させる (`align-content`)

さらにコンテナに `align-content` が指定されていたら、コンテナ内に縦方向の重力を作用させてアイテムたちを<INS>行単位で</INS>移動します。`start` ならコンテナ上端への重力、`end` ならコンテナ下端への重力、`center` ならコンテナ中央への重力、`space-between` なら行間に斥力を発生させます。
<font color="mediumvioletred">※ すでにコンテナの高さに余白がないようなときは何も起きません。</font>

### まとめ

フレックスボックスは 6 ステップの処理をしていると解釈すると、意味がないプロパティの指定を回避し (Ex. 余白がない方向に重力を発生させても移動できないので意味がない)、ほしい見た目に必要十分な CSS の記述をできる気がします。

というか本来は、STEP3. まででアイテムを自動的にふくらませきってもらって、人間は配置について何も考えなくてよいというのがフレックスボックスレイアウトの真価だと思うので (?)、「アイテムをふくらませずにこのサイズにしなきゃやーやーなの」とする時点でそれが台無し (?) だと思いますが、そうしてできた余白をどの方向に詰めたいのかに基づいて重力を発生させようと考えるとストレートフォワードに CSS を書ける気がします。

### 参考文献

下記のページ及びリンク先の各プロパティのページを参考にしています。

https://developer.mozilla.org/ja/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox