兄弟要素がずらーっと並ぶレイアウト。記事ページ、LPの説明文、FAQ、仕様書、採用ページの本文。 こういう場面で毎回やりがちなのが「各要素にmargin-bottomを付けて、最後だけ消す」やつです。
やってはいけないわけではありません。でも、実務ではだいたいこうなります。
- どこかの要素だけ余白が違う
- 見出しだけ詰めたいのに、指定が散らかる
- 後から要素が増えたら余白の整合が崩れる
- 最後の要素だけ余白を消す処理が増殖する
そこで効くのが、いわゆるstackパターンです。 「並んだ要素の間だけに余白を入れる」発想に切り替えるだけで、余白の事故がぐっと減ります。
今回はこの一行を主役にします。
.stack > * + *:not(h2 + *) {
margin-block-start: 1.5rem;
}
兄弟要素の縦並びに均一な余白を付けつつ、見出し(h2)の直後だけ余白を詰める。 このちょい気の利いた条件付けが、記事系UIでかなり便利です。
先に結論 何がうれしいのか
この書き方で得られること
- 余白の指定が1カ所に集約される
- 要素が増減しても余白ルールが崩れにくい
- 最後の要素だけ余白を消す処理が不要になる
- 見出し直後だけ詰めるなど、本文の読みやすさを制御しやすい
逆に注意すべきこと
- 子要素の構造が変わると、意図と違う要素にも余白が付く可能性がある
- コンポーネントごとの余白ルールと競合すると、どっちが正か迷う
- h2以外の見出しも詰めたいなら条件を拡張する必要がある
要するに、余白の管理を「各要素に持たせる」から「コンテナがまとめて持つ」へ移す話です。 地味ですが、制作速度と保守性に直撃します。
なぜ止める必要があるのか 余白指定のよくある地獄
地獄1 margin-bottomを各要素に付けて、最後だけ消す
よくある例です。
.item {
margin-bottom: 24px;
}
.item:last-child {
margin-bottom: 0;
}
一見まともに見えますが、要素が増えたり、途中で別の要素が混ざると崩れやすいです。 しかも「最後だけ消す」指定がコンポーネントごとに増え、後で見返すと意味が分からなくなります。
地獄2 要素ごとに余白が散り、調整箇所が増える
pは16px、ulは24px、画像は32px、見出しは40px。 最初は丁寧なつもりが、ページが増えると破綻します。
理由は単純で、余白ルールが分散するほど「全体の整合」を取るコストが上がるからです。 見出しの余白だけ変えたいのに、関連する要素を全部探して回る。 こういう無駄な時間が積み上がります。
地獄3 マージンコラプスで意図しない詰まりが起きる
block要素の上下マージンは状況によって相殺や合流(いわゆるmargin collapse)が起きます。 「margin-topを付けたのに詰まって見える」みたいな現象の原因になりやすいです。
stackパターンは、基本的に「後続要素にmargin-top(正確にはmargin-block-start)を付ける」ので、 この種の事故を避けやすくなります。
stackパターンの基本 .stack > * + *
まずは基本形からです。
.stack > * + * {
margin-block-start: 1.5rem;
}
読み方はこうです。
- .stack の直下の子要素のうち
- 何かの要素(*)の次に続く要素(* + *)にだけ
- 上方向の余白(margin-block-start)を付ける
つまり「先頭以外の子要素に余白を付ける」です。 先頭には付かないので、最初の要素だけ詰める処理も、最後だけ消す処理も要りません。
HTML例
<div class="stack">
<h2>見出し</h2>
<p>本文</p>
<ul>
<li>リスト1</li>
<li>リスト2</li>
</ul>
<p>追記</p>
</div>
この中で余白が付くのは、2つ目以降の子要素です。 h2の次のpにも付くし、ulにも付くし、最後のpにも付く。 だから「縦のリズムが揃う」。
margin-block-startを使う理由
margin-topでも動きますが、margin-block-startの方が方向に依存しません。 日本語サイトでも基本は縦書きではなく横書きなので差を感じにくいですが、 将来的な拡張や、CSSの思想としては論理プロパティを使う方が事故が減ります。
今回の主役 :not(h2 + *)で見出し直後だけ詰める
stackの基本形は便利ですが、記事系UIだと「見出しの直後だけ余白を小さくしたい」ことが多いです。
例えばこういうリズムです。
- 見出しの前は大きく空けたい
- 見出しの直後は少し詰めたい
- 本文同士は一定の間隔で良い
そこで、次の条件を追加します。
.stack > * + *:not(h2 + *) {
margin-block-start: 1.5rem;
}
ここが分かりにくいので噛み砕きます。
- * + * は「先頭以外の子要素」
- h2 + * は「h2の直後にある要素」
- :not(h2 + *) は「h2直後の要素ではないもの」
つまり「先頭以外の子要素に余白を付ける。ただし、h2の直後は除外する」です。
具体例 どこに余白が付くか
<div class="stack">
<h2>A</h2>
<p>Aの本文</p>
<p>補足</p>
<h2>B</h2>
<p>Bの本文</p>
</div>
この場合、A直後のpとB直後のpは除外され、余白が付かない(または別ルールに任せる)。 それ以外のpには1.5remの余白が付く。
見出しの直後を詰めるだけで、読みやすさが上がります。 文章量の多いページほど効きます。
実務で使うならこうする おすすめの拡張パターン
パターン1 h2の前は広く、直後は詰める
多くのページで欲しくなるやつです。
.stack > * + * {
margin-block-start: 1.5rem;
}
/* 見出しの前は広め */
.stack > h2 {
margin-block-start: 3rem;
}
/* ただし見出しが先頭の場合は無駄に空けない */
.stack > h2:first-child {
margin-block-start: 0;
}
/* 見出し直後は詰める */
.stack > * + *:not(h2 + *) {
margin-block-start: 1.5rem;
}
.stack > h2 + * {
margin-block-start: 0.75rem;
}
ポイントは、見出し直後を0にするのではなく、0.75remなど適度に残すことです。 詰めすぎると本文が見出しに貼り付いて息苦しくなります。
パターン2 h2だけでなくh3も同じ扱いにする
記事の中でh2とh3が混在するなら、まとめた方が事故が減ります。
.stack > :is(h2, h3) + * {
margin-block-start: 0.75rem;
}
:is()を使うとセレクタが短くなります。 ただし、プロジェクトのCSSルール次第で採用してください。
パターン3 stack内のリストだけ少し広くしたい
ulやolは情報量が多いので、本文より少し余白を取ると読みやすいことがあります。 この場合は例外を明示します。
.stack > ul,
.stack > ol {
margin-block-start: 2rem;
}
例外は悪ではありません。 「余白のルールが集約されていて、例外が少数である」なら、むしろ保守しやすいです。
メリット stackパターンが実務に効く理由
メリット1 余白の指定が集約される
余白のルールが1カ所に集まると、全体のリズム調整が一気に楽になります。 ページデザインが変わって「余白を全体的に少し詰めたい」となった時も、修正範囲が小さい。
メリット2 要素の増減に強い
「要素が増えたら余白も付く」というルールなので、追加で崩れにくいです。 CMS案件やWordPress案件のように、後から要素が増える現場ほど効果が出ます。
メリット3 last-child調整が不要
余白は「間」に付くので、最後を消すという発想が消えます。 地味にレビューが楽になります。
メリット4 意図が伝わりやすい
.stackという名前の通り「縦に積むコンテナ」です。 命名が素直だと、他の人が触りやすい。 実務ではこれが強いです。
デメリット 注意点 ここでハマる
デメリット1 直下の子要素にしか効かない
セレクタが .stack > なので、直下の子にだけ適用されます。 もし中にラッパーdivが入ると、その内側には余白が付かない。
これは欠点でもあり利点でもあります。 利点は、影響範囲が狭くて安全なこと。 欠点は、構造を変えると期待通りに動かなくなること。
対策はシンプルで、stackを使う箇所は「直下に意味のある要素を並べる」構造に寄せることです。
デメリット2 コンポーネント内のmarginと競合する
例えばカードコンポーネントが自分でmargin-topを持っていると、stackの余白と二重になります。 この問題は「余白の責務」をどこに置くかで解決します。
- コンポーネントは内側のpaddingを持つ
- コンポーネント同士の間隔はstackが持つ
この役割分担がはっきりすると、CSSが落ち着きます。
デメリット3 :not(h2 + *)は要件が増えると複雑になる
h2だけ除外で済むなら美しい。 でも「h2もh3もh4も」「特定クラスの直後も」「画像直後は詰める」など要件が増えると、 :not()が長くなり、読みにくくなります。
この場合は、例外を増やすより「見出し直後の余白を別ルールで上書き」する方が読みやすいことが多いです。
実務のコツ 余白設計の基準を持つ
stackパターンは強いですが、数字が適当だと結局ブレます。 忙しい現場ほど「基準」を持っておくと勝ちです。
おすすめ基準 3段階だけ決める
- 本文同士の余白: 1.25remから1.75rem
- 見出しの前: 本文の2倍前後
- 見出しの直後: 本文の半分から3分の2
これだけでページのリズムが揃います。 細部はデザインに合わせて調整すればOKです。
関連して役立つ情報 ついでに覚えると強い
gapで縦余白を作れるケースもある
子要素がflexやgridで縦に並ぶなら、marginではなくgapで間隔を作る手もあります。
.stack {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
ただしgapは「並べ方」を担うので、もともと通常フローで並んでいる本文には適用しにくいことがあります。 文書構造のまま余白だけ管理したいなら、今回のセレクタ方式が強いです。
見出し直後を詰めるなら :is()を併用すると読みやすい
h2だけでなくh3も詰めたいなら、次の書き方は現場で便利です。
.stack > :is(h2, h3) + * {
margin-block-start: 0.75rem;
}
例外を:notで頑張るより、意図が伝わりやすい場合があります。
stackは名前を統一するとプロジェクトが整う
余白の仕組みを複数作ると混乱します。 stackを採用するなら、同じ目的のコンテナはstackに寄せる。 命名とルールを揃えるだけで、CSSが急に読みやすくなります。
まとめ stackパターンは余白の事故を減らす実務武器
縦の余白を「各要素に持たせる」方式は、規模が大きくなるほど崩れます。 stackパターンは、余白を「間」にだけ付けることで、要素の増減に強くし、最後の調整を不要にします。
さらに
.stack > * + *:not(h2 + *) {
margin-block-start: 1.5rem;
}
のように、見出し直後だけ余白を詰める条件を足すと、記事系UIの読みやすさが上がります。 カンプに書いてなくても、見た人が感じます。 なんか読みやすい。なんか整ってる。つまり、気が利く。
余白は地味ですが、コンテンツの体験そのものです。 今日の1行が、来週の修正依頼を減らします。

