🙌

CSSのmarginをあまり書かなくなった話

2023/12/09に公開

この記事は、Lancers(ランサーズ) Advent Calendar 2023 の9日目の記事です。
本記事では、マークアップで、CSSのmarginプロパティをあまり書かなくなった話を紹介します。

Webサービスのレイアウトについて

大きく以下が考えられます。

  • 縦に並べる
  • 横に並べる
  • 絶対配置

さらに、それぞれに対して以下が考えられます。

  • 不規則的な配置
  • 規則的な配置

今回は、縦に並べることに着目して、マークアップを考えてみます。

不規則的な配置のパターン

marginを利用する

.mt-4  { margin-top: 4px;  }
.mt-16 { margin-top: 16px; }
.mt-24 { margin-top: 24px; }
.mt-32 { margin-top: 32px; }

<div class="box box1"></div>
<div class="box box2 mt-32"></div>
<div class="box box3 mt-16"></div>
<div class="box box4 mt-4"></div>
<div class="box box5 mt-24"></div>

margin-topで揃える

兄弟要素間の余白のルールを整えやすいため

margin-topで統一すると、以下の様な記述だけで済みます。

/* 兄弟要素 */
.element + .element {
  margin-top: 16px;
}

margin-bottomで統一すると、最終要素で以下の様なスタイルを記述する必要があります。

.card > :last-child {
  margin-bottom: 0;
}

要素の下部で余白が必要な場合は、子要素のmargin-bottomではなく、親要素のpadding-bottomで調整する方が良いですね。

.parent {
  padding-bottom: 16px;
}

<div class="parent">
  <div class="box box1"></div>
  <div class="box box2"></div>
  <div class="box box3"></div>
  <div class="box box4"></div>
  <div class="box box5"></div>
</div>

marginの相殺防止のため

margin-topとmargin-bottomのルールが整理されていない場合、marginの相殺が発生します。
marginの相殺とは、兄弟要素でmargin-botttomとmargin-topが重なりあう場合、大きい方が優先される現象のことです。

引用:https://with.sunabaco.com/964

marginは扱いにくい

marginは、UI間の余白を調整するプロパティです。
marginありきでUIをマークアップすると、UI同士を組み合わせる際、不都合が生じやすいです。
そのため、marginは、コンポーネントには直接記述せず、親側の要素から適用した方が良いです。

.example {
  display: block;
  margin-top: 16px; /* ← こういったmarginの記述は、コンポーネントの汎用性がなくなる */
  font-size: 40px;
  font-weight: 300;
  line-height: 1;
  color: $colorGrey60;
}

marginを利用する場合は、utilクラスなどを外から適用する形が理想的です。

.example {
  display: block;
  font-size: 40px;
  font-weight: 300;
  line-height: 1;
  color: $colorGrey60;
}

/* こういったcssクラスを用意する */
.u-mt16 { margin-top: 16px; }
<!-- 外部から適用する -->
<div class="example u-mt16"></div>

不規則な配置は極力なくした方がよい

例として、不規則な配置の組み方を紹介しましたが、本来こういった配置はWebサービスおいては、望ましくありません。
デザインには4原則というものがあり、このルールが正しく適用されていないと、情報が整理されていない印象をユーザーに与えてしまいます。
そのため、WebサービスのUIは規則的な配置が望ましいです。

4gensoku.jpg

引用:https://docodoor.co.jp/staffblog/design-4general-rule/

規則的な配置のパターン

marginを利用する

css

.mt-16 {
  margin-top: 16px;
}

html

<div>
  <div class="box box1"></div>
  <div class="box box2 mt-16"></div>
  <div class="box box3 mt-16"></div>
  <div class="box box4 mt-16"></div>
  <div class="box box5 mt-16"></div>
</div>

利用シーン

  • 要素数が少ないとき
  • ラップする親要素をもたないとき

メリット

  • スタイルを適用したい箇所に、自由に記述できる

デメリット

  • 要素が増えた場合、クラスの記述が増える為、htmlの可読性が落ちる
  • マージンの幅を変える際に、全てのhtmlやcssを変更する可能性がある
  • 各要素にmarginが含まれていると、marginの相殺が発生する恐れがある
  • cssの記述が雑になりがち

個人的なお気持ち

marginを使う方法はデメリットが多く、最近はmarginをなるべく書かないことを意識しています。

flexboxを利用する(おすすめ)

display: flex + flex-direction: column + gapを利用します。

css

.wrap {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

html

<div class="wrap">
  <div class="box box1"></div>
  <div class="box box2"></div>
  <div class="box box3"></div>
  <div class="box box4"></div>
  <div class="box box5"></div>
</div>

メリット

  • 親要素にスタイルを適用するだけなので、html、cssともに可読性が良い
  • 要素同士の幅を変更する際も、親要素のgapの値を変更するだけでok
  • marginの相殺を考えなくて良い
  • コンポーネント自体にmarginを書かなくて良いので、コンポーネント指向と相性が良い

デメリット

  • スタイル記述の為に、ラップ用の要素を用意する必要がある
  • デザインがあえてあえて不規則なレイアウトの場合、適用するのが難しい
    • デザイナーと相談して、不規則である理由がなければ、規則的なレイアウトにすればよいと思います

余談

下記のような等間隔の要素間に線が入る系の組み方について

下記のようなCSSを記述します。

.wrap {
  display: flex;
  flex-direction: column;
  gap: 32px; /* gap */

  & > *:not(:last-child) {
    position: relative;

    &:after {
      position: absolute;
      left: 0;
      bottom: -17px; /* -1 * (gap / 2 + lineWidth / 2) */
      content: '';
      display: block;
      height: 1px;
      width: 100%;
      background-color: #F4F4F4;
    }
  }
}

記述が長く、複雑ですが、子要素側でpadding, margin, borderの調整の必要がない為、コンポーネント指向的な組み方をする場合は、かなり良い体験になります。

CSSを関数化する

util関数を作成することで、等間隔の要素間に線が入る系UIのcssを書く手間が減ります。
下記はemotionのコードになりますが、CSS Modulesでもsassでも、似たような関数を作成可能です。

export const flexColumnBetweenLineCss = (gap = 0, lineWidth = 1) => css`
  display: flex;
  flex-direction: column;
  gap: ${gap}px;

  & > *:not(:last-child) {
    position: relative;

    &:after {
      position: absolute;
      left: 0;
      bottom: -${gap / 2 + lineWidth / 2}px;
      content: '';
      display: block;
      height: ${lineWidth}px;
      width: 100%;
      background-color: ${Color.LineGray};
    }
  }
`

まとめ

UIは、ユーザー視点でも開発者視点でもシンプルなものがベターです。
シンプルなマークアップは、コードの可読性、ブラウザのレンダリングパフォーマンス向上に繋がります。

flexboxやgridは、横に並べる場合、よく利用されますが、
縦に並べる場合でも、有効活用出来ますので、どんどん利用していきましょう。

※flex-gapは、全モダンブラウザで利用可能なのですが、iOS14に関しては、14.0〜14.4はサポートされていない為、注意が必要です。

ランサーズ株式会社

Discussion