CSSのmarginを使わずにWebサイト構築してみた
はじめに
2023年の6月22日に開催された「Sendai Frontend Meetup #9」で「marginを使わずにWebサイト構築してみた」という内容でLTをしました。
もはや2年近く前の登壇なんですが、記事にしようと思ったけどめんどくさくなって放置した書きかけの記事が残ってました。Cursor使って技術記事を書くのに慣れたくてちょうど良かったので供養します。
CSSでレイアウトを組むとき、display: grid や display: flex の gap プロパティで余白を調整することが多くなりました。
それで、ふと「paddingとgapがあればWebサイト組めるのでは?」と半分ネタと半分本気で考えたのでやってみることにしました。
当日のLTで使ったスライドはこちら。
ルールと素材
ルールは以下
_人人人人人人人人人人人_
> marginを使わない <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
- 元のデザインを(できるだけ)崩さない
- リセットCSSで使っているmarginは許容
- ライブラリで使っているmarginも許容
- 確認ブラウザはMacのChromeのみ(スマホはChromeのエミュレーター)
素材は無料コーディング練習所さんで提供している【上級編】企業サイトのコーディング練習を使用しました。
このサイトでは117個のmarginがヒットしました。
これをすべてmarginなしで再現できるのか試します。

で、結論から言うとできました。
2つヒットしていますがこれはリセットCSSのmarginとコメントなのでセーフです。

というわけでWebサイトを構築するのにmarginは不要です!
とはもちろんならなくて、marginを使わない方がやりやすいケースもあれば難しいケースもありました。
意図せず gap と margin の使い分けの整理になったので紹介します。
marginなし構築でやりやすかったところ
1. 規則的な余白パターン
規則的な余白のパターンでは、gapプロパティの威力を実感しました。
たとえば以下のような3カラムのレイアウトを実装する場合に、元の実装ではカードの右側にmarginを設けて、3番目の要素の右側のmarginを打ち消すために :nth-child(3n) 擬似クラスを使用していました。

marginを使った実装(before)
.c-card-wrapper--col3 {
display: flex;
flex-wrap: wrap;
}
.c-card-wrapper--col3 .c-card {
width: calc(33.33333% - 20px);
margin-right: 30px;
}
.c-card-wrapper--col3 .c-card:nth-child(3n) {
margin-right: 0;
}
gapを使った実装(after)
余分な打ち消し処理が不要になり、コードがすっきりしメンテナンス性が向上します。
.c-card-wrapper--col3 {
display: flex;
flex-wrap: wrap;
gap: 30px;
}
.c-card-wrapper--col3 .c-card {
width: calc(33.33333% - 20px);
}
2. レスポンシブデザインでの柔軟性
画面サイズによって縦並びと横並びが変わるレイアウトでも、gap を利用することでコードをシンプルに保つことができました。

marginを使った実装(before)
余白を設ける方向が変わるので、メディアクエリの中で横並びレイアウトで使用した margin-right の打ち消しが発生します。
.c-media {
display: flex;
}
@media screen and (max-width: 768px) {
.c-media {
display: block;
}
}
.c-media__img-wrapper {
flex: 0 1 40%;
margin-right: 50px;
}
@media screen and (max-width: 768px) {
.c-media__img-wrapper {
margin-right: 0;
margin-bottom: 20px;
}
}
gapを使った実装(after)
flex-direction: column によって縦並びに変更して、メディアクエリの中で gap プロパティの値を変更するだけで、レスポンシブ対応が完結しています。
.c-media {
display: flex;
gap: 50px;
}
@media screen and (max-width: 768px) {
.c-media {
flex-direction: column;
gap: 20px;
}
}
.c-media__img-wrapper {
flex: 0 1 40%;
}
3. 変更に対して強い実装

左右に要素が並ぶレイアウトでも、gap を活用することで変更に強い実装が可能になりました。
marginを使った実装(before)
子要素の日付に対して margin-right を指定していますが、要素を入れ替えたいとなった場合はタグのHTMLに付け替える必要があります。
.p-news-list__heading {
display: flex;
}
.p-news-list__date {
margin-right: 20px;
}
gapを使った実装(after)
gap を使用した場合は、左右の要素が入れ替わっても修正が不要で、子要素に直接スタイルを当てる必要がなくクラス名を減らせ、レイアウト変更時の影響範囲が小さくなります。
.p-news-list__heading {
display: flex;
gap: 20px;
}
marginなし構築で微妙だったところ
1. 文章コンテンツの扱い

記事などの文章コンテンツでは、以下のような課題がありました。
- 見出しや段落など、要素によって余白が異なる
- どのようなHTMLが入ってくるか予測しづらい
- 組み合わせパターンが非常に多い
- エディターによって出力が変わる
このようなケースでは、各要素に適切なmarginを設定する方がシンプルかつ堅牢な実装になることが多いです。
padding や gap を使用して置き換えましたがやりづらかったです。
2. 不規則な余白を持つレイアウト

規則的でない余白を持つレイアウトで無理にgapを使おうとすると、ムダにHTMLの階層が深くなったり、ムダに flex や grid が増えたりして、結果としてかえってコードが複雑になることがありました。
見出し下の余白とボタン上の余白が異なるため、下記のように元々は不要だったHTML要素を追加して対応しました。
<!-- marginを使った実装(before) -->
<div class="l-inner">
... 各要素のHTML
</div>
<!-- gapを使った実装(after) -->
<div class="l-inner">
<div class="p-contact__inner">
... 各要素のHTML
</div>
</div>
2025/4/15追記
XでTAK さんがコメントしてくれましたが grid-template-areas で空グリッドセルを余白にするという手法だとDOMを変更する必要がなく、管理が楽で良さそうです。
2025/6/18追記
上記の手法について詳しい記事が公開されました
3. インライン要素的な挙動

ここは唯一デザインを再現できなかったところです。
display: inline のようにテキストが長くなった場合に、そのまま次の行に流すことができませんでした。
4. autoを使いたい場面

margin-xx: auto を使いたいというケースは意外に多いと思います。
今回はコンテンツを中央寄せにしたい場合でしたが、素直に margin: 0 auto のように書きたいというケースがありました。
/* marginを使った実装(before) */
.l-section {
padding: 70px 0;
}
.l-inner {
max-width: 1140px;
margin: 0 auto;
padding: 0 15px;
}
/* flexのalign-itemsを使った実装(after) */
.l-section {
padding: 70px 0;
display: flex;
flex-direction: column;
align-items: center;
}
.l-inner {
width: 100%;
max-width: 1140px;
padding: 0 15px;
}
結論
それぞれの特性を理解して使っていきましょう
Discussion