Flex MessageをCSS Flexboxと対応付けながら学ぶ

この記事は [LINE DC Advent Calendar 2021](https://qiita.com/advent-calendar/2021/linedc) 18日目の記事です。

# はじめに

[Messaging APIのドキュメントによると](https://developers.line.biz/ja/docs/messaging-api/message-types/#flex-messages)、Flex Messageは、[CSS Flexible Box（CSS Flexbox）](https://www.w3.org/TR/css-flexbox-1/)の基礎知識を使って、レイアウトを自由にカスタマイズできるメッセージです。

Flex Messageを使って自由なレイアウトのメッセージを作りたいと思ったとき、ドキュメントには分かりやすいチュートリアルや豊富なサンプル、[Flex Message Simulator](https://developers.line.biz/flex-simulator/)という便利なシミュレータが用意されているので手軽にリッチなメッセージを作成できるようになっています。
一方で、サンプルに無いレイアウトを検討するときにはCSS Flexboxの知識が無いと思い通りのレイアウトが実現できず苦労します。


そこで、本記事ではFlex Message Simulatorを使いながらMDN Web Docの[**フレックスボックスの基本概念**](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox)に対応する形でFlex Messageの対応するプロパティを紹介し、Flex Messageを理解する助けになることを目指します。

# [フレックスボックス 2 つの軸](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox#%E3%83%95%E3%83%AC%E3%83%83%E3%82%AF%E3%82%B9%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9_2_%E3%81%A4%E3%81%AE%E8%BB%B8)
フレックスボックスには `flex-direction` プロパティによって定義される **主軸** と、主軸と垂直に交わる **交差軸** と呼ばれる2つの軸があります。
Flex Messageではボックスの `layout` プロパティがこれに該当します。

`flex-direction` には

- row
- row-reverse
- column
- column-reverse

の4種類の値が存在しますが、Flex Messageの `layout` プロパティでは

- horizontal
- vertical
- baseline

の3種類の値をとります。この3種類のうち、 `baseline` はやや特殊なのでここでの説明は割愛します。

`horizontal` は フレックスボックスの `row` に該当し、主軸の向きは水平方向です。

例えば、以下の構成でボックスとコンポーネントを配置した場合、

```
bubble
├── header
├── hero
├── body
│   └── box[horizontal]
│       ├── box[vertical]
│       │   └── text[hello, world]
│       ├── box2[vertical]
│       │   └── text[hello, world]
│       └── box3[vertical]
│           └── text[hello, world]
└── footer
```

表示は

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/7ebe46c6-122e-81a1-9078-006827641f28.png)

のようになります。

<details><summary>上記例のJSON</summary><div>

```json
{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "horizontal",
    "contents": [
      {
        "type": "box",
        "layout": "vertical",
        "contents": [
          {
            "type": "text",
            "text": "hello, world"
          }
        ],
        "backgroundColor": "#ecd6c7"
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [
          {
            "type": "text",
            "text": "hello, world"
          }
        ],
        "backgroundColor": "#e79686"
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [
          {
            "type": "text",
            "text": "hello, world"
          }
        ],
        "backgroundColor": "#a39391"
      }
    ],
    "height": "300px"
  }
}
```

</div></details>


一方で`vertical` は フレックスボックスの `column` に該当し、主軸の向きは垂直方向です。

先ほどの例の、body直下にあるボックスの `layout` プロパティを `vertical` に変更すると

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/d3b73440-9f05-deea-edb9-9b881c5f0ba1.png)

のように表示されます。


# [行の先頭と末尾](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox#%E8%A1%8C%E3%81%AE%E5%85%88%E9%A0%AD%E3%81%A8%E6%9C%AB%E5%B0%BE)

フレックスボックスでは `flex-direction` が `row` のとき、書字方向によって主軸の先頭が右側に来ることがあるようです。
Flex Messageでは書字方向が自動で識別されることは無いですが、 `bubble` の `direction` プロパティによって制御することが出来ます。

`direction` プロパティを `rtl` に設定すると以下のように表示されます。
（雰囲気を出すために **hello, world** をGoogle翻訳でアラビア語にしてみましたが、書字方向が正しいのかは判断できません。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/28963463-48aa-b90a-a05c-9aed3a27fe9f.png)


