Figmaのモバイル設計手法

## はじめに

この記事は私の初記事 && [Figma Advent Calendar 2021](https://qiita.com/advent-calendar/2021/figma) の24日目の記事です。

突然ですが皆さん、自分なりのFigmaのレイアウトルールを作ったことはあるでしょうか？Figmaはさまざまな方法で同じレイアウトが実現しているので、明確なルールを作ることはファイル全体に統一感を持たせ、作成する時に無駄な思考をする必要がないので非常に便利です。

今回は、自分なりの設計手法について4日ほど悩みまくって決めました。長いですが最後まで読んでいただけると嬉しいです。

### 対象の人

- Auto layout, Variantsを理解している人
- Figmaでモバイルアプリ画面を作る際に、Auto layoutをノリで適用している人

### 省略します

この記事ではAuto layoutの長い機能名を次のように省略して表記します。

- Spacing between items (要素同士の間隔）→ Spacing between
- Padding around items (要素から親のFrameまでの間隔） → Padding around

## この記事で目指す設計手法

- モバイルのレイアウトに特化
- Figma編集者にとって、構造変更が容易（エンジニアにとってどうかは別問題）
- 操作の手順が少ない
- 曖昧な項目が少なく、シンプル
- レイアウトによって適用するルールを変える必要がない

では、作成手順を見ていきましょう！

## 1. スマホの土台Frameとfix要素を作成

<img width="250px" style="border:solid 5px #333333;" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/bc2d6608-c579-dd44-ad59-3434b6522f10.png">

### 構造

- Frame-1
    - fix 要素1
    - fix 要素2

### 解説

今回作った土台を「Frame-1」と今後呼びます。

Auto layout中にfixは含められないので、Frame-1にはAuto layoutを適用しません。

また、今後要素がはみ出ることを考えてFrame-1のClip content: offとして、はみ出た要素を見えるようにしておきます。

## 2. Auto layoutの入れ物を作成

<img width="250px" style="border:solid 5px #333333;" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/0fd3a2a8-d33c-7d3d-cb3a-0e94d794dd09.png">

### 構造

- Frame-1
    - fix 要素1
    - fix 要素2
    - Frame-2 (Auto layout)

### 解説

Frame-1にAuto layoutを適用できないので、代わりにAuto layoutを適用させるためだけの透明なオブジェクトを作成します。以降、これを「Frame-2」と呼びます。

下記の設定通りに行えば、入れた子要素を横に中央揃えで、上からどんどん敷き詰めるFrame-2ができます。

### Frame-2の詳細

- Width: Fixed（Frame-1と同じWidthにする）
- Height: Hug contents
- direction: vertical
- Alignment
    - Vertical: top
    - Horizontal: center
- Spacing between: 0
- Padding around: 0
- Fill: 背景色

### Frame-2のPadding aroundで左右に余白を作らない理由

仮にPadding around: 0でない場合を考えてみましょう。

下の画像のように、青いボタンのみの場合は、Frame-2にPadding aroundを設定し、青い要素をWidth: Fill conteinerにすれば左右を揃えてくれていい感じです。

次に、黒いブロックようなWidthが大きいものを入れるとどうでしょうか。Frame-2のAlignment: Vertical centerの場合はなぜかすっぽり入ってしまいますが、左右のPadding aroundを無視しているので気持ち悪いし、CSSの構造的にも良くないです。

さらに、緑色の長さが異なるボタンを配置したとします。ここまで左右の余白がずれていると、Frame-2に一定の余白の値を決める意味が無くなってきます。

このようにPadding aroundの左右に余白を作ると問題が起こりやすくなり、画面によってPadding aroundの値を決める必要も出てくるため面倒です。よって、どの要素のサイズにも対応できるPadding around: 0に統一します。

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/b65f4eca-cfcb-e483-aa57-7cc66ac868e8.png">

### Frame-2のHeightの初期値

Frame-2のHeightはHug contentsなので、Frame-2の子要素によってHeightが自動で伸びます。

とはいえ、最初は何も要素がないため、Fiexed heightと同じように高さを調節できます。fix要素のステータスバーなどのすぐ下から始め、要素を入れやすいように適当に下に伸ばしておきます。

### Frame-2が縦にFrame-1をはみ出したら？

Frame-2に多くの要素を入れていくと、Frame-2がFrame-1を縦にはみ出すことがあります。その場合、特に何もする必要はありません。

Frame-2は背景ありで自動で伸びてくるので見栄えを調整する必要はないです。

また、Frame-1が画面のHeightを維持しているため次のメリットが生まれます。

- PrototypeでDeviceを指定していない場合にも、Frame-1のサイズでPrototypeが表示される
- Frame-1のClip contentで、はみ出た要素を簡単に非表示にできる

## 3. Frame-2に要素を入れる

先ほど作ったFrame-2に子要素として、さまざまな種類を入れまくっていきます。

では、入れる要素の配置ごとに見ていきましょう！

## 3-1. 要素が1つで中央揃えしたい時（要素はFixed Width）

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/f307beae-d1ec-e964-ac1a-7f1b103d2f09.png">

### 構造

- Frame-1
    - fix 要素1
    - fix 要素2
    - Frame-2 (Auto layout)
        - テキスト1
        - テキスト2
        - ...

### 解説

ボタンなどのオブジェクトはFrame-2に入れるだけで中央揃えになってくれるので特に言うことはないです。

曲者なのがテキストなので、これについて解説します。

### テキストの詳細

- Width: Fixed width
- Text align: left, center, right

### テキストのWidth: Fixed widthの理由

上の画像のレイアウトを実現する方法は2つあります。

- 方法1: テキストをWidth: Fixed widthにする
- 方法2: Auto layoutを適用した親を横いっぱいに広げ、その中にテキストを入れる

方法2は、左右の余白の変更がしやすいというメリットがありますが、それよりも次に挙げられる方法1のメリットを重要視したため、この設計手法では方法1を採用しています。

- 階層が浅い
- 左右に等しい余白が絶対にできる
    - FIgmaの数値入力ボックスは四則演算が利用できるので、左右に16pxずつ余白が欲しければ「360 - 16*2」とそのまま入力すれば良いです。（上画像の青いテキスト）
    - テキストのtext-alginをleftからrightに変更した時、左と同じサイズの余白を右に作れるので便利です。（上画像のグレーのテキスト）

## 3-2. 要素が複数で中央揃えしたい場合

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/cfd37541-d9da-8e98-b870-9d8e04d3e2bc.png">

### 構造

- Frame-1
    - fix 要素1
    - fix 要素2
    - Frame-2 (Auto layout)
        - Frame-3 (Auto layout)
            - ボタン1
            - ボタン2

### 解説

先ほどはテキスト、1つのボタンなどを考えましたが、今回は2つのボタンを中央寄せする場合を考えます。

まずFrame-2がvertical directionなので、Auto layoutを適用した親を作り、その中で横並びにするのまでは良いでしょう。以降、この親を「Frame-3」と呼び、上の画像では青い点線で示しています。

### Frame-3の詳細

- Width: Hug contents
- Height: Hug contents
- Padding around: 0

### Frame-3のWidthがHug contentsの理由

問題は、下画像で示されるような2種類の方法からどちらを選ぶかということです。

- 方法1: Frame-3をWidth: Hug contentsにする
- 方法2: Frame-3をWidth: Fill containerにしてAlignment: centerにする

この設計手法では方法1で次ののメリットが得られるため、方法1を採用します。

- Frame-2のAlignment: centerで中央寄せになっていると明確に分かる
    - 方法2だとFrame-3がAlignment: leftで、左にPaddingがいい感じについている可能性や、もしくはFrame-3がAlignment: centerで左右に小さいPaddingが含まれている可能性がゼロではありません。これに対して、方法1は、Frame-3を選択し、選択範囲（青い点線）を見るだけで、Frame-2のAlignment: centerで要素が真ん中に配置され、余計なPaddingが付いていないことが分かります。（方法2が正しく機能していたとしても、Frame-2とFrame-3で中央寄せを2回行なっており、複雑に感じてしまいます。）
- コンポネント化しやすい
    - コンポネント化する要素は、別の場所でも利用可能にするため、余計なpadding aroundをつけないことが普通だと思います（CSSのBEM設計における「ブロックにはmarginを含めない」というルールと同じノリです）。その点、方法2はPadding around: 0がないので繰り返し使いやすい構造と言えます。

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/bb6bed9c-074b-00e8-fe5a-a388fc94bbad.png">


## 3-3. 要素を左右の余白が異なる場所に配置したい場合

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/2535bef0-c8a4-412d-6a9e-6c8f618bf0c3.png">

### 構造

- Frame-1
    - fix 要素1
    - fix 要素2
    - Frame-2 (Auto layout)
        - Frame-3 (Auto layout)
            - ボタン1
        - Frame-3 (Auto layout)
            - ボタン2
        - Frame-3 (Auto layout)
            - ボタン3

### 解説

スマホ画面の場合は99%、上記の[3-1](https://qiita.com/drafts#3-1-%E8%A6%81%E7%B4%A0%E3%81%8C1%E3%81%A4%E3%81%A7%E4%B8%AD%E5%A4%AE%E6%8F%83%E3%81%88%E3%81%97%E3%81%9F%E3%81%84%E6%99%82%E8%A6%81%E7%B4%A0%E3%81%AFfixed-wid)と[3-2](https://qiita.com/drafts#3-2-%E8%A6%81%E7%B4%A0%E3%81%8C%E8%A4%87%E6%95%B0%E3%81%A7%E4%B8%AD%E5%A4%AE%E6%8F%83%E3%81%88%E3%81%97%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88)の方法で解決できます。ただし稀にテキストの左右で異なるサイズの余白を作りたい場合や、ボタンを中央以外の位置に配置したい場合は、3-2と同じくAuto layoutを適用した親要素を作ります。（3-2と同じく、これを「Frame-3」と呼び、青い点線で示します。）

### Frame-3の詳細

- Width: Fill containerでFrame-2と同じ
- Height: Hug contents
- Alignment
    - vertical : left, right（centerの場合は[3-1](https://qiita.com/drafts#3-1-%E8%A6%81%E7%B4%A0%E3%81%8C1%E3%81%A4%E3%81%A7%E4%B8%AD%E5%A4%AE%E6%8F%83%E3%81%88%E3%81%97%E3%81%9F%E3%81%84%E6%99%82%E8%A6%81%E7%B4%A0%E3%81%AFfixed-wid)を参照）
- Padding around:
    - leftを設定: 左に余白が作りたい場合
    - rightを設定: 右に余白を作りたい場合
    - leftとrightを設定: 左右に余白を作り、間をFill containerの子要素で埋めたい場合

### Padding around: Fill containerの理由

Frame-3をWidth: Fill containerで横幅いっぱいに広げると以下のメリットが生まれます。

- 左右の余白調整の自由度が高い
- 画面サイズの横幅が変わっても、左右の余白を保てる
- Paddingをどこにつけたかによって、[Frame-3の詳細](https://qiita.com/drafts#frame-3%E3%81%AE%E8%A9%B3%E7%B4%B0-1)のPaddingのように、目的を伝えられる

これらのメリットが重要だと判断したため、3-2の[Frame-3のWidthがHug contentsの理由](https://qiita.com/drafts#frame-3%E3%81%AEwidth%E3%81%8Chug-contents%E3%81%AE%E7%90%86%E7%94%B1)で話した、Padding around:0が持つメリットでは手放しています。

### Padding aroundに左右等しい余白を設定する場合

仮に左右の余白が等しいと[3-2](https://qiita.com/drafts#3-2-%E8%A6%81%E7%B4%A0%E3%81%8C%E8%A4%87%E6%95%B0%E3%81%A7%E4%B8%AD%E5%A4%AE%E6%8F%83%E3%81%88%E3%81%97%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88)と見かけ上は同じようになります。

この場合は「Widthを固定したい」のか、「左右の余白を固定し、子要素は自動で伸ばしたい」のか、という目的によって2つを使い分けると良いと思います。

## 4. Frame-2の要素の間に余白を挿入

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/2380f8db-ba5f-efa4-9f37-13a962158890.png">

### 構造

- Frame-1
    - fix 要素1
    - fix 要素2
    - Frame-2 (Auto layout)
        - $spacing
        - 要素1
        - $spacing
        - 要素2
        - $spacing
        - ...

### 解説

まず、FigmaのAuto layoutの問題点について考えてみましょう。1番の問題点は、間隔ごとに個別の値を設定できないということです。

そこで、解決策としてFrame-2のSpacing betweenで縦方向の間隔を作ることを禁止し、新たに$spacingというオブジェクトを挿入します。

これによって、間隔ごとに個別の値を設定できるだけでなく次のメリットも得られます。

- 間隔の種類を限定できるため、統一感を持てる
- Fillなどを活用すれば、見ただけで間隔の種類を特定できる

ちなみに、この話はFrame-2の子要素に対してのもので、それより深い要素のまとまりに対しては考えていません。それらの深い要素のまとまりについては、自由にPadding around, Spacing between, $spacingを使っても良いこととします。

### $spacing とは

間隔を作るための\$spacingですが、これは\$spacingという名前のVarisntsです。

それぞれの要素はボタンのように、親Frame（Fillあり、Auto layoutでWidth: Fill container）と、その中のテキスト要素のみで構成されています。（後述のプラグインのため、「$spacing」という名前が重要です。）

Variantsには、下の画像のように2つのPropertyを設定します。

- height
    - 値はsmall, medium, large...と、高さによって設定
    - それぞれFIllの色を変え、テキストをつけると分かりやすいです。
- color
    - 値はon とoffの2種類
    - offの時はFillを透明に設定

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/9325cb99-a374-c31f-1d1d-da5f8d0cbb03.png"><img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/f555f4b4-8c6b-7c37-0f8f-61dca5a210dd.png">



### $spacingのwidthがFill contaierの理由

Fill containerだと以下のメリットが得られます。

- 親のサイズに合わせて自動で伸びる
- 見やすい
    - 左下の画像がサンプルで作って見た$spacingですが、遠くからだとあるのかないのか良く分かりません。ファイル全体を見渡した時には右下の画像のように、Fillがあるほうが識別しやすいです。
- 階層が分かりやすい
    - 右下のの画像の場合は、グレーのブロック、テキストのlorem..がひとまとまりのFrameになっていることが分かると思います。

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/92afdac5-7a63-9bba-6348-29a2f3f6bcdf.png"><img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/efd22b68-6231-42bc-cab8-72c1596d9046.png">

### Spacing Manager pluginで表示、非表示切り替え

先ほどのVariantsのcolorをon, offすることで、\$spacingごとに表示、非表示は切り替えることができます。ただ、全ての$spacingを1つ1つ変えることは面倒なため、[Spacing Manager](https://www.figma.com/community/plugin/809099681305129697/Spacing-Manager) pluginを利用します。

このプラグインを利用すると、Frame/Pageごとに「$spacing」という名前のオブジェクトを非表示、表示できます。

ここで新たに起きる問題として、間隔が透明の場合に、次の2つのうちどちらかが判断できないものがあります。

- Variantsのcolorがoffになっている
- Spacing Manager pluginで非表示になっている

よって、colorのon, offは一時的な検証にのみに使い、デフォルトはonにする、というルール設定が必要です。

### Spacers pluginを使わない理由

$spacingと似たような要素を作ることができる、[Spacers](https://www.figma.com/community/plugin/802230205741773645/Spacers)というプラグインがあります。ただし、以下のデメリットが存在するため、Variantsであえて自作しています。

- 間隔の色をHeightごとに変えられない
- 書き込むテキストを自由に変更できない
- 間隔のデザインを一度に変更できない

## 5. 最後にfooter-space Variantsを挿入する

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/c8646012-8673-4616-b51b-118bdb358ae3.png">

### 構造

- Frame-1
    - fix 要素1
    - fix 要素2
    - Frame-2 (Auto layout)
        - $spacing
        - 要素1
        - $spacing
        - 要素2
        - $spacing footer-space

### 解説

少し話が変わってPrototypeについて考えます。

PrototypeのVertical scrollは、要素があるところまでしか下にスクロールできないので、スクロール要素がfix要素に隠れてしまうというものがあります。例えば左下の画像のようなレイアウトを作った場合、Prototypeで最後までスクロールしようとしても赤いテキストはボトムナビゲーションに隠れてしまいます。

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/b5de0fd2-d5d5-9d56-8b9c-a108bdfbfa2b.png"><img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/b591f772-b988-659c-f9ca-05a91b8e2c0d.jpeg">

要素の少し下までFrame-2を伸ばせば良いのですが、毎回それを手動で行うのは面倒です。

この設計手法では\$spacingを利用することによって解決します。具体的には先ほどの$spacingと同じ構造の要素を1つ増やし、次のように設定します。

### footer-spaceの詳細

- Property height: footer-space
- Height: (下にあるfix要素) + (fix要素の上に持ちたい余白の大きさ)

このfooter-spaceを左下の画像のように、Frame-2の最後に入れることで、右下の画像のようにFrame-2が伸び、最後の赤いテキストを表示し、余白も持つことができます。

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/c8646012-8673-4616-b51b-118bdb358ae3.png"><img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/47ff9a80-d8e3-37df-21e0-30909d757b87.jpeg">

注意として、Frame-2がFrame-3をはみ出さない限りは、footer-spaceを入れる必要はありません。むしろ、footer-spaceを追加することで、Frame-2がFrame-1をはみ出し、予期しないVertical scrollが起きる可能性があります。

## 6. リスト要素だけはAuto layoutを適用

<img width="250px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504385/c88270ef-ce69-5fc8-28eb-c56a532ec365.png">

### 構造

- Frame-1
    - fix 要素1
    - fix 要素2
    - Frame-2 (Auto layout)
        - 要素1
        - $spacing
        - リスト (Auto layout)
            - リスト要素1
            - リスト要素2
        - $spacing
        - 要素2

### 解説

先ほども言ったように、要素自体にこれまでの\$spacingやPadding around:0のルールを絶対に適用する必要はありません。

ただし、リストはに関しては、積極的にAuto layoutを適用するべきだと思っています。

### リストがAuto layoutの理由

まず、Auto layoutの特徴はSpacing betweenで一定の間隔を作れることです。これがデメリットとなって、Frame-2では$spacingを作りました。

しかし、リストはそもそも並列の構造で要素を並べるので、一定の間隔を空けることが必要な要素のまとまりであると考えられます。よって、リストにはAuto layoutを用いてSpacing betweenで間隔を調整します。

また、Auto layoutを利用することで次のメリットを得られます。

- リスト要素を複製（command + D）するだけで勝手に間隔も作れられる
- 縦要素にAuto layoutを適用することで、リストだと示すことができる
    - このメリットを活かすため、リストならば要素が2つでもAuto layoutを適用するべきです。

## 7. 完成！メンバーに必要な知識をインプット

結構特徴ある設計手法にしてしまったので、最低限メンバーに伝えなければいことをまとめます。

### 深い階層へのアクセス方法

- command + クリックすると、深い階層の要素でも一発で選択できます。
- Inspectモードにすると、Auto layoutを適用した親要素を選択した状態で、余白をホバーするだけでPaddingのが表示されます。

### $spacingはプラインで表示、非表示を切り替える

- [Spacing Manager](https://www.figma.com/community/plugin/809099681305129697/Spacing-Manager) pluginの導入
- 機能の説明
    - 「$spacing」という名前の間隔を作っているオブジェクトの表示非表示を切り替えるもの
- ルール
    - $spacingのcolorは常にonにする

### Frame-2の上に要素を載せる方法

Frame-2の上に要素を重ねようとすると、Auto layoutの子要素だと認識されて内に取り込まれてしまいます。そのような時はスペースキーを押しっぱなしにすることで、Auto layoutに取り込まれないようにできます。

### Auto layoutとCSSのflexは一致しない

FigmaのAuto layoutはCSSのflexという機能に相当します。なので、デザイナーはFigmaを利用することでエンジニアにレイアウトの意味を伝えることができます。

はずなのですが、今回は様々なところにAuto layotuを使いまくってしまい、CSSのflexとは利用法が異なります。（CSSでは要素単体でmarginを持てるため）

## 最後に

このめちゃくちゃ長い文章を最後まで読んでいただいてありがとうございます！

「これと違った自分の設計手法もってるぜ！」とか「ここはこうしたほうが便利」などあれば、ぜひコメントください！!