HTML5
CSS3
UI
フロントエンド

CSSで「余白」を制してデザイナーに好かれよう

余白を制する者はデザインを制する

らしい。
いろいろとデザインの記事を読んでみても、やはり余白は大事と書かれています。
「余白 デザイン」でググってみても、記事がわんさか出てくるので、やはりデザインにおいて余白は大事みたいですね。

Webデザイナーとフロントエンドが歩み寄るためのPhotoshop豆知識
こちらはLIGの記事です。
これはデザイナー側が歩み寄ろうねという内容の話ですが、フロントエンド側も歩み寄ってもいいはずです。

最近はデザイナーがSketchでデザインし、フロントエンドがZeplinを見ながら実装するというのが主流になりつつあると思いますが、
それでも、デザインを学んできていないのにいきなり余白を気にしろと言われてもなかなか難しいですよね。

印刷物でしたら、デザイナーが作ったものがそのまま印刷されて配布されたり掲示されたりするので良いのですが、Webやアプリだとなかなかそうもいきません。

デザインが、フロントエンドに委ねられる時代になりました。

デザイナーに褒められると嬉しい

そんな中、「きれいにデザインを再現してくれてありがとう!」と言われると、やっぱり、純粋に、嬉しい。
「あなたに実装してもらって本当に良かった!他の人だとそこまできれいじゃないから…」なんて言われた日には、宙を舞うほど嬉しい!
人間は褒められて伸びるもの。やっぱり褒められたい!

なので、デザイナーに好かれるために、褒めてもらうために、フロントエンドがCSSを実装するときにどのように「余白」と付き合っていけばいいのか記しておきたいと思います。
※この記事、結構長くなってしまいました。覚悟の上、お読みいただければ幸いです!

基本的な例を用意

説明をする前に具体的な例があるとわかりやすいので、例を出しておきます。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>sample</title>
  <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <link rel="stylesheet" href="./reset.css">
  <link rel="stylesheet" href="./style.css">
</head>
<body>
<nav class="globalNavigation">
  <ul class="globalNavigation__list">
    <li class="globalNavigation__item"><a href="#" class="globalNavigation__link">HOME</a></li>
    <li class="globalNavigation__item"><a href="#" class="globalNavigation__link">ABOUT</a></li>
    <li class="globalNavigation__item"><a href="#" class="globalNavigation__link">ACCESS</a></li>
  </ul>
</nav>
<div class="postButton">
  <div class="blockHeader">
    <h2 class="blockHeader__title">君も記事を投稿しよう!</h2>
  </div>
  <p class="postButton__main">
    <a href="#" class="postButton__link">記事を投稿する</a>
  </p>
</div>
<div class="articleList">
  <div class="blockHeader">
    <h2 class="blockHeader__title">記事一覧</h2>
  </div>
  <div class="articleList__main">
    <ul class="articleList__items">
      <li class="artcileList__item">
        <a href="#" class="articleList__link">
          <p class="articleList__name">記事1</p>
          <p class="artcileList__description">記事1の内容が入るよ</p>
        </a>
      </li>
      <li class="artcileList__item">
        <a href="#" class="articleList__link">
          <p class="articleList__name">記事2</p>
          <p class="artcileList__description">記事2の内容が入るよ</p>
        </a>
      </li>
      <li class="artcileList__item">
        <a href="#" class="articleList__link">
          <p class="articleList__name">記事3</p>
          <p class="artcileList__description">記事3の内容が入るよ</p>
        </a>
      </li>
    </ul>
  </div>
  <div class="articleList__action">
    <p>
      <a href="#" class="articleList__more">さらに読み込む</a>
    </p>
  </div>
</div>
</body>
</html>
style.scss
@charset "UTF-8";

body {
  background-color: #eaeaea;
  line-height: 1.2;
}

.globalNavigation {
  font-size: 12px;
  padding: 1px 0;

  .globalNavigation__list {
    margin-left: -1px;

    &::after {
      content: "";
      display: block;
      clear: both;
    }
  }

  .globalNavigation__item {
    float: left;
    width: 33.33%;
  }

  .globalNavigation__link {
    display: block;
    margin-left: 1px;
    background-color: #fff;
    text-align: center;
    line-height: 40px;
    color: inherit;
    text-decoration: none;
  }
}

