HTML/CSS
投稿日:

「ここの余白、もうちょっと詰められませんか?」─ 余白設計で失敗しないCSS実装

こんにちは!株式会社ファストコーディングの働くおかんです。

先日、子どもの運動会のプログラムを作るお手伝いをしたんですが、「文字と文字の間がスカスカで読みにくい」と先生に言われて余白を詰めたら、今度は「ギュッとしすぎて窮屈」と。余白って、多すぎても少なすぎてもダメで、ちょうどいい塩梅が難しいです。

Webサイトのコーディングでも、クライアントから「ここの余白、もうちょっと詰められませんか?」と言われることは本当に多いです。デザインカンプ通りに実装しているのに、実機で見ると「なんか間延びしている」と感じるのは、画面サイズやフォントレンダリングの違いが原因だったりします。

ただ、このとき場当たり的にmarginを削ると、別のブレイクポイントで崩れたり、他のセクションとのバランスが狂ったりするんですよね。余白は「その場で直す」のではなく「設計として管理する」ものだと、何度も痛い目を見て学びました。これ、本当に大事なんです。

今回は、余白に関するクライアントからの修正依頼に振り回されないためのCSS設計を紹介します。

よくある失敗:場当たり的なmargin調整

まず、やりがちなパターンから見てみます。

/* Bad: 場当たり的な余白調整 */
.hero-section {
  margin-bottom: 80px;
}

/* クライアントから「詰めて」と言われて直す */
.hero-section {
  margin-bottom: 48px;
}

/* スマホだと今度は狭すぎると言われる */
@media (max-width: 768px) {
  .hero-section {
    margin-bottom: 60px;
  }
}

/* 別のセクションも同じように個別対応が増えていく */
.about-section {
  margin-bottom: 72px;
}

.service-section {
  margin-bottom: 56px;
}

このパターンの問題は3つです。

  • セクションごとに違う余白がバラバラに散らばり、全体の統一感がなくなる
  • 1箇所を変えると他のセクションとのバランスが崩れ、連鎖的に修正が発生する
  • レスポンシブ対応がセクションの数だけ必要になる

ある案件では、クライアントから「全体的にもう少し詰めてほしい」と言われて、20箇所以上のmargin-bottomを個別に修正したことがあります。夜中に子どもが寝た後、CSSファイルを行ったり来たりしながら「これは設計が間違っている」と気づきました。

解決策:CSS変数で余白をトークン化する

余白をCSS変数(カスタムプロパティ)で一元管理します。「スペーシングトークン」と呼ばれる設計パターンです。

/* 余白トークンの定義 */
:root {
  --space-xs: 8px;
  --space-sm: 16px;
  --space-md: 24px;
  --space-lg: 40px;
  --space-xl: 64px;
  --space-2xl: 96px;

  /* セクション間の余白 */
  --section-gap: var(--space-2xl);
  /* コンテンツ内の余白 */
  --content-gap: var(--space-lg);
  /* 要素間の余白 */
  --element-gap: var(--space-md);
}

/* タブレット */
@media (max-width: 1024px) {
  :root {
    --space-xl: 48px;
    --space-2xl: 72px;
  }
}

/* スマホ */
@media (max-width: 768px) {
  :root {
    --space-lg: 32px;
    --space-xl: 40px;
    --space-2xl: 56px;
  }
}

こうしておけば、クライアントから「全体的にもう少し詰めて」と言われたとき、:rootの値を変えるだけで全ページの余白が一括で調整できます。

/* 使う側はトークンを参照するだけ */
.hero-section {
  margin-bottom: var(--section-gap);
}

.about-section {
  margin-bottom: var(--section-gap);
}

.service-section {
  margin-bottom: var(--section-gap);
}

/* セクション内の要素間 */
.section-title {
  margin-bottom: var(--element-gap);
}

.section-text {
  margin-bottom: var(--content-gap);
}

全セクションが同じ--section-gapを参照しているので、「全体的に詰めたい」ときは:root--space-2xl96px72pxに変えるだけ。20箇所を個別に探して直す作業がゼロになります。

