CSSにおけるスペースの与え方、paddingやmarginなどを使った実装テクニックを詳しく解説

Webページやスマホアプリの実装で悩ましいのが、スペースの与え方です。マージンとパディングをどう使うか、margin-topとmargin-bottomのどちらにすべきか、グリッド間のスペース、表示デバイスによって異なるスペース、大規模プロジェクトにおけるスペースの管理方法など、CSSにおけるスペースについて解説します。

CSSにおけるスペースの与え方、paddingやmarginなどを使った実装テクニックを詳しく解説

Spacing in CSS
by Ahmad Shadeed

下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。

はじめに

2つ以上の要素が近接している場合、ユーザーはそれらが何らかの形で関連していると見なします。複数のデザイン要素をグループ化するには、それらの間にあるスペースの量でお互いにどのように関連しているかを決定することができます。

スペースが適切でないと、どのアイテムが関連していて、どのアイテムが関連していないか判断することが難しくなり、ユーザーはページを読み飛ばすかもしれません。

この記事では、CSSにおけるスペースについて知っておくべきこと、スペースを実現するためのさまざまな方法、paddingやmarginをどのように使用するかについて解説します。

CSSにおけるスペースの種類

CSSにおけるスペースには2つの種類があり、1つは要素の外側(outside)、もう1つは要素の内側(inside)です。この記事ではこれらを単にouter(外側)inner(内側)と呼ぶことにします。

例えば、ある要素があり、要素の中のスペースはinner(内側)で、要素の外のスペースはouter(外側)となります。

inner(内側)スペースとouter(外側)スペース

inner(内側)スペースとouter(外側)スペース

CSSでは、これらのスペースを下記のように与えることができます。

inner(内側)スペースにはpadding、outer(外側)スペースにはmarginを使用しました。とてもシンプルですね。しかし、多くのディテールや子要素を持つコンポーネントを扱う場合は、これがどんどん複雑になります。

マージン -outer(外側)スペース

マージンはある要素と別の要素の間にスペースを与えるために使用されます。例えば、上記の例では2つの積み重ねられた要素の間に垂直方向のスペースを与えるために、margin-bottom: 1rem;を使用しています。

マージンは4つ異なる方向(上・右・下・左)に与えることができます。実際の使用例に触れる前に、基本的なコンセプトを理解しておくことが重要です。

マージンの相殺

マージンの相殺を簡単に説明すると、垂直方向に2つのマージンがあり、1つのマージンがもう1つのマージンより大きい場合に発生します。その場合、大きいマージンの方が適用され、もう1つの小さいマージンは無視されます。

マージンの相殺

マージンの相殺

この例では上の要素にmargin-bottom: 50pxがあり、下の要素にmargin-top: 30px;があります。マージンの相殺が起こるので、スペースは50pxになります。

マージンの相殺を避けるためには、一方向のマージンを使用することをお勧めします。CSS Tricksの調査によると、61%がmargin-topよりもmargin-bottomを好むという結果が出ています。

修正方法は下記をご覧ください。

:notセレクタを使用すると、最後の子要素からマージンを削除して、簡単に不要なスペースを避けることができます。

マージンの相殺でもう1つ例を見てましょう。
親と子で、子の上に50pxのスペースを与えたいとします。

しかし、このCSSでは期待通りに表示されません。

マージンの相殺: 子と親の場合

マージンの相殺: 子と親の場合

子要素が親要素の上部にくっついていることに注目してください。これはマージンの相殺によるものです。W3Cによると、この問題の解決策にはいくつかあります。

  • 親要素にborderを追加する。
  • 子要素のdisplayをinline-blockに変更する。

より簡単な解決策は、親要素にpadding-topを追加することです。

親要素にpadding-topを追加

親要素にpadding-topを追加

負のマージン

マージンは4方向に使用でき、効果的に使用すると非常に便利です。例えば、下記のような場合を想定してみてください。

親にpaddingがあるけど、子は親にくっつけたい

親にpaddingがあるけど、子は親にくっつけたい