<details><summary>上記例のJSON</summary><div>

```json
{
  "type": "bubble",
  "direction": "rtl",
  "body": {
    "type": "box",
    "layout": "horizontal",
    "contents": [
      {
        "type": "box",
        "layout": "vertical",
        "contents": [
          {
            "type": "text",
            "text": "مرحبا بالعالم"
          }
        ],
        "backgroundColor": "#ecd6c7"
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [
          {
            "type": "text",
            "text": "مرحبا بالعالم"
          }
        ],
        "backgroundColor": "#e79686"
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [
          {
            "type": "text",
            "text": "مرحبا بالعالم"
          }
        ],
        "backgroundColor": "#a39391"
      }
    ],
    "height": "300px"
  }
}
```

</div></details>


# [フレックスコンテナー](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox#%E3%83%95%E3%83%AC%E3%83%83%E3%82%AF%E3%82%B9%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%83%BC)

フレックスボックスではレイアウトされる文書の領域を **フレックスコンテナー** 、その直下に配置される子要素を **フレックスアイテム** と呼びます。
これらの要素はFlex Messageではそれぞれ **ボックス** と **コンポーネント** に該当します。

MDN Web Doc ではフレックスアイテムの振る舞いを決定するフレックスコンテナーのプロパティとして `flex-direction` プロパティと `flex-wrap` プロパティ、2つのプロパティを一括指定出来る `flex-flow` プロパティが解説されていますが、Flex Messageにはそれに該当するプロパティは存在せず、

- `flex-direction` : `layout` プロパティに応じて決定（`reverse` は存在しない）
- `flex-wrap` : `nowrap` に固定

となっています。
したがって、Flex Messageでコンポーネントがボックスに収まらない場合には以下のように折り返されずにあふれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/9dd0738c-b8e1-c010-f46e-a61c1a27c25b.png)

<details><summary>上記例のJSON</summary><div>

```json
{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "horizontal",
    "contents": [
      {
        "type": "box",
        "layout": "vertical",
        "contents": [],
        "width": "50px",
        "height": "50px",
        "backgroundColor": "#ecd6c7"
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [],
        "width": "50px",
        "height": "50px",
        "backgroundColor": "#e79686"
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [],
        "width": "50px",
        "height": "50px",
        "backgroundColor": "#a39391"
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [],
        "width": "50px",
        "height": "50px",
        "backgroundColor": "#ecd6c7"
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [],
        "width": "50px",
        "height": "50px",
        "backgroundColor": "#e79686"
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [],
        "width": "50px",
        "height": "50px",
        "backgroundColor": "#a39391"
      }
    ]
  }
}
```

</div></details>


# [フレックスアイテムに適用されるプロパティ](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox#%E3%83%95%E3%83%AC%E3%83%83%E3%82%AF%E3%82%B9%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0%E3%81%AB%E9%81%A9%E7%94%A8%E3%81%95%E3%82%8C%E3%82%8B%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3)

フレックスボックスではアイテムの幅や高さを制御するために `flex-grow`、`flex-shrink`、`flex-basis`とこれらを一括指定出来る `flex` プロパティが用意されています。Flex Messageでこれに該当するプロパティは `flex` となっており、

- `flex` プロパティに `0` を指定 ： フレックスボックスの `flex: 0 0 auto;` と同等のレイアウト
- `flex` プロパティに `0` 以上を指定 ： フレックスボックスの `flex: X 0 0;` と同等のレイアウト（Xは `flex` プロパティで指定した値） 

のように対応しています。
また、 `flex` プロパティのデフォルト値はボックスの `layout` プロパティによって異なっており、

- `layout` プロパティに `horizontal` を指定 : `flex` プロパティのデフォルト値は `1`
- `layout` プロパティに `vertical` を指定 : `flex` プロパティのデフォルト値は `0`

となっています。