.blockHeader {
  .blockHeader__title {
    padding: 16px;
    font-size: 18px;
    font-weight: bold;
  }
}

.articleList {
  margin-top: 24px;
  background-color: #fff;
  font-size: 12px;
  line-height: 1.4;
  color: #333;

  .articleList__items {
    padding-top: 1px;
  }

  .articleList__link {
    margin-top: -1px;
    display: block;
    padding: 16px;
    border-top: 1px solid #eaeaea;
    border-bottom: 1px solid #eaeaea;
    color: inherit;
    text-decoration: none;
  }

  .articleList__name {
    font-size: 14px;
    font-weight: bold;
  }

  .artcileList__description {
    margin-top: 5px;
    color: #808080;
  }

  .articleList__more {
    display: block;
    line-height: 40px;
    text-align: center;
    color: inherit;
    text-decoration: none;
  }

  .globalNavigation + & {
    margin-top: 0;
  }
}

.postButton {
  margin-top: 24px;
  padding-bottom: 16px;
  background-color: #fff;
  font-size: 14px;

  .postButton__main {
    padding: 0 16px;
  }

  .postButton__link {
    display: block;
    border: 1px solid #0b4;
    border-radius: 10px;
    color: #0b4;
    line-height: 40px;
    text-align: center;
    text-decoration: none;
  }
}

※resetとなるcssはEric Meyerのリセットをつかっています。

これを実際に表示してみると以下のような感じになります。

「ザ・サンプル」と呼べるサンプルができました。
これをもとにお話していきたいと思います。

marginは下ではなく「上」で取る!

サンプルを見ていただければわかると思うのですが、ブロックとブロックの余白を上で取っていると思います。
これはもちろん意図的で、私は滅多なことがない限り「margin-bottom」を使いません。

例えば、こんな指示が降りてきたとしましょう。

「記事の投稿システムが死んだ!直ちに投稿ボタンを削除してくれ!」

これは大変ですね。
それを受けて、さらにデザイナーからこんな指示ももらったとしましょう。

「記事投稿ボタンを削除した場合、ナビゲーションと記事一覧の余白は取らないことになっているのでよろしくお願いしますね!」

うおおおお悠長なことを言ってくれるねーーー今システム壊れているんだよねーーーーーー
と思うはずです。
(こんなこと言うデザイナーもいないとは思いますが…笑 今回はあくまでも例です。)

でも今回のテーマはデザイナーに好かれようなので、そもそもこんな気持ちにならないようにしたいですよね!

なので、今回のサンプルだと、なんとHTMLを消せば余白がもう詰まるようになっています!


実際に<div class="postButton">...</div>をごっそり消したスクリーンショットです。

いい感じになっていますね。デザイナーの要望も叶えられています。
本当に、一切scssをいじっていません。

ポイントはstyle.scssの.articleList内にあるこの記述です。

  .globalNavigation + & {
    margin-top: 0;
  }

cssに出力されると.globalNavigation + .articleListというセレクタになります。(隣接セレクタですね。)
つまり、「グロナビと記事一覧が隣接した時は、余白を取らないでね」と予め指定を入れておいてあったので、スタイルは一切いじらずにHTMLの削除だけで対応ができたわけです。

最初にサイトを作っているときには、グロナビ側のmargin-bottomで余白を取ってしまえば一律余白が空くし、いちいち他のブロックでmarginを取らなくて良いので便利そうです。
しかし、実は保守をしていくとめちゃくちゃ大変なことになるので、それを証明するために、
もし、兄要素でmargin-bottom: 24px;と取っていたとして、対応時にどんなことになるのかをシミュレーションしてみようと思います。

  1. 投稿ボタンを消すことで余白がいらなくなるので、グロナビのmargin-bottomを取ろうとする
  2. でもシステムが直ったらすぐにグロナビのmargin-bottomを復活させなければならない
  3. 上記はめんどくさいので、記事一覧のネガティブマージンで、グロナビのmargin-bottomを相殺することにする
  4. でもシステムが直ったらすぐに、記事一覧のネガティブマージンやめて、投稿ボタンとの間に正規のマージンを取らなければならない
  5. 一旦正規のマージンをコメントアウトしておいて、ネガティブマージンを入れる
  6. ネガティブマージンの指定の付近に// 投稿ボタンが復活したらこの指定は削除するとコメントしておく