gapプロパティで「隣り合う要素の間」を管理する

FlexboxやGridで子要素を並べるとき、marginの代わりにgapを使うと余白管理がさらにシンプルになります。

/* Bad: margin で間隔を作る */
.card-list {
  display: flex;
  flex-wrap: wrap;
}

.card-list .card {
  margin-right: 24px;
  margin-bottom: 24px;
}

/* 右端のカードに不要なmargin-rightが付く問題 */
.card-list .card:nth-child(3n) {
  margin-right: 0;
}
/* Good: gap で間隔を作る */
.card-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--content-gap);
}

gapを使うと、要素と要素の「間」だけに余白が入ります。最初の要素の前や最後の要素の後ろには余白が付きません。右端のカードの:nth-child問題も発生しません。

レスポンシブ対応もトークンを通じて一括で変わります。--content-gapがスマホで32pxに縮まれば、カード間の余白も自動で32pxになる。個別のメディアクエリは不要です。

margin collapsingの罠

余白設計で最もハマりやすいのが、marginの相殺(margin collapsing)です。

.section-title {
  margin-bottom: 24px;
}

.section-text {
  margin-top: 16px;
}

この場合、見出しとテキストの間の余白は「24px + 16px = 40px」にはなりません。大きい方の「24px」だけが適用されます。これがmargin collapsingです。

意図通りの余白にならず、「40px空くはずなのに24pxしか空いてない」「場所によって余白が違う」という現象の原因は、大抵これなんですよね。

対策は2つあります。

対策1:一方向だけにmarginを付ける

/* margin-bottomだけを使うルール */
.section-title {
  margin-top: 0;
  margin-bottom: var(--element-gap);
}

.section-text {
  margin-top: 0;
  margin-bottom: var(--content-gap);
}

margin-topを使わず、margin-bottomだけで間隔を管理するルールにすれば、相殺を気にする必要がなくなります。

対策2:親要素にFlexboxを使う

.section-body {
  display: flex;
  flex-direction: column;
  gap: var(--element-gap);
}

.section-body > * {
  margin: 0;
}

Flexboxコンテナ内ではmargin collapsingが発生しません。子要素のmarginをリセットしてgapで管理すると、予測可能な余白になります。

個人的には対策2がおすすめです。gapなら要素の追加・削除・並び替えをしても余白が崩れないんですよね。案件で「この段落を消してほしい」と言われたとき、marginだと前後の要素の余白が変わってしまうことがありますが、gapなら影響しません。

クライアントへの説明のコツ

余白の修正依頼を受けたとき、「わかりました、詰めます」で終わらせると、後から別の箇所でも同じ修正が発生します。

案件で効果的だったのは、修正依頼のタイミングで「全体的に詰めるか、この箇所だけ詰めるか」を確認することです。

全体的に詰める場合はトークンの値を変えるだけなので工数は少ない。特定の箇所だけ例外にする場合は個別対応が必要なので工数がかかる。この違いをディレクターに伝えておくと、クライアントへの見積もり説明もしやすくなります。

まとめ

今回は、余白設計で失敗しないCSS実装を紹介しました。

  • 場当たり的なmargin調整は連鎖的な修正を生む。CSS変数でトークン化して一元管理する
  • gapプロパティを使えば、要素間の余白を直感的に管理できる。:nth-childによる調整も不要
  • margin collapsingは「一方向marginルール」か「Flexbox + gap」で回避する
  • 「全体を詰めるか、この箇所だけか」をクライアントに確認することで、修正の手戻りが減る

余白は地味ですが、サイト全体の印象を大きく左右します。「詰めてほしい」と言われたときに1箇所ずつ対応するのではなく、設計として管理できる仕組みを最初に入れておくと、後々の修正コストが大幅に下がります。次のプロジェクトでぜひ試してみてください。

株式会社ファストコーディングでは、CSS設計やレスポンシブ対応のコーディングをお手伝いしています。「余白やレイアウトの設計を見直したい」「既存サイトのCSS設計を改善したい」という方は、お問い合わせフォームからお気軽にご連絡ください。