[CSS]marginの相殺を正しく理解しておこう!相殺はいつ起こるのか、相殺を回避するテクニック

まずは、クイズ。
Aのdiv要素には「margin-bottom: 10px;」を、Bのdiv要素には「margin-top: 30px;」を指定した場合、それらを垂直に配置するとマージンはいくつでしょうか?

マージンの相殺を徹底解説

What's the Deal with Collapsible Margins?

これは「マージンの相殺(Collapsing Margins)」と呼ばれるものです。
このマージンの相殺とは何なのか? いつどういう条件の時に起こるのか? それぞれどのように回避できるのかを紹介します。

下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。

marginの相殺についてアンケート

わたしは先月、Twitterでアンケートを行いました。

CSSのクイズ: 2つのdiv要素を配置し、1つ目に「margin-bottom: 10px;」を、2つ目に「margin-top: 30px;」を指定した場合、それらの間のスペースはいくつになるでしょうか?
ツイートのキャプチャ

アンケートのツイート

アンケートの結果、30pxと正しい答えに投票した人は39%でした。

この答えが30pxになる理由は、「マージンの相殺(Collapsing Margins)」と呼ばれるCSSの機能によって起こります。

CSSのmarginがどのように機能するか

CSSのマージンがどのように機能するかを理解するために、ボックスモデルを再確認しておきましょう。CSSのボックスモデルは前の記事「Controlling The Box Model」で触れたように、ドキュメントツリーの各要素はコンテンツ、パディング、ボーダー、スペースの4つのエリアで構成された長方形のボックスです。

.box要素を例に見てましょう。

マージンはボーダーの外側で、下図のストライプのエリアです。

CSSのボックスモデル

マージン: ストライプのエリア

マージンはボックスモデルの他の3つのエリアとは異なり、要素自体の表示には影響を与えません。しかしマージンの相殺のように、Webページに表示される実際のマージンは他の要素の影響を受ける可能性があります。

marginの相殺(Collapsing Margins)とは

マージンの相殺は、垂直マージンを指定した2つのブロックレベル要素が並んだ時に起こります。マージンの相殺が起こると、2つのマージンのうち大きい方(等しい場合はいずれか)が1つのマージンとしてみなされます。

Twitterのアンケートから例を引用します。
2つのdiv要素があり、Aのdiv要素は10pxのmargin-bottomを、Bのdiv要素には30pxのmargin-topを指定します。

マージンを指定した2つのdiv要素

マージンを指定した2つのdiv要素

この2つのブロックレベルの要素をマークアップで順番に並べると、下記のように表示されます。

2つのdiv要素を表示

2つのdiv要素を表示

2つの要素の間にあるマージン(10pxと30px)はマージンの相殺が起き、大きい方のマージンである30pxのマージンが残ります。

marginの相殺はいつ起こるのか

原則として、インフロー、ブロックレベル、ボックス間の隣接する垂直マージンは常に相殺されます。これは4つのシナリオで起こります。

ケース1: 親と最初の子の上マージン

margin-topが親要素に指定し、インフローの最初の子要素にも指定した場合、それらのマージンは相殺されます。

親要素の「margin-top: 30px;」、子要素の「margin-top: 30px;」は相殺され、マージンは30pxで表示されます。

インフローで相殺される隣接する垂直マージン

インフローで相殺される隣接する垂直マージン

ケース2: 親と最後の子の下マージン

ケース1と同じように、margin-bottomが親要素に、インフローの最後の子要素にも指定した場合、マージンは相殺されます。
ただし、上マージンとは異なり、下マージンは親の高さがautoの場合にのみ相殺されます。

親要素の「margin-bottom: 30px;」、子要素の「margin-bottom: 30px;」は相殺され、マージンは30pxで表示されます。
※親要素の高さが、autoの場合のみ。

インフローで相殺される隣接する垂直マージン

インフローで相殺される隣接する垂直マージン

ケース3: 兄弟要素の下と上のマージン

このケースは、アンケートで提示した例です。
margin-bottomを1つ目の要素に、margin-topを2つ目の要素に指定した場合、それらの間にあるマージンは相殺されます。

兄要素の「margin-bottom: 10px;」、弟要素の「margin-top: 30px;」は相殺され、マージンは30pxで表示されます。

ブロックレベルで相殺される隣接する垂直マージン

ブロックレベルで相殺される隣接する垂直マージン

ケース4: 空の要素

最後は要素の高さが0、いわゆる空の要素にmarign-bottomとmargin-topを指定した場合、それらのマージンは相殺されます。

空要素の「margin-bottom: 30px;」と「margin-top: 30px;」は相殺され、マージンは30pxで表示されます。

ボックス間で相殺される隣接する垂直マージン

ボックス間で相殺される隣接する垂直マージン

例外

マージンの相殺が起きない例外も少数ですが、あります。

Flexbox、Grid、その他のブロックレベルでない要素
マージンの相殺はブロックレベルの要素にのみ適用されます。つまり、displayプロパティの値がblock, list-item, tableのいずれかの時です。そのため、flexアイテムやgridアイテムや絶対値アイテム、そしてブロックレベルではない要素には適用されません。
ルート要素
ルート要素のボックスのマージンは絶対、相殺されません。
ボーダーやパディングを使ったラインのボックス
下図のように、2つの要素間にあるラインのボックス、パディングやボーダーがある場合、相殺されません。ケース1では親と最初の子の上マージンは相殺されましたが、これにボーダーを加えるだけで、相殺されません。
ボーダーを加えると、相殺されない

ボーダーを加えると、相殺されない

これがなぜ起きるかというと、親のマージンと子のマージンは直接接触していないことがその理由です。

marginの相殺を回避するテクニック

マージンの相殺がいつ発生するのかを正しく理解していないと、思い通りのレイアウトができないで困ることがあります。それを回避するための第一歩は、その相殺がどのケースなのか、正確に理解することです。

空要素や親子要素で起きるマージンの相殺は、実際には避けることができません。これに対応する唯一の方法は、要素の間にボーダーなど何かを挿入することです。ほかの対応方法としては、要素のプロパティをブロックレベル以外(flex, gridなど)に変更します。

隣接する兄弟要素で起きるマージンの相殺は、CSSのスタイルを変更することで回避することができます。個人的には「Single-direction margin declarations」に書かれている「マージンの指定は単一方向にする」に従うことを好みます。このルールはマージンの相殺を回避するだけでなく、ほかにも利点があります。

サイトのキャプチャ

What's the Deal with Collapsible Margins?

top of page

©2017 coliss