↔️

[CSS] 私がmarginではなくgapを使う理由

に公開

結論

それが要素と要素の間隔だからです。

はじめに

最近、SNSで要素同士の間隔、余白について、「縦並びの要素同士の間隔ならブロック要素のスタッキング+marginで十分。Flex Layoutをわざわざ使う必要はないのでは」みたいな主張を見かけました。それに対する反応を見る限り、スペーシング問題だったり、Flex Layoutの採用是非だったり、論点は違えど概ね同じような主張の方も多そうだったので、自分にとっては少し衝撃的でした。

自分は基本的に縦横関係なく、要素を一方向に並べるならFlex Layoutを使う[1]し、スペーシングはほぼpadding、gapで表現する派で、marginはほとんど使いません。

この考え方の違いはどこからくるのかなと思い、自分がなぜgapをよく使いmarginをほとんど使わないのかを考えることにしました。自分の中でFlex Layoutの採用是非はgapの採用有無にも強く影響されているのでこちらについてはあまり深く触れません。

gapの特徴と用途

gapは主にFlex、あるいはGrid Containerで利用でき、内部のアイテムの間隔、行や列の間隔を一括で設定することができます。

この「一括で(均等に)設定できる」ことに着目してgapの採用(及びFlex Layout、Grid Layoutの採用)有無を決定する、という考え方もあるようですが、自分は要素同士の間隔を設定したい場合においては子要素の数がいくつであろうが、均等でなかろうがgapを採用しています。(均等でない場合どうするのか、は後の章で触れます)

つまり、例えば「要素A、B、C...と複数並んでいる要素の間隔を全て16pxに設定したい。けどmarginをそれぞれに設定したり、.parent > * { ... }のかたちで一括でmarginを設定した上で、&:last-childとかで最初か最後の要素のmarginを別で(0pxなどに)設定するのがめんどくさい!そこでgapを使うとシンプルにかけて便利だから採用する!」とか、そういう話ではないということです。

要素A、BとあってAにだけmargin-bottom: 16pxを設定することで、デザイン通りに実装できる場面でも、そのスペーシングがAとBの間隔として設定されるべきものなら、親要素をFlexコンテナにしてgapで間隔を設定します。

スペーシングの知識はどこに

さっきから、間隔、間隔と書いていますが、例えば「要素AとBの間隔」は「要素Aから要素Bの方向への余白」と書き換えることもできるし、余白なら語意としてもmarginが妥当という考え方もできます。

では、なぜ自分がそうならずここまで一貫して間隔という語彙をもって要素間のスペーシングを表現しているのか。自分の思考の言語化のためにも深ぼってみます。

まず、先述した間隔と余白に関する表現

  • 要素AとBの間隔
  • 要素Aから要素Bの方向への余白

これは、

  • gapは「AとBの親要素(Flex Containerなど)」がそれを規定する。
  • marginは「Aという特定の要素(gapの例と対になる表現なら"子要素")」がそれを規定する。

という、スペーシングの知識がどこについているのか、を表現したセンテンスになっています。

その上で、自分が前者(間隔/gap)を採用しているのは、要素間のスペーシングに関する知識は親要素が持つべき(子要素がレイアウトの都合を知るべきでない)という考え方に落ち着いたためです。

根拠としてこの考えに至った経緯を述べると、まず以下の記事で
https://qiita.com/terakura-aina/items/52b9be2cca2b0594d4ae#どちらを使うのが良いか-1

「そもそも余白は誰が知っているべき(制御すべき)なのか?」を考えたときに、gap を使う方が自然に感じました。

のように語られていることに共感しました。その後「なぜgapを使う方が自然に感じるのか」と考えて、以下の記事にたどり着きました。

https://qiita.com/seya/items/8814e905693f00cdade2#スタイルクローズドの原則

こちらで語られている:

  • 「子コンポーネントは親コンポーネントの"レイアウトのスタイル"を知ってはならない」
  • 「親コンポーネントは子コンポーネントの"見た目のスタイル"を知ってはならない」

この「スタイルクローズドの原則」を読んだ上で、もともと自分の中にあった思考とも結びつき、納得感をもって整理できた、という流れになります。

ドキュメントの構造に基づいたスタイリング

marginで余白を設定するということはどういうことか。
gapで間隔を設定するということはどういうことか。

この章ではこの観点について、サンプル実装のイメージともに語っていきます。

件の「Flex Layoutを使わずとも自然に表現できる縦方向のレイアウト」を例として考えてみましょう。

縦並べの要素同士の間隔設定イメージサンプル
見出しと本文がセットになったセクション3つ