親要素にはpadding: 1rem;があり、これにより子要素は上下左右にオフセットが発生しています。しかし、子要素は親要素にくっついている必要があります。ここで負のマージンの出番です!

子要素は親要素にくっつく

子要素は親要素にくっつく

マージンの相殺について詳しく知りたい方は、下記を参考にどうぞ。

パディング -inner(内側)スペース

パディングは要素のinner(内側)にスペースを与えるために使用されます。

例えば、リンクの周辺のスペースを広げるために使用することができ、これによりリンクのクリック可能な領域が大きくなります。

パディングを使用してクリック可能な領域を大きくする

パディングを使用してクリック可能な領域を大きくする

パディングが機能しない条件

垂直方向のパディングは、spanaのようなdisplay: inlineを含む要素では機能しないことに注意してください。インライン要素にパディングを与えても、その要素には影響せず、他のインライン要素を上書きします。

もしspanなどに垂直方向のパディングを与えたい場合は、displayプロパティを変更する必要があります。

CSS GridのGap(溝)

CSS Gridでは、grid-gapプロパティで列と行の間にスペースを簡単に与えることができます。grid-gapは、grid-row-gapgrid-column-gapのショートハンドです。

CSS Gridにおける列と行の間のスペース

CSS Gridにおける列と行の間のスペース

ショートハンドを使用しない場合は、下記のようにも記述できます。

CSS FlexboxのGap(溝)

gapはCSS GridとFlexboxの両方に使用されるプロパティとして提案されています。しかし、執筆時点では、Firefoxでしかサポートされていないのが難点です。
参考: gapのサポートブラウザ

@supportsでサポートされているかどうかを検出して、使用するという使い方はできません。
参考: testing support of properties and values with partial implementations

早く使いたいという人は、Chromeへの導入に投票をお願いします。

CSSのポジション

これは要素間にスペースを空ける直接的な方法ではないですが、デザインによってはその役割を果たします。例えば、絶対配置された要素で、親要素の左端と上端から16px離して配置したい場合です。

下記をご覧ください。
親要素の左上からアイコンを離したい場合、下記のようなCSSが使用できます。

親要素の左上からアイコンを離して配置

親要素の左上からアイコンを離して配置

CSSのプロジェクトで使用するスペースの実装テクニック

このセクションでは、CSSのプロジェクトで日常的に使用するスペースのさまざまな実装方法について解説します。

ヘッダ コンポーネント

ヘッダ内の各要素にスペースが必要

ヘッダ内の各要素にスペースが必要

ヘッダにはロゴ・ナビゲーション・ユーザーが配置されています。CSSでこれらのスペースをどのように空ければよいかを解説します。まずは、スケルトンのモックアップを実装します。

スペースをパディングとマージンに分類

スペースをパディングとマージンに分類

まず、ヘッダ内のコンテンツが端にくっつかないようにするために、ヘッダの左右にパディングを与えます。

ナビゲーションでは各アイテムの上下左右にパディングが必要で、これによりクリック可能な領域が大きくなり、アクセシビリティが向上します。
参考: ボタンやアイコンやナビゲーションなど、クリック・タップ可能な領域のサイズを広くする実装方法のまとめ

また、各アイテム間のスペースはmarginを使用するか、<li>displayinline-blockに変更します。インライン ブロック要素は要素を文字のように扱うため、兄弟要素の間に少しスペースを追加します。

最後に、アバターとユーザー名の左側にマージンを与えます。

多言語サイトを構築する場合は、margin-leftではなく、論理プロパティを使用することをお勧めします。

セパレーターの左右のスペース

セパレーターの左右のスペースが一貫していない

セパレーターの左右のスペースが等しくないことに注目してください。この原因は、ナビゲーションのアイテムに特定の幅が定義されておらず、代わりにパディングがあるためです。その結果、ナビゲーションのアイテムの幅はコンテンツに基づいています。
解決策は、いくつかあります。

  • ナビゲーションのアイテムの最小幅を定義する。
  • 水平方向のパディングを増やす。
  • セパレーターの左側にマージンを追加する。