闇が垣間見えましたね。。。
結局、3.でめんどくさいと言っていた「ソースコードの復活」は諦めて「復活させる」というコメントアウトを残していますし、そもそも、消される可能性が著しく低いコメントアウトを生み出してしましました。

しかし、margin-topでやっておけば、隣接セレクタなどを駆使して自分で取ったmarginを、状況に合わせて自分で変えたり消したりできるソースコードが書けるようになります。
「ボタンが復活したら…」とかコメントアウトしないで、そもそも「ボタンが消える想定が予め成されたソースコード」が書けるようになります。

しかも、もし、ボタンを削除するタイミングで、グロナビと隣接したときにmargin-topを0にする対応を入れたとしても、ボタンが復活したときにmargin-topを0にするCSSを削除する必要はありません。
ボタンが消えることが想定されているソースコードになっているので、ただHTMLを復活させれば対応完了となります。

毎度、修正の度にHTMLとCSSを触るのではなく、HTMLだけを触って保守ができるようにすればコストも下がりますので、ちょっとだけ残業が減るかもしれないですね。幸せ!

なので、marginは上で取りましょう!

borderがある時のpaddingに注意する!

またまた、こんな指示を受けたとしましょう。

「投稿ボタンの下に利用規約のリンクを入れてほしい」

これを、デザイナーがデザインを作って、エンジニアが実装したとしましょう。


こんな感じになりました。

ソースコードは以下のとおりです。

<div class="postButton">
  <div class="blockHeader">
    <h2 class="blockHeader__title">君も記事を投稿しよう!</h2>
  </div>
  <p class="postButton__main">
    <a href="#" class="postButton__link">記事を投稿する</a>
  </p>
  <p class="postButton__note">
    <span>投稿する際には</span><a href="#" class="postButton__noteLink">利用規約</a><span>に同意してください。投稿する直前の確認画面にて、チェックを入れて投稿をすると同意したことになります。</span>
  </p>
</div>
.postButton {
  margin-top: 24px;
  padding-bottom: 16px;
  background-color: #fff;
  font-size: 14px;

  .postButton__main {
    padding: 0 16px;
  }

  .postButton__link {
    display: block;
    border: 1px solid #0b4;
    border-radius: 10px;
    color: #0b4;
    line-height: 40px;
    text-align: center;
    text-decoration: none;
  }

  .postButton__note {
    margin: 16px 16px 0;
    padding: 8px;
    background-color: #f5f5f5;
    border: 1px solid #ccc;
    font-size: 10px;
    line-height: 1.4;
  }

  .postButton__noteLink {
    color: inherit;
  }
}

.postButtonのソースコードだけにしてあります。
.postButton__noteの部分を追加しました。

実は、このソースコードだと、デザイン上1pxずれている箇所があります。
もうちょっと細かくお話していきます。

グリッドシステムを知る

デザイナーはページ全体を縦に区切ってデザインをしていることが多いので、この縦の区切りに注意してコーディングをする必要があります。

※引用元:https://www.tam-tam.co.jp/tipsnote/html_css/post13057.html

これをグリッドシステムと呼びまして、このグリッドを意識しながらコーディングすると余白を制することができてデザインがきれいになります。

今回、サンプルで作成したこのサイトは、余白を8の倍数で取るようにしています。
(実は間違えてmargin-top: 5px;と書いている箇所があるのですが、グリッドに影響するところではないので、ご愛嬌で…)

なので、一見追加したソースコードを見ると、大体marginもpaddingも8の倍数で取られているので大丈夫なように思えるのですが、、、

padding: 8px;
border: 1px solid #ccc;

この2つの記述によって、文字の開始位置が1pxずれてしまっていることになります。

borderとpaddingの幅は足して考える

