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は規則的な配置が望ましいです。
引用: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はサポートされていない為、注意が必要です。