
CSSのマージンが効かない時の解決法(marginの縮小・折りたたみ)
理想とするCSSmargin解決方法
・子要素間は上あるいは下にのみmarginを設定する
・空要素の場合は1pxでもpaddingなど設定を入れる
・親要素があれば枠線を入れる
・親要素をフレックスコンテナに変換する(display: flow-root;)
・古いブラウザ向けフォールバックなら(overflow: auto;)
・親要素に(display: flex; flex-direction: column;)を入れる事
CSSのmargin設定において思う様に効かないのは、マージンの縮小(折りたたみ)が発生しているからです。
マージンの折りたたみが発生するケースはおおよそ決まっており、CSSの仕様なのでそれ自体を解消する事はできません。
marginが効かない場合の解決法は各種あるので、ルールを決めて情報共有して下さい。
CSSのマージン(margin)とは
CSSで構成される「ボックスモデル」と呼ばれる構造には様々な要素があります。
中でも一番使われるボックスモデルの要素の1つが、ボックスの周囲の透明な領域であるマージン(margin)です。
これによりボックスのコンテンツと他の要素の間に隙間を空ける事ができます。
marginの4つの設定要素
margin-top(上)
margin-right(右)
margin-bottom(下)
margin-left(左)
上記の4つのプロパティは常にCSSの主役であり、様々な状況で頻繁に使われています。
このマージン指定は一見、数値を設定した分だけ間が空いてくれる単純な設定に見えます。
皆さんがよくぶち当たる疑問「マージンが効かない」
しかし皆さん、CSSの記述の際マージンの幅が出せなかった経験ってありませんか?
「40pxのmarginを設定しているのに、40pxの間隔があかない」
これはCSSのマージンに特有のルールがあるため、それを知らない人がよくつまずく部分なのです。
本記事ではこのCSSマージンがなぜ効かないのか、そしてこの特性に対しどのように対応をするべきかをご紹介します。
CSSボックスモデルの構成について
CSSのボックスモデルは下記図のように表す事ができます。
CSSボックスモデルの構造
contents(内容部分:白)
padding(内容部分の外側余白:緑)
border(パディングの外枠線:黄色)
margin(外枠線の余白:青)で構成されています。
マージンに関する事例や話を進める前に一通りおさらいしておきましょう。
細かく言えばまだいろいろな要素指定があるのですが、ここでは慣れ親しんだ上記図をイメージしつつ話を進めていきます。
上の図の青色ゾーン(margin)を作る事ができない場合に焦点を当てています。
マージンの縮小(折りたたみ)とは?
「マージンの縮小」はCSS1から定義されて以来、ユーザーの中で不満とされている往年の課題になっています。
マージンの縮小
これは下部にマージンが設定されている見出しなどのコンテンツの後に、上部マージンがある次の段落要素などが続く場合に起こります。
これらの下と上のマージン幅が「合算されて表現されない現象」をマージンの縮小と呼びます。
具体的には上と下にある余白設定(2つのマージン)のうち「大きい数値のみ」が選択され、もう一方のマージンは折りたたまれてしまいます。
マージンの折りたたみ
この事から「マージンの折りたたみ」とも呼ばれています。
例えば、見出しのmargin-bottomが50pxで、次に来る段落のmargin-topが20pxだった場合、両者を足して70pxではなく、大きい方「50pxのみ」となるのです。
折りたたみのポイント
・常に「大きいマージンを設定している方」が選択されます。
・両方とも同じ50px同士の場合は、50pxのみに留まる事になります。
※基本的に単位はpx「ピクセル」で表現しています。
マージンが効かないパターン
マージンの縮小が発生する(効かなくなる)パターンは以下の状況時に起こります。
・要素が上下に隣接する場合
・完全に空のボックスがある場合
・親要素と最初の子要素の間
・最後の子要素と親要素(終わり部分)の間
これらの事例を一つずつ見ていきましょう。
要素が上下に隣接する場合
通常2つの要素が順番に表示される構図の場合、最初の要素の下マージンは、次の要素の上マージンと一緒に折りたたまれます。
下のレイアウトでは3つのdiv要素があります。
1つ目は上部と下部に50pxのマージン
2番目は上部と下部に20pxのマージン
3番目は上部と下部に5emのマージン
それぞれを設定しています。
設定レイアウト
HTML
<div class="wrapper"> <div class="box1 ex1_1">margin-top: 50px; margin-bottom: 50px;</div> <div class="box1 ex1_2">margin-top: 20px; margin-bottom: 20px;</div> <div class="box1 ex1_3">margin-top: 5em; margin-bottom: 5em;</div> </div>CSS
.wrapper { border: 2px dotted #999999; } .ex1_1 { margin: 50px 0 50px 0; } .ex1_2 { margin: 20px 0 20px 0; } .ex1_3 { margin: 5em 0 5em 0; } .box1 { background-color: #1a62bc; color: white; padding: 20px; border-radius: .5em; }
1番目と2番目の2つの要素間のマージンを見ると、70ではなく50pxになっています。
これは50pxと20pxとを比較して大きい方が選択されているため、20px分マージンが効いていません。
3番目の5emは、2番目の要素の下部にある20pxより大きいため、3番目の要素の上マージンは5emになっています。
要素内が「空」の場合
ボックス要素内に何も入っていない「空」の場合、ボックスに設定された上部・下部のマージンはともに発生しません。
下記レイアウトはBox A・Box B・Box Cの三つが並んでいる例です。
設定レイアウト
HTML
<div class="wrapper"> <div class="box_a">Box A margin: 0px;</div> <div class="empty">Box B</div> <div class="box_c">Box C margin: 0px;</div> </div>CSS
.wrapper { border: 2px dotted #999999; } .box_a, .box_c { background-color: #1a62bc; color: white; padding: 20px; border-radius: .5em; } .empty { margin: 50px 0 50px 0; }
Box AとBox Cの間に本来2番目のボックス要素(empty)があるのですが、全く見えない状態です。
Box AとBox Cにはマージンは設定されていません。
この時見えないemptyの上下マージンは50pxに設定されていますが、上部レイアウトのBox AとBox Cのアイテム間のスペースは100pxではなく50pxになっています。
empty上下のマージンは同じ50pxのため、「ポイント」の項目でお話した通り、片方の50pxのみに縮小されているのです。
要素内に何か入ると復活
この時この2番目のボックス(empty)内に何かの要素指定が入ると(paddingなど)、上下のマージンが復活して折りたたまれなくなります。
親要素と最初の子要素の間、最後の子要素と親要素(終わり部分)の間
これがマージンが効かない例として一番陥りやすいパターンではないでしょうか。
以下に紹介する例ではわかりやすい様に、囲っているwrapperクラスに赤のラインを引いています(borderではありません)。
3つの子要素には全て50pxのマージンがあります。
設定レイアウト
HTML
<div class="wrapper"> <div class="box">Item 1</div> <div class="box">Item 2</div> <div class="box">Item 3</div> </div>CSS
.wrapper { outline: solid 1px #cc0000; } .box { margin: 50px; background-color: #1a62bc; color: white; padding: 20px; border-radius: .5em; }
レイアウトを見ると、最初と最後の要素がそれぞれwrapperの縁に引っ付いていますよね。
要素とwrapperの間にあるべき50pxのマージンが効いていません。
親要素を突破して発生する子要素のmargin
これは厳密には子要素のマージンが親要素を突き抜けて、親の外側に出現している状態になります。
子要素のmarginの範囲
このように子要素のマージンが正常に発生しない場合があり、CSSmarginの仕様となりますので注意が必要です。
マージンの縮小を防ぐ方法
主に以上の様なケースでマージンの縮小(折りたたみ)が発生します。
皆さんも上記のいずれかに遭遇した事があるのではないでしょうか。
ではどのようすればこのようなマージンが効かない現象を防げるのでしょう。
要素が連続する場合のmargin
残念ながら要素の下部マージンと次の要素の上部マージンを合算して表示する方法はありません。
ですので両マージンのうちどちらかを「0」にし、もう片方に合算分のマージン値(例えば50+20で70など)を入れて、調整するしかないと言えます。
場所によって上に入れたり下に入れたりバラバラではなく、統一しておいた方が管理しやすくなりますね。
要素が空のボックスの場合
完全に空の要素のmarginに対しては、ある程度簡単です。
borderやまたはpaddingなど要素が一つでも空要素に適用されれば、上下のマージンが効くようになります。
以下の例ではボックスに1pxのpaddingを追加しています。
設定レイアウト
HTML
<div class="wrapper"> <div class="box">Box A margin: 0px;</div> <div class="empty"></div> <div class="box">Box C margin: 0px;</div> </div>CSS
.wrapper { border: 2px dotted #999999; } .box { background-color: #1a62bc; color: white; padding: 20px; border-radius: .5em; } .empty { margin: 50px 0 50px 0; background-color: #1a62bc; padding: 1px; }
これで空白ボックスemptyの上下に50pxのマージンを復活させる事ができています。
1pxのpaddingが見える様にemptyの背景に色を付けています。
見えない空の要素にも利用用途がある
上記は空の要素をあえて作っていますが、実際のWEBの現場でこの空白の要素というのはよく使われています。
ボックス要素にborderもpaddingもない状態の場合、記述していても基本的に表面には見えません。
この状態はよくWordpressなどのCMSシステムで「空の段落要素」マークアップのため利用される事があります。
この様な段落要素を追加する度にマージンが発生していると、変に他の段落との間ができてしまいますよね。
それを避ける狙いがあるのだと思います。
この様な用途に利用する事を想定されたルールであると考えられます。
親要素と最初の子要素の間、最後の子要素と親要素(終わり部分)の間
親を通して折りたたまれる最初または最後の子要素マージンをきちんと出すための一番の方法は、親要素に枠線を追加する事です。
そうすると子要素のマージンは親の外に出たりせずに内側にきちんと発生します。
設定レイアウト
HTML
<div class="wrapper"> <div class="box">Item 1</div> <div class="box">Item 2</div> <div class="box">Item 3</div> </div>CSS
.wrapper { border: 3px solid #cc0000; } .box { margin: 50px; background-color: #1a62bc; color: white; padding: 20px; border-radius: .5em; }
※outlineは枠線ではないので、先ほどはboxのマージンが内側に発生していませんでした。borderとoutlineは違うものです。
親要素と子要素の間を空けない理由
親要素と子要素との間を空けない仕様にしているのも、上記で述べた空要素と同じ理由で導けます。
裏方の目的でラッピング要素を作るのであれば、表面上にも「存在を裏付ける痕跡も残したくないだろう」という配慮です。
これはテキスト主体のWEBサイトの場合には、非常に理にかなっていると言えます。
要素を使用してデザインをレイアウトする必要がないので、役に立たなくて良いのです。
display: flow-root;の追加
display: flow-root;(ブロックフォーマットコンテキスト)を設定する事でも、マージンの縮小を防げます。
下記のレイアウトを見ると、wrapperに枠線を入れなくても内側にmarginができているのがわかります。
設定レイアウト
HTML
<div class="wrapper"> <div class="box">Item 1</div> <div class="box">Item 2</div> <div class="box">Item 3</div> </div>CSS
.wrapper { outline: 1px solid #cc0000; display: flow-root; } .box { margin: 50px; background-color: #1a62bc; color: white; padding: 20px; border-radius: .5em; }
wrapperに「display: flow-root」を指定すると、子要素のマージンが内側に留まります。
display: flow-root;は、最新ブラウザのみに適用される設定です。
こうすれば親要素に枠線を付けたりする必要が無くなります。
注意
ここでflow-rootの代わりに、wrapperに「overflow: auto;」を設定する事でも同じ効果があります。
CSS
.wrapper { outline: 1px solid #cc0000; overflow: auto; }
しかし一部のケースで不要なスクロールバーが作成される場合もありますので注意しましょう。
フレックスシステムとグリッドシステム
フレックスシステムとグリッドシステムは、子のフレックスとグリッドのフォーマットコンテキストを確立するため、レイアウトをブロック構成する基本の動作が異なります。
この動作の違いによりマージンの縮小・折りたたみは発生しません。
wrapperに「display: flex;」を設定しさらに「flex-direction: column;」を設定しましょう。
設定レイアウト
HTML
<div class="wrapper"> <div class="box">Item 1</div> <div class="box">Item 2</div> <div class="box">Item 3</div> </div>CSS
.wrapper { outline: 1px solid #cc0000; display: flex; flex-direction: column; } .box { margin: 50px; background-color: #1a62bc; color: white; padding: 20px; border-radius: .5em; }
各要素のmarginがwrapperの内側に発生している事がわかります。
さらにこの仕様の場合、隣接するフレックス要素間のマージンは全く縮小されていません。
アイテム間の余白はこれまで説明したいずれとも違い、上部と下部の50px+50pxで合計100pxの余白が出ており、折りたたみはされていないですね。
縮小が発生しない特例
ちなみに、以下の状況の場合はマージンは縮小(折りたたみ)されません。
・アイテムが絶対位置の表示状態にある場合
・アイテムが横並び(フローティング)の場合
しかしこれは特別な場合ですよね。
上の事例のような要素が普通に並んだり親要素で囲む様なケースの方が、遭遇確率は圧倒的に多いはずです。
CSSのmargin解決方法を共有しよう
先ほども説明した通り、マージンの縮小により要素間に設定値通りのマージンが効かない事自体は解消する手立てがありません。
ですのでWEBサイトの設計時に、マージンを処理する一貫した処理方法を決定するべきです。
マージンを設定するルールを設ける
基本的には、要素の上部または下部にのみマージンを設定するとルールを決める事です。
常に余白のある側は余白のない側に隣接する事を念頭にして設計しておけば、折りたたみの問題に直面する必要がなくなります。
例えば子要素間のマージンであれば「上方向のみ・或いは下方向のみ」に設定する、とルール付けするのですね。
ルール付けは必要ですし、なぜそのルールを付けたかの理由は明確にして共有しておくべきです。
情報共有:コメントや記事リンクなどの資産を作る
特に複数スタッフで一つのWEBサイトを構築している場合は、必ずこの情報をチームと共有してください。
マージンの縮小する現象やその理由を知らない人がいると、対応策に困る事ためですね。
例えばCSSコード内に「マージンの縮小に関する設定コメント」を入れておくとチームが助かりますね。
或いはこの記事にリンクして、マージンが縮小する現象に関する知識を共有するのに役立てて頂いても構いません。
まとめ
繰り返しとなりますが、マージンが効かない理由とそのケースを常に把握する事が重要です。
理由とセットで対処法・解決ルールを決めておく必要があります。
そうすればその後の更新や改変の際、それをどのように処理するかを統一管理できますよね。
理想とするCSSmargin解決方法
・子要素間は上あるいは下にのみmarginを設定する
・空要素の場合は1pxでもpaddingなど設定を入れる
・親要素があれば枠線を入れる
・親要素をフレックスコンテナに変換する(display: flow-root;)
・古いブラウザ向けフォールバックなら(overflow: auto;)
・親要素に(display: flex; flex-direction: column;)を入れる事
皆さんのCSSmargin対策の一助になれば幸いです。