last-childのmarginを0にして再利用しやすいCSS設計をする

HTML/CSS

『レイアウト』と『UIパーツ』を意識してCSS設計をするとデザインの再利用がしやすくなります。
UIパーツに関するCSSには再利用性の観点からUIパーツの外側の余白(margin)を含めないのが原則です。

今回はリストアイテムを例に、UIパーツの余分なマージンを:last-childで削除するテクニックについて紹介します。

今回利用するサンプルのデザインについて

横並びの正方形のリストアイテム(青色の要素)と、長方形のアイテム(緑色の要素)を並べるケースについて考えます。

<div class="container">
  <div class="pager container__pager">
    <div class="pager__item"></div>
    <div class="pager__item"></div>
    <div class="pager__item"></div>
    <div class="pager__item"></div>
  </div><div class="button container__button"></div>
</div>
.pager {
  display: inline-flex;
}

.container__pager {
  margin-right: 30px;
}

.pager__item {
  width: 60px;
  height: 60px;
  background-color: lightblue;
}

.pager__item:not(:last-child){
  margin-right: 10px;
}

.button {
  width: 200px;
  height: 100px;
  background-color: greenyellow;
  display: inline-block;
  vertical-align: top; /* 上揃えにする */
}

See the Pen
pagenation-button-container
by Toshiharu Nishina (@nishina555)
on CodePen.

以降の説明では横並びの正方形のリストアイテム(青色の要素)をページネーション、長方形のアイテム(緑色の要素)をボタンと定義します。

改善前: UIパーツのCSSに外側の余白が含まれている場合

margin-rightでページネーションの各アイテムの間隔を調整するとします。
ページネーションの右横にさらにボタンを並べる場合、ページネーションの最後のアイテムのmargin-rightの影響でページネーションとボタンの間に余白ができます。

<div class="pager">
  <div class="pager__item"></div>
  <div class="pager__item"></div>
  <div class="pager__item"></div>
  <div class="pager__item"></div>
</div><div class="button"></div>
.pager {
  display: inline-flex;
  border: dotted; /* マージンをわかりやすくするために追加 */
}

.pager__item {
  width: 60px;
  height: 60px;
  background-color: lightblue;
  margin-right: 10px;
}

.button {
  width: 200px;
  height: 100px;
  background-color: greenyellow;
  display: inline-block;
  vertical-align: top; /* 上揃えにする */
}

See the Pen
pagenation-button-before
by Toshiharu Nishina (@nishina555)
on CodePen.

たとえば『ページネーションとボタンの間のマージンは30px』という仕様の場合、仮にmargin-rightが10pxであれば20px(30px-10px)のマージンを追加すれば仕様は満たせます。
しかし、UIパーツのマージンを考慮してレイアウトの余白を調整するのは健全なCSS設計とはいえません。

改善後: 『:last-child』をUIパーツの外側マージンを削除した場合

:last-childは最後の兄弟要素を指定する疑似クラスです。

:last-childmarigin: 0を指定することで、ページネーションの最後のアイテムのマージンのみ削除できます。具体例は以下の通りです。

<div class="pager">
  <div class="pager__item"></div>
  <div class="pager__item"></div>
  <div class="pager__item"></div>
  <div class="pager__item"></div>
</div><div class="button"></div>
.pager {
  display: inline-flex;
  border: dotted; /* マージンをわかりやすくするために追加 */
}

.pager__item {
  width: 60px;
  height: 60px;
  background-color: lightblue;
  margin-right: 10px;
}

/* このCSSを追加 */
.pager__item:last-child {
  margin-right: 0px;
}

.button {
  width: 200px;
  height: 100px;
  background-color: greenyellow;
  display: inline-block;
  vertical-align: top; /* 上揃えにする */
}

See the Pen
pagenation-button-after
by Toshiharu Nishina (@nishina555)
on CodePen.

上記の結果を見て分かる通り、先ほど存在していたページネーションとボタンの間の余白がなくなったので、レイアウトの余白を考える際にUIパーツのマージンを考慮する必要がなくなりました。

なお、pagerクラスにpager__item以外の子要素が存在するケースを考慮して、以下のように最後の要素を指定する記述方法もあります。

/* pagerクラスの直下にある最後の要素に適用される */
.pager > *:last-child {
  margin-right: 0px;
}

参考: :not(:last-child)を利用するとよりシンプルに記述できる

『最後の要素のみマージンが不要』は『最後の要素以外はマージンが必要』と言い換えられます。
ですので、:last-child:notを組み合わせることで以下のように書き換えられます。

.pager__item {
  margin-right: 10px;
}

.pager__item:last-child {
  margin-right: 0px;
}
.pager__item:not(:last-child) {
  margin-right: 10px;
}

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考記事