最も優れた解決方法は、3番目です。
ここでは、margin-leftを追加します。

グリッドにおけるスペース- Flexboxの場合

グリッドレイアウトは、スペースが最もよく使用される例の一つです。

グリッドにおけるスペース

グリッドにおけるスペース

スペースは、グリッドの列と行の間になければなりません。下記のHTMLを例に見てましょう。

私は通常、コンポーネントをカプセル化しておいて、それらにマージンを追加しないようにしています。そのために、カードコンポーネントが配置される要素grid__itemがあります。

上記のCSSでは、各列に4つのカードが配置されます。それらの間にスペースを追加するには、下記のようにします。

calc()関数を使用することにより、マージンはflex-basisから差し引かれます。この解決方法はそれほど簡単ではありません、私が好むのは次の通りです。

  • グリッドアイテムにpadding-leftを追加する。
  • グリッドの親にpadding-leftと同じ値の負のmargin-leftを追加する。

この解決方法は何年か前にCSS Wizardryで知りました(記事タイトルは忘れたので、ご存じの方はお知らせください)。

負のmargin-leftを使用した理由は、1枚目のカードにはpadding-leftがあるのですが、実際には必要ないからです。そのため、ラッパーを左にプッシュして、その不要なスペースをキャンセルします。

他にも似たようなコンセプトとして、両側にパディングを与えて、マージンを負にするというものがあります。Facebookの事例を見てみましょう。

FacebookのUI

FacebookのUI

グリッドにおけるスペース- CSS Gridの場合

CSS Gridでは、grid-gapを使用して簡単にスペースを追加することができます。また、グリッドアイテムの幅や下マージンを気にする必要もありません。CSS Gridがすべてをあなたのために代行してくれます!

以上です!
簡単で分かりやすいと思いませんか?

デバイスに応じたスペース

CSS Gridで特に気に入っているのは、grid-gapが必要な時だけ適用されることです。下記のモックアップを見てみてください。

デバイスに応じたスペース

デバイスに応じたスペース

2枚のカードで構成されたセクションです。スマホでは1枚目の下に、デスクトップでは2枚の間にスペースを配置します。CSS Gridがなければ、こういった柔軟性を実現することはできません。
まずは、下記のCSSを見てみてください。

一応実現はできていますが、いまいちです。
では、次のCSSはどうでしょう?

はるかに簡単です!

下マージンの処理方法

コンポーネントが積み重なっており、それぞれには下マージンがあります。

下マージンの処理方法

下マージンの処理方法

最後の要素にマージンがあることに注目してください。マージンは要素間のみ存在させたいので、これは正しくありません。
これを解決する方法がいくつかあります。

解決方法1: :notセレクタ

解決方法2: +隣接兄弟結合子

解決方法1は魅力的ですが、以下の欠点があります。

  • CSS固有の問題が発生します。:notセレクタが使用されるまで、上書きされません。
  • 複数の列がある場合は機能しません。下記をご覧ください。
複数の列がある場合

複数の列があると、:notセレクタは機能しない

解決方法2には、CSS固有の問題はありません。ただし、1つの列でのみ機能します。

よりよい解決方法は、親要素に負のマージンを追加し、不要なスペースをキャンセルすることです。

要素を下のスペースと等しい値で下にプッシュします。兄弟要素と重なるため、マージン値を超えないように注意してください。

カード コンポーネント

カード コンポーネントのスペースについてすべて解説すると、一冊の本になってしまうかもしれません。一般的なパターンに注目して、どのようにスペースを適用すべきか見てみましょう。

カード コンポーネントにおけるスペース

カード コンポーネントにおけるスペース

このカードにおけるスペースをマージンとパディングに分けてみました。

スペースをマージンとパディングに

スペースをマージンとパディングに

パディングは、その中のすべての子要素にオフセットを追加します。
次に、すべてのマージンを追加します。