今回の指定だと、左右のpaddingを8pxずつ取って、さらにborderを1px入れてしまったことによって、テキストの開始位置が9pxからになってしまうのです。
なので、グリッドの8pxから1pxずれているということになってしまうのです。

つまり、この場合はpaddingを1px引いてあげると、グリッドシステムに沿った形で余白を設定することができます。

padding: 7px;
border: 1px solid #ccc;

marginは外側の余白なのでそのままの数字を入れればよいのですが、borderからコンテンツの内側の余白になるので、borderもpaddingも入れるときには注意が必要です。
(borderが2px、3pxと増えてきたり、こんな注釈ではなくてもっとメインのところの実装をするとより顕著に表れてくるので、その際はより意識した方が良いです。)

この1pxのこだわりをすることで、デザイナーがあなたにメロメロになること間違いなしです!

line-heightの余白に気をつける!

また、投稿ボタンのブロックに着目してみたいと思います。

この部分のソースコードを見てみましょう。

...

.blockHeader {
  .blockHeader__title {
    padding: 16px;
    font-size: 18px;
    font-weight: bold;
  }
}

...

.postButton {
  margin-top: 24px;
  padding-bottom: 16px;
  background-color: #fff;
  font-size: 14px;

  .postButton__main {
    padding: 0 16px;
  }

  .postButton__link {
    display: block;
    border: 1px solid #0b4;
    border-radius: 10px;
    color: #0b4;
    line-height: 40px;
    text-align: center;
    text-decoration: none;
  }

  .postButton__note {
    margin: 16px 16px 0;
    padding: 8px;
    background-color: #f5f5f5;
    border: 1px solid #ccc;
    font-size: 10px;
    line-height: 1.4;
  }

  .postButton__noteLink {
    color: inherit;
  }
}

ソースコードを見ると、.blockHeader__titleにはpadding: 16px;.postButtonには、padding-bottom: 16px;.postButton__noteにはmargin: 16px 16px 0;が入っているので、一律同じ余白が取られているように見えます。

上記を踏まえた上で、タイトルの上下の余白と、利用規約の外側の上下余白をよーーーーーく見比べてみてください。

若干、「君も記事を投稿しよう!」の上下余白のほうが広くないですか…?
本当に、若干。。。
なんとデザイナーは、この余白に気づける才能を持っています。

line-heightによる余白を調査してみる

まだわかりづらいと思いますので、要素検証をしている状態でスクリーンショットを撮ってみました。

青い領域を見てみると、文字の上下に隙間があるのがわかりますか??
この余白が、若干余白が広く見えている原因です。

これ、どうしてこんなことが起きているのかというと、

body {
  background-color: #eaeaea;
  line-height: 1.2;
}

ここの、line-heightの指定が原因です。

タイトルのfont-sizeは18px取られているので、line-heightはその1.2倍取ってくださいね!という指示を入れているので、先ほどの要素検証したところの高さは18px * 1.2 = 21.6px取られていることになります。
実際の見えている文字よりも、余分の高さを取られているので、微妙に余白が多く見えてしまうのです。

line-heightは1にできない

ここで、一つの疑問が生まれます。

「別に、line-heightを1にすればいいんじゃないの??」

そうなんです。
1にすればいいのですが、タイトルでネタバレしているとおり、闇雲に1にするわけにはいかないのです。

line-height: 1でセルから文字がはみ出して見えない。
かなり前の記事ですし、IE6での確認したと書かれていますが、

なぜか内のテキストの上部1~2pxだけ
削れたように見えなくなる事例が発生。

と書かれています。

現在のブラウザではよしなにやってくれるのかもしれませんが、フォントは環境依存するので制御しきれないことも考えると、闇雲にline-height: 1;にするのはデザイン的に危険そうです。

さらに、もう一つ理由があります。
このタイトルの文字が訳あって長くなり、さらにユーザがiPhone SE等の小さい端末で見ると、2行になってしまうというケースは十二分に考えられます。
2行になったときにline-heightが入っていないと、行間がなくて文字と文字がくっついてしまい大変見にくくなってしまいます。

line-height: 1のとき。

line-height: 1.2のとき。

こうしてみると、1.4ぐらい取ったほうが見やすいのかもしれないですね…笑
ただ、やっぱり2行になっているときは行間が少しでも空いている方がいいのがわかるかと思います。