この例において、marginによって余白を設定した場合のイメージを以下の2通り用意してみました。

縦並べの要素同士の間隔設定イメージ例1
下余白を各要素に設定した例

これは素直に、並べられた要素たちに対して下余白を16px/8pxの2パターンで設定したものです。ここでやっていることを自然言語で表すと以下のようになります。

  • 見出しには下余白を8pxつける
  • 本文には下余白を16pxつける

非常にシンプルなスタイリングです。さらに踏み込むなら、余白サイズの関係から

  • 見出しの次には本文が来る(だから下余白は小さく)
  • 本文の次には次のセクション(見出し)が来る(だから余白は大きく)

という前提が文脈情報として入っていると解釈できます。

ちなみに、この例では:last-childを使って最後の要素の下余白を0で上書きしたかたちのものになっていますが、この辺はこの章の論点には含まれないので比較には入れません。[2]

縦並べの要素同士の間隔設定イメージ例2
見出しに対して上下余白を設定した例

これは「見出しには上余白と下余白をつける。サイズは上が16px、下が8px。」という設定の仕方をしているパターンです。さっきの例と同様にさらに解釈をするなら

  • 見出しの前には前のセクションが来る(だから余白は大きく)
  • 見出しの次には本文が来る(だから下余白は小さく)

という意図が入っているといえます。

縦並べの要素同士の間隔設定イメージ例3
Flex Layout + gapによる間隔設定をした例。
階層構造ができていることをわかりやすくするために、一番外側の枠線にはpaddingとgapを着色領域と別で設定しています

対してgapによる間隔の設定。gapは子要素同士の間隔を一括で設定してしまうため、間隔が異なる要素がいる場合は階層分けをして別途gapを設定する必要があります。

今回の場合、図の通り

  • もっとも外側の要素の中のアイテムは16pxの間隔をあける
  • 見出しと本文を囲った要素の中のアイテムは8pxの間隔をあける

という設定をしています。marginの例とは異なり、前述の通り異なるgapの設定のためネストが1つ追加され、間隔を設定する対象として「見出しと本文を囲った要素」という概念が必要になりました。これは今回の場合、marginの例から出ていた通り「セクション」と呼べるまとまりとなっています。

この語を主軸に据えて、より踏み込んだ解釈をしたとき、

  • セクション同士は16pxの間隔をあける
  • セクション内のアイテムは8pxの間隔をあける

という読み取り方ができます。

このように、要素に対して設定されているスタイルによって実装者の意図や、なぜそうなっているのか、という解釈がいくつか生まれます。その点で、より本質的な要素をくみ取りやすい実装の仕方がgapによる間隔の表現だと、個人的には思っています。

gapの採用に伴い、異なる間隔サイズを表現するために階層分離が必要になることがあります。これはある意味手間で、ネストが深くなりがち問題も生まれますが、これもまた要素同士の関係性を明確に表現することに一役買っていると思っています。

要素同士の間隔が同じ箇所は、その間で意味的なまとまりがあると解釈できることが多いし、逆に間隔が異なるなら何らかの境界で分かれている可能性が高いと解釈できます。

この解釈が破綻する場合は、そもそもデザイン段階で間隔の設定や設計に違和感がないかを立ち返って考えることもあります。

私がmarginではなくgapを使う理由

結局、「そのスペーシングが何を意味しているか、を考えたときそれが要素同士の間隔を表すのであればgapを採用する。」としたとき、大体のケースが要素同士の間隔であったためです。

  • marginは一括で設定しようとすると:last-childなどによる分岐を加えて末端の要素の余白をなくす必要が出てくる。
  • gapは異なるサイズを個別に設定したい、と思うたびにネストが深くなりがち。
  • Flex Layoutを採用するほどの構造ではない。ブロック要素のスタッキングで十分。だからmarginを使う。
  • gapは一括で間隔が設定できて楽。
  • 間隔を設定したい子要素の数が2つしかないので、片方にmarginを設定するだけの方がシンプル。

これらのような実装都合の要因は、個人的には重要ではないと考えています。

おわりに

冒頭で断った通り、これは他の考え方を排他的に扱ったり強制する意図はなく、いちフロントエンドエンジニアの思考開示くらいのつもりで書いたものになります。

当然ですが、marginによる余白の設定が適切な場面もあります。結局使い分けの話ではあるのですが、少なくとも自分の思考だとgapを使うことが多いよ、という主張でした。

脚注
  1. スタイルの標準化、それに伴う整列方向の明示。gapの利用などのため。 ↩︎

  2. この記述の手間をなくせるという点でも個人的にはgapの採用を推していますが。 ↩︎

ispec inc.

Discussion