レーティングとカードメタの区切り線として、ボーダーを追加します。

おっと! 親要素.card__contentにパディングが適用されているため、ボーダーが端にくっついていません。

ボーダーが端までくっついていない

ボーダーが端までくっついていない

これは、負のマージンで修正します。

おっと! ボーダーは端にくっつきましたが、今度はコンテンツも端にくっついてしまいました、、、

コンテンツが端にくっついた

コンテンツも端にくっついた

これを解決するには、コンテンツの左右にパディングを与えるだけです。

完成

これで完成です

記事コンテンツ

これは非常に一般的な使用例だと思います。
記事コンテンツはCMSから取得されるため、要素のclassを追加することができないマークダウンファイルから自動的に生成されます。

見出し、段落、画像が混在する例を考えてみましょう。

記事コンテンツの見栄えをよくするためには、スペースを統一して、慎重に使用する必要があります。スタイルはtype-scale.comを参考にしました。

記事コンテンツのスタイル

記事コンテンツのスタイル

<p>の後に見出し(例えば「Types of Spacing」)が続く場合は、<p>margin-bottomは無視されます。あなたが思った通りです、これはマージンの相殺です。

万が一に備えてのマージン

私はこれを「Just In Case Margin(万が一に備えてのマージン)」と呼んでいます。下記のモックアップをご覧ください。

上: 要素間にスペースが十分にある、下: スペースがない

上: 要素間にスペースが十分にある、下: スペースがなくなる

要素同士が近接していると、見栄えがよくありませんね。これはFlexboxで構築してあるのですが、CSS Tricksで「整列シフト ラッピング」と呼ばれるものです。

ビューポートのサイズが小さくなると、要素は積み重なります。

ビューポートのサイズが小さい場合

ビューポートのサイズが小さい場合

解決しなければならないのは、2つのアイテムが隣接したままで、スペースがゼロになっている中間のデザイン状態です。この場合、要素にmargin-rightを追加することをお勧めします。これにより、2つのアイテムは接触することがなくなり、flex-wrapが期待通りに動作します。

マージンを右に追加して解決

マージンを右に追加して解決

CSSのwriting-mode

MDNによると、

CSSのwriting-modeプロパティは、テキストの行を水平または垂直にし、ブロックの進行方向を設定します。

writing-modeが異なる要素でマージンを使用する場合、どのように動作するか考えたことがありますか?
下記の例をご覧ください。

水平と垂直が混在する要素

水平と垂直が混在する要素

.titleは90度回転しており、画像との間にスペースが必要です。この場合、スペースはwriting-modeに基づいて機能します。

日常的に使用する例は、これで十分だと思います。
次に、コンポーネントにおけるスペースのコンセプトを紹介します。

コンポーネントのカプセル化

大規模なデザインには、非常に多くのコンポーネントが使用されています。それらのコンポーネントに直接マージンを追加するのは理にかなっているでしょうか?

ボタンを例に見てみましょう。

ボタンのコンポーネント

ボタンのコンポーネント

ボタン間のスペースは、どうすればよいでしょうか?
左または右のボタンにスペースを追加すべきか、もしかしたら、隣接兄弟セレクタを使用する方法もあります。

これはよくないですね。
ボタンが1つしかない場合はどうなるでしょうか? また、スマホの表示で積み重なった場合はどうなるでしょうか? たくさんの場合を考える必要があります。

抽象化されたコンポーネントの使用

上記の問題の解決策は、他のコンポーネントをホストすることを目的とした抽象化されたコンポーネントを使用することです。Max Stoiberによると、マージンを管理する責任を親要素にする、です。
このコンセプトで解決してみましょう。

ボタンのキャプチャ

ボタンにラッパーを追加して、各ボタンが独自の要素でラップされていることに注目してください。

以上です!
さらに、このコンセプトをJavaScriptフレームワークに適用するのはかなり簡単です。
例えば、

使用しているJavaScriptツールは、各アイテムを独自の要素でラップする必要があります。

スペーサーのコンポーネント

