🐙

マージンの折り重ね(マージンの重複)って知ってた?

2023/08/29に公開

Gutenbergのブロック開発をしていて、おかしな現象に見舞われました。次の画像をご覧ください。

image

image

これはブロックにニューモフィズムというシャドーをつけたものです。
上がブロックエディタの表示で、下がフロントエンドの表示です。
ご覧のとおり下は高さが狭く、ニューモフィズムの浮き出た感じが出し切れていません。
なぜ、こんな現象が起こるのかをブログにしたいと思います。

この画像のHTMLとCSSについて

まず、この画像のHTMLとCSSを示します。
まず、HTMLです。

<div>
 	<ul>
 		<li>情報入力</li>
		<li>確認</li>
		<li>処理完了</li>
	</ul>
</div>

次にCSS(SCSS)です。

div{
	ul{
		margin: 1em 2em 1em 2em;
		padding: 1em 2em 1em 2em;
		box-shadow: 5px 5px 5px #ecd4d4,-5px -5px 5px #fcf8f8;
	}
}

簡単なコードです。div要素でul要素をラップし、ul要素にはmarginとpaddingをつけました。
その上でbox-shadowをつければ、marginとpaddingの間にシャドーが落ちてくれると思ったわけです。
しかし、ブロックエディタは思惑どおりでしたが、肝心のフロントエンドは変な表示になってしまいました。

margin-topとmargin-bottomにはマージンの折り重ね(マージンの重複)という現象がある

調べてみるとこんなことがわかってきました。
margin-topとmargin-bottomというのは、上下に接触する要素同士においては、折り重なるという性質があります。
次のようなコードでは、要素Aの margin-bottom と要素Bの margin-top が折り重ねられ、実際のマージンは30px(大きい方の値)となります。

<div style="margin-bottom: 20px;">要素A</div>
<div style="margin-top: 30px;">要素B</div>

image

この事例は隣接する兄弟要素の場合ですが、この現象は、親要素とその最初または最後の子要素の間でも発生します。

<div style="margin-bottom: 20px;">
	要素A
	<div style="margin-top: 30px;">要素B</div>
</div>

image

この図のように子要素の上側・下側のマージンは親要素の外に「押し出される」ことになり、親要素の縦幅を広げるわけではないのです。
これが「マージンの折り重ね(マージンの重複)」です。

親要素に border や padding が付いているとこの現象は起こらない

では、なぜブロックエディタの方は、期待どおりの動きをしてくれるのかということです。
実はGutenbergのブロック開発ではブロックエディタ側には、ブロックに1pxの破線のボーダーが付いています。
これのおかげで子要素のマージンは親要素のボーダーの内側に回るのです。
コードと図解は次のようになります。

<div style="
	margin-bottom: 20px;
	border: 1px dotted #f00;
">
	要素A
	<div style="margin-top: 30px;">要素B</div>
</div>

image

これは親要素にパディングがある場合でも同じです。
その他にも「マージンの折り重ね」が起きない場合があるので、箇条書きにしてまとめます。

  1. borderやpaddingの追加:親要素に少なくとも1pxの border や padding がある。
  2. overflowプロパティの使用:親要素に overflow: auto または overflow: hidden を設定する。
  3. flexboxやgridの使用:FlexboxやGridレイアウトを使用する。

幅(margin-left および margin-right)についてはどうなのか

ちなみに、水平方向のマージン(margin-left および margin-right)には、マージンの折り重ね(マージンの重複)という現象は存在しません。
そもそも、水平方向のマージンというのは、要素を親要素のどの位置に配置されるかを決定するためのもので、親要素の全体の幅そのものを広げるという効果はありません。
仮に、水平方向のマージン(margin-left および margin-right)が自らの幅とあわせて、親要素の幅を超えるとオーバーフローを起こします。

親要素の幅に影響を与えるのは子要素のwidthやpadding, borderなどのプロパティです。子要素の幅やパディング、ボーダーが親要素の利用可能な幅を超える場合、通常、親要素は子要素を収容するために拡張されます。
ただし、親要素に固定の幅が設定されている、あるいは他のスタイルが適用されている場合はそうならないこともあります。

まとめ

CSSの基本中の基本とも言えるmarginプロパティですが、実はこんなに奥が深かったということを今更のように知りました。

皆さんはいかがでしょう。

もし、私が経験したような現象でお悩みの方がいれば、是非参考にしていただきたいと思います。

Discussion