**フレックスボックス 2 つの軸** で `layout` プロパティの違いを紹介した際に、主軸が水平方向の際には3つのコンポーネントが余白を均等に分配していたのに対し、主軸が垂直方向の際にはコンポーネントの高さは文字の高さとなっており、余白を残していました。
この違いは上記の `flex` プロパティのデフォルト値の違いからきており、 `layout` プロパティを `vertical` に指定した場合も `flex` プロパティを `1` に指定すると同様のレイアウトにすることが出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/a16b0f25-455a-c202-7ff4-513579c3243a.png)


<details><summary>上記例のJSON</summary><div>

```json
{
  "type": "bubble",
  "body": {
    "type": "box",
    "layout": "vertical",
    "contents": [
      {
        "type": "box",
        "layout": "vertical",
        "contents": [
          {
            "type": "text",
            "text": "hello, world"
          }
        ],
        "backgroundColor": "#ecd6c7",
        "flex": 1
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [
          {
            "type": "text",
            "text": "hello, world"
          }
        ],
        "backgroundColor": "#e79686",
        "flex": 1
      },
      {
        "type": "box",
        "layout": "vertical",
        "contents": [
          {
            "type": "text",
            "text": "hello, world"
          }
        ],
        "backgroundColor": "#a39391",
        "flex": 1
      }
    ],
    "height": "300px"
  }
}
```

</div></details>


# [アイテム間での位置合わせ、端揃え、余白分配](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox#%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0%E9%96%93%E3%81%A7%E3%81%AE%E4%BD%8D%E7%BD%AE%E5%90%88%E3%82%8F%E3%81%9B%E3%80%81%E7%AB%AF%E6%8F%83%E3%81%88%E3%80%81%E4%BD%99%E7%99%BD%E5%88%86%E9%85%8D)

フレックスボックスでは主軸および交差軸においての位置合わせや端揃えを `align-items` プロパティや `justify-content` プロパティによって制御します。
Flex Messageでは `alignItems` プロパティと `justifyContent` プロパティがこれに該当します。


## [align-items](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox#align-items)

`align-items` プロパティは交差軸におけるアイテムの位置合わせを行います。
Flex Messageの `alignItems` プロパティとの対応は以下の通りです。

|フレックスボックス|Flex Message|イメージ|
|:-:|:-:|:-:|
|stretch|(デフォルト値)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/7ebe46c6-122e-81a1-9078-006827641f28.png)|
|flex-start|flex-start|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/9da8b2de-0276-2645-b103-f7bbad65d548.png)|
|flex-end|flex-end|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/a6b6865e-49ba-0aa4-b5ad-cd8fb2992d8f.png)|
|center|center|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/45021b7a-abe1-1940-77a8-c7326fb8f894.png)|

## [justify-content](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox#justify-content)

`justify-content` プロパティは主軸におけるアイテムの配置を制御します。Flex Messageでは `justifyContent` プロパティがこれに該当しており、とりうる値の種類は一致しています。

|`justifyContent` プロパティ|イメージ|
|:-:|:-:|:-:|
|flex-start|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/3d761357-fc50-c971-b806-89504ad7fd70.png)|
|flex-end|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/f977d3aa-69a2-3719-9880-b0edeb1b888e.png)|
|center|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/3a5e8e28-284c-e1ea-b9a3-6d37fed9c078.png)|
|space-around|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/f6f4c38d-6b5f-7d9e-d4e6-a6ce9aec4661.png)|
|space-between|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/2a0866a6-3468-da64-fc4d-b33736fc190c.png)|
|space-evenly|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/314315/64c97d7c-1a9e-1a82-6a53-6033c20e5d9f.png)|


# まとめ

本記事ではフレックスボックスとFlex Messageのプロパティを対応付けながら紹介していきました。
対応付けを見ると `flex-direction` プロパティ と `flex-wrap` プロパティ は存在せず、 `flex` プロパティも若干の制限が存在する一方で、十分に自由なレイアウトを作れるだけのプロパティがサポートされていることが分かりました。

世の中には多くのフレックスボックスを活用したレイアウト例が存在しており、それらをFlex Messageのレイアウトに活用する際にこの記事がお役に立てると嬉しく思います。