スペースではなく、スペーサーについてです。
ここではマージンを回避するコンセプトとして、マージンの代わりにスペーサーを使用する方法について説明します。

例えば以下の制約を考慮して、セクションの左に24pxのマージンが必要な場合を考えてみましょう。

  • マージンはすでに構築されたデザインシステムであるため、コンポーネントに直接使用することはできません。
  • フレキシブルでなければなりません。このスペースはXページにはあるかもしれませんが、Yページにはない場合もあります。

これはFacebookの新しいUIデザインでCSSを調べているときに、気付きました。
参考: Facebookの新しいUIデザインで見つけたCSSのテクニックのまとめ

Facebookの新しいUIデザイン

Facebookの新しいUIデザイン

このスペースはwidth: 16px;のインラインスタイルがある<div>で実装されています。目的は、ラッパーと左端の間にスペースを追加するためのものです。
React playbookによると、

しかし、リアルの世界ではコンポーネントをページやシーンに構成するために、コンポーネントの外側にスペースを空ける必要があります。ここでマージンがコンポーネントのコードに忍び込むことになります。

私も同感です。
大規模なデザインシステムの場合、コンポーネントにマージンを追加し続けることはスケーラブルではありません。追加すると、最終的に不気味なコードになってしまいます。

スペーサーコンポーネントの課題

スペーサーコンポーネントのアイデアを理解したので、今度は予想される課題について詳しく見てましょう。

  • スペーサーコンポーネントは、親の内部でどのように幅や高さを確保しますか? 水平レイアウトと垂直レイアウトでどのように機能しますか? 例えば、スタック内のスペーサーと左スペースを追加するスペーサー。
  • 親のdisplayのタイプ(flex, grid)に基づいてスタイルを定義する必要があります。

この問題について、1つずつ見てましょう。

スペーサーコンポーネントのサイズ変更

スペーサーは、さまざまなバリエーションや設定を用意できます。私はJavaScriptデベロッパーではありませんが、Props(小道具)と呼ばれるものです。
styled-system.comの例を見てましょう。

ヘッダとセクションの間にスペーサーがあります。

今度は少し異なりますが、ヘッダのロゴとナビゲーションの間に自動スペースを作成するスペーサーです。

これをCSSで実装するには、justify-content: space-betwee;を使えば簡単だと思うかもしれまえん。しかし、デザインを変更する必要がある場合はどうなりますか? その場合はスタイルも変更する必要があります。

下記をご覧ください。
フレキシブルに実装されているのが分かると思います。

まぁ、その場合はスタイルを変更すればいいのですが、それはフレキシブルですか?

サイズ変更の場合、スペーサーはその親に基づいてサイズを変更できます。上記の場合では、CSSでflex-grow: 1;を計算するgrowという小道具を作成できます。

疑似要素の使用

私が考えた別のアイデアは、スペーサーを疑似要素で実装することです。

スペーサーは分離された要素ではなく、疑似要素で追加するオプションがあってもいいのではないでしょうか?
例えば、

今まで、私はプロジェクトでスペーサーコンポーネントを使用していませんでしたが、使用する日を楽しみにしています。

CSSの数学関数: Min(), Max(), Clamp()

動的なマージンを設定することは可能でしょうか?
例えば、ビューポートの幅に応じて最小値と最大値を持つマージンを設定できるでしょうか。答えは、YESです。CSSの数学関数はFirefox 75でサポートされるようになったので、これで主要ブラウザのすべてでサポートされています。
参考: CSSの数学関数のサポートブラウザ

グリッドを例にして、動的なスペースをどのように使用できるか見てみましょう。

min(2vmax, 32px);は、グリッドのギャップは2vmaxで32pxを超えてはいけない、を意味します。

動的なマージン

このような柔軟性を持たせることは本当に素晴らしいことで、より動的で柔軟なレイアウトを構築するための多くの可能性を与えます。

以上です。
コメントや提案があれば、@shadeed9までお願いします。

sponsors

top of page

©2024 coliss