Chapter 06無料公開

- 均等に並ぶ要素間の余白をスマートに付ける(頻出度:★★★)

hideki_climax
hideki_climax
2022.08.30に更新

概要

ヘッダー/フッターによく見られるグローバルナビやなんらかの項目を列挙しているリストなど、要素が等間隔で並んでいるようなデザインってよくありますよね。例えばzennさんのフッターを見てみると、リストが並んでいる形になっていて、それぞれの項目は等間隔に並んでいます。

他にも、記事の一覧も記事カードが均等に並んでいますよね。

さて、このように要素が均等に並ぶ際の余白の付け方ですが単純に子要素にmarginをつけると1つ目、ないしは最後の要素の余白が邪魔になります。横方向であればflexとjustify-content: space-betweenを使えば良いですが縦方向ではそうもいきません。

そこで、「子要素にmarginを付けて、1番目のmarginのみfirst-childで打ち消す」といった方法で対応していませんか?

実はもっとスマートな方法があります。ここでは「flexboxとgapプロパティを使う方法」と「フクロウセレクタと呼ばれる書き方を使う方法」の2つを紹介します。

デモ

見た目というよりは、デモサイトがどのようになっているかデベロッパーツールから確認してみてください。

https://itokoba.com/demos/list-and-margin/

コード

flexとgapプロパティを使った場合

index.html
<ul class="List">
  <li class="ListItem">項目1</li>
  <li class="ListItem">項目2</li>
  <li class="ListItem">項目3</li>
  <li class="ListItem">項目4</li>
</ul>
style.css
.List {
  width: 100%;
  display: flex;
  flex-direction: column; /* 縦方向のflexを使う */
  gap: 20px;
}

.ListItem {
  width: 100%;
  padding: 8px;
  background-color: #87ceeb;
  color: #fff;
}

フクロウセレクタを使った場合

index.html
<ul class="List">
  <li class="ListItem">項目1</li>
  <li class="ListItem">項目2</li>
  <li class="ListItem">項目3</li>
  <li class="ListItem">項目4</li>
</ul>
style.css
.List {
  width: 100%;
}

.List > * + * { /* ←これがフクロウセレクタ */
  margin-top: 20px;
}

.ListItem {
  /* 上記と同様 */
}

解説

flexとgapプロパティを使った場合について

まず、display:flexは横並べにするためのプロパティと思われがちですが厳密には1次元方向のレイアウトを行うためのプロパティです。よって今回のように縦方向のデザインにも使うことができます。

方向はflex-directionというプロパティで指定します。値がrowなら横方向(初期値)、columnなら縦方向になります。

flex-direction: row;
flex-direction: column;

そしてdisplay:flexもしくはdisplay:gridの時にはgapというプロパティが利用できます。これで要素間の余白を決めることができます。厳密には横方向と縦方向に分かれていて、それぞれrow-gap、column-gapというプロパティです。

row-gap: 20px;
column-gap: 20px;

これらを親要素側(.List)に指定することで、子要素(.ListItem)の並び方と余白が反映されるという訳ですね。

gapについてはMDNでも詳しいページがありますので合わせて参照してください。
https://developer.mozilla.org/ja/docs/Web/CSS/gap

フクロウセレクタを使った場合について

これはかなり特殊な書き方ですね。「> * + * 」の部分がフクロウの顔に見えるからそう呼ばれているようです。

このセレクタを親側に使うことで、結果として子要素の1番目以外の要素にスタイルが適用される形になります。

改めて例を見ると、

.List > * + * 
  margin-top: 20px;
}

となっていて、.ListItemの1番目以外にmargin-top: 20pxが適用されます。

原理

一応原理を解説します。(結構ややこしいので必ずしも理解する必要はないかと個人的には思っています。)

まず .List > * から考えましょう。

この > は子セレクタと言い、直下の要素のみを対象とします。

<section>
  <p>テキスト</p>
  <div>
    <p>テキスト<p/>
  </div>
</section>

とあった時、

section > p {
  /* 省略 */
}

と記述するとdivの中のpは対象に含まれないということです。

また * は全要素を表します。
よって .List > * で「.List直下の全要素」を指します。

そして + は隣接セレクタと呼ばれるものです。A + Bとすると「A要素の直後にあるB要素」を示します。

<section>
  <p>テキスト</p>
  <p>テキスト</p>
  <p>テキスト</p>
  <h2>見出し</h2>
  <p>テキスト</p>
</section>

とあった時、

h2 + p {
  /* 省略 */
}

と記述すると最後のpタグのみが対象になります。
隣接という名前ですがあくまで直後のもののみ対象になるのがポイントです。

上記 .List > * にこの隣接セレクタで * をつなげるとフクロウセレクタになるわけですが、その意味は「.List直下の全要素の直後の全要素」となります。分かりにくい日本語ですね。笑

要するにどの要素から見ても直後の要素にならない、つまり直前に要素がない1番目の要素は対象から外れるということです。そのため今回のように1番目以外の要素にスタイルが適用されるのです。