また、これはアップしたスクリーンショットなのでわかりづらいかもしれませんが、line-heightが1だと、素人が作った感じに見えてしまうという欠点もあります。
やはり、ここはline-heightは無くさずに、余白を制していきましょう。

ならpaddingを削ればいい

こうなってしまうと、もう余白を削る場所はpaddingしかありません。
.blockHeader__titlepadding: 16px;をどうにかしていきましょう。

まず、16pxから何px削れば良いかというと、line-height: 1.20.2分だけ削ってやればいいのです。

しかし、単純に0.2分引けばいいというわけではなく、line-heightの特徴を知る必要があります。

line-height文字の上下に行間を取っていきます。
なので、line-heightの行間の高さの中央に文字は配置されることになります
つまり、line-height: 1.2;と指定すると、文字の上下に0.1ずつ余白ができることになります

このことを考慮すると、paddingから引かなければならない値は、、、

タイトルのfont-sizeの0.1倍した値

ということになります。
今までの説明を計算式にすると次のとおりになります。

padding - タイトルのfont-size * (タイトルのline-height - 1) / 2

これをline-heightの値だけ代入して限界まで計算すると

padding - タイトルのfont-size * 0.1

ということになります。
今回はscssで書いているので、変数を活用して、計算式を入れてスタイルを修正しましょう。

.blockHeader {
  .blockHeader__title {
    $font-size: 18px;
    $line-height: 1.2;
    // line-height分の余白を相殺
    // padding-left,padding-rightは計算せずに16pxのままとする
    padding: 16px - $font-size * ($line-height - 1) / 2 16px;
    font-size: $font-size;
    line-height: $line-height;
    font-weight: bold;
  }
}

今回の修正で、.blockHeaderline-heightの指定を足しました。
前のままだと、bodyのline-heightに依存していましたが、あえて指定することでこのブロック内で完結できるようになりました。

また、今回は上下の余白のみの話なので、左右の余白はそのままにしておきます
(逆に適応させるとグリッドシステムが崩れます)
なので、吐き出されるCSSはpadding: 14.2px 16px;となります。
このように吐き出されれば、padding-toppadding-bottomからline-heightの0.1をそれぞれ引いていることになるので、実質0.2分引けたことになります。

上記の修正を適応させるとこんな感じになります。

さっきより余白がいい感じになりましたね!
無事、余白を制することができました!めでたし!

余談ですが、今回はscssで書いているので計算式を入れて、なぜ14.2pxになっているのかをコードベースで伝えることができましたが、
cssで書いているときに、この計算式をとっておきたいがためにcalc()を使うのは良くないと思っています。
calc()はあくまでも単位が違う値をやむを得ず計算するためのもの(追記:やむを得ず、は言いすぎているなと思ったので訂正します。主に単位が違う計算で使うものであり、コード管理する上で計算式を明記するためにcalc()が用意されたわけではない。ですね。)なので、今回のためにcalc()使うべきではないでしょう。
どうしても残しておきたい場合は計算式をコメントアウトで取っておくのが良いかと思います。

さいごに

今回、3つほど余白を制する方法を記事にしました。
あまりこういう記事ってないなと思い、ずっと書いてみたかったので書いてみました。

デザイナーは余白をかなり意識してデザインをしている中で、それをフロントエンドが汲み取り切れないという現実もあるなと思っていたのと、
デザイナーがコーディングしていても、せっかくモックまではきれいなのに、コーディングすると汚くなってしまうというのもよく見てきたという2つの理由から、思い立って書いてみた次第です。
少しでも余白を制したデザインが世の中に溢れるといいなと思っています!

ただ、一点お伝えしておきたいのは、あまりにも余白を気にしすぎてコーディングしてしまうと逆にコードの視認性や保守性が著しく下がってしまうこともあるので、
そこは、やはりデザイナーと密にコミュニケーションを取って、どこまでこだわりたいのか、どこまでは許容値なのかを把握しながらものを作り上げていくのが良いと思います。

デザイナーとフロントエンドがお互い寄り添いながら作っていきましょ〜