CSSの中央寄せはFlexbox万能説

# CSSの中央寄せはややこしい

CSSの縦横センタリングについて調べると、

* `text-align: center;` で中央揃えする
  * ただし目的の要素をinline要素にする必要がある
* `margin: auto;` で中央寄せをする
  * ただし目的の要素の大きさを指定する必要がある
* `line-height` を親要素の `height` と同じにして縦方向の中央寄せをする
  * ただし親要素の高さを指定する必要がある
* `position: absolute;` で50%指定して `transform` で中央にずらす
  * ただし色々する必要がある

など様々な方法が紹介されているものの、それぞれ条件があり使い分けが面倒。
そんなわずらわしさを解消してくれる（気がする）のが、本記事で紹介する **CSS Flexible Box Layout** による中央寄せである。

# 中央寄せにはFlexboxを使う

以下のCSSを中央寄せしたい要素の**親要素**に指定する。
コンテナをFlexboxにし、水平方向と垂直方向に対し子要素を中央に持ってくるように指定している。

```css:css
.parent {
    display: flex;            /* Flexboxを指定 */
    justify-content: center;  /* 水平方向の中央寄せ */
    align-items: center;      /* 垂直方向の中央寄せ */
}
```

だいたいこれをすれば条件などを気にすることなく素直に中央に寄ってくれる。
いちいち3行書くのも面倒なので、SCSSを導入して関数にしてあげれば1行で指定できて楽。

```scss:scss
@mixin flexCentering($justify: center, $align: center) {
    display: flex;
    justify-content: $justify;
    align-items: $align;
}

.parent {
    @include flexCentering();
}
```

# 基本的な中央寄せの例

とりあえず例に使う要素とスタイルは以下のようにする。

```html:html
<div class="parent">
    <div class="child">袋井宿</div>
</div>
```

```css:css
.parent {
    width: 300px;
    height: 200px;
    background: lightblue;
}
.child {
    margin: 10px;
    padding: 10px;
    background: lightgreen;
}
```

## ど真ん中に中央寄せ

先ほどのFlexboxのCSSを親要素に指定する。

```css:css
.parent {
    display: flex;
    justify-content: center;
    align-items: center;
}
```
![Screen Shot 2020-02-07 at 06.13.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/58493618-bfa3-2440-fdc2-8f3c9f5b7f92.png)
これが中央寄せのベースとなる。

## 横方向の中央寄せ

垂直方向の配置を指定する `align-items` に `flex-start` を指定する。

```css:css
.parent {
    display: flex;
    justify-content: center;
    align-items: flex-start;  /* 垂直方向は上に寄せる */
}
```
![Screen Shot 2020-02-07 at 06.17.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/73863594-793f-c6f4-ef42-683f3c842ef4.png)
`align-items` に `flex-end` を指定すれば下に行く。
![Screen Shot 2020-02-07 at 06.25.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/e5ebc6ed-e541-b7dc-d853-ee781cf08bb9.png)

## 縦方向の中央寄せ

水平方向の配置を指定する `justify-content` に `flex-start` を指定する。

```css:css
.parent {
    display: flex;
    justify-content: flex-start;  /* 水平方向は左に寄せる */
    align-items: center;
}
```
![Screen Shot 2020-02-07 at 06.26.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/b14b8ba3-6afc-f499-d114-1c441ff2839c.png)
`justify-content` に `flex-end` を指定すれば右に行く。
![Screen Shot 2020-02-07 at 06.29.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/c3895f2a-0b94-3136-efbb-2c328a24234d.png)

# 応用的な中央寄せの例

## 丸の真ん中に文字

大きさのない要素を `border` と `border-radius` を使い円弧で囲うことによって丸形を作り、その中に入れる文字を中央寄せする。
`white-space: nowrap;` を指定すると狭い幅でも文字列が折り返しされない。

```html:html
<div class="parent">袋井宿</div>
```

```css:css
.parent {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 0;
    height: 0;
    border: 50px solid lightblue;
    border-radius: 50px;
    white-space: nowrap;
}
```

![Screen Shot 2020-02-08 at 11.53.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/c55f9429-aa98-3723-3fa2-718adc759477.png)


## 複数要素の中央寄せ

Flexboxなので複数要素でもラッパーをつけたりせず中央寄せできる。

```html:html
<div class="parent">
    <div class="child">掛川宿</div>
    <div class="child">袋井宿</div>
    <div class="child">見附宿</div>
</div>
```

```css:css
.parent {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 300px;
    height: 200px;
    background: lightblue;
}
.child {
    margin: 10px;
    padding: 10px;
    background: lightgreen;
}
```

![Screen Shot 2020-02-07 at 06.35.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/41844dbb-0500-abc5-e137-01f304a98969.png)
縦方向に並べて中央寄せしたい場合は、新たに `flex-direction` プロパティを追加して `colmun` （縦方向）を指定してあげればOK。

ただし、このプロパティを指定することによって要素の配列方向が変わるため `justify-content` と `align-items` の役割は**逆になる**ことに注意。
そもそも、本来 `justify-content` は**主軸方向**、`align-items` は**交差軸方向**の配置を指定するプロパティなので、言っておいてなんだが「水平」や「垂直」と呼ぶのはあまり良くない。デフォルトでの `flex-direction` の値は `row` （横方向）である。
深掘りはもはやFlexboxの説明になってしまうので割愛。

```css:css
.parent {
    display: flex;
    justify-content: center; /* 主軸方向に中央寄せ */
    align-items: center;     /* 交差軸方向に中央寄せ */
    flex-direction: column;  /* 縦方向に並べる */
}
```

![Screen Shot 2020-02-07 at 06.42.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/b453774e-98da-1ca2-6481-6ec8e2eca994.png)
蛇足だが、`flex-direction` プロパティに `row-reverse` を指定してあげると逆順になる。
東海道の起点は日本橋なので掛川宿は袋井宿の東にある。
![Screen Shot 2020-02-07 at 06.48.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/ccad9fb3-164d-d10c-78fe-5192d801e5ea.png)

# メリットとデメリット

他の中央寄せの方法と違って**親要素や子要素の大きさを指定するすることなく中央寄せができる**ところがメリット。
たとえば、CSS Gridの各要素は基本的にセルごとにストレッチされるため、大きさを直接指定することがない。よってこの中で中央寄せをしたいとなると、子要素の大きさを指定して `margin: auto;` を指定することになるだろうが、その子要素が文字列で可変なので大きさを指定するのはなんかやだ！みたいなときに使える。

```html:html
<div class="container">
    <div class="parent">
        <div class="child">掛川宿</div>
    </div>
    <div class="parent">
        <div class="child">袋井宿</div>
    </div>
    <div class="parent">
        <div class="child">見附宿</div>
    </div>
</div>
```

```css:css
.container {
    display: grid;
    grid-template-rows: 1fr 1fr;
    grid-template-columns: 1fr 1fr;
    row-gap: 10px;
    column-gap: 10px;
    width: 300px;
    height: 200px;
    background: lightgray;
}
.parent {
    display: flex;
    justify-content: center;
    align-items: center;
    background: lightblue;
}
.child {
    margin: 10px;
    padding: 10px;
    background: lightgreen;
}
```

![Screen Shot 2020-02-10 at 16.47.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/81563271-0872-8c92-f2f7-e65775ec299e.png)
また、基本的なFlexboxの機能は現行のブラウザではすべて `-ms-` や `-webkit-` などの**ベンダープレフィクスをつけることなく使用できる**。接頭辞をつければ全ユーザの99%、つけなくても98%をサポートする。
ただし、IE9以下などの古いバージョンのブラウザは対応していないことが多いため、そのあたりを考慮に入れるなら他の方法をとるしかない。そもそもIEはIE11でもバグが多く部分的なサポートのみであるようなので要確認。
また「基本的な」Flexboxの機能といったように、[複数要素の中央寄せ](#複数要素の中央寄せ)で出てきた `flex-direction` などは別枠である。

詳細は以下のリンクからどうぞ。
[Can I use... Support tables for HTML5, CSS3, etc](https://caniuse.com/#search=CSS%20Flexible%20Box%20Layout%20Module)

なお、中央寄せといっても以下のような**複数行の中央揃えはできない**ので、素直に `text-align: center;` するしかない。というかこっちのほうが楽。

```html:html
<div class="parent">
    中央揃えは<br>
    text-align: center;を<br>
    やるしかない
</div>
```

```css:css
.parent {
    width: 300px;
    height: 200px;
    background: lightblue;
    text-align: center;
}
```

![Screen Shot 2020-02-10 at 17.03.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/415493/4269bb3d-be9b-7371-2b2e-7116a3604953.png)
また、値を `center` から `flex-start` へ変えるなどの変化にアニメーションは適用されないようなので注意が必要。ここは `margin` の操作で代用できる。