CSSのgap
プロパティーは余白を指定できる新しめの手法です。余白といえば、margin
プロパティーやpadding
プロパティーを思い浮かべる方が多いと思いますが、CSS GridやFlexboxでgap
プロパティーを使うと柔軟にレイアウトを組めます。もともとgap
プロパティーはCSS Gridでのみ利用できていましたが、macOS Safari 14.1およびSafari on iOS 14.6からFlexboxでもgap
プロパティーが使えるようになりました。
この記事ではFlexbox、CSS Gridで利用できるgap
プロパティーを活用したレイアウト手法とそのメリットを紹介します。今までmargin
プロパティーで要素間の余白を調整していたものもgap
プロパティーで柔軟に対応できる場面もあります。なお、下記サンプルでmargin
プロパティーは一切使っていません。
なお、この記事では要素間の空白部分のことをmargin
の訳語としての「余白」との混乱を避けるため、「アキ」と呼ぶことにします。
gapプロパティーの使い方
gap
プロパティーはFlexboxおよびCSS Gridで要素間のアキを設定できます。
.hoge {
display: flex;
gap: 30px 20px;
}
.fuga {
display: grid;
gap: 30px 20px;
}
gap
プロパティーは1つもしくは2つの値をとり、それぞれ行間・列間のアキを設定します。1つ指定の場合は行間・列間を同一の値で指定します。2つの場合は、1つ目が行間、2つ目が列間の値になります。gap
プロパティは一括指定プロパティーなので、行間・列間を個別に指定する場合はそれぞれrow-gap
、column-gap
を使います。
CSS Gridでのgap
プロパティーの使い方などは『CSS Grid Layout入門』の記事に詳しく解説されています。
marginを使わないページデザイン
サンプルのページはデスクトップレイアウト、モバイルレイアウトともにmargin
プロパティーを使っていません。gap
プロパティーおよびpadding
プロパティーのみを用いて要素をレイアウトしています。いくつかmargin
プロパティーを使わない手法をピックアップして解説します。
全体のレイアウトについて
まず全体のレイアウトについて見ていきます。大きく分けてヘッダー、メインコンテンツ、フッターとモバイル版のハンバーガーボタン(デスクトップ版では非表示)で構成されています。さらにメインコンテンツは7つセクションに分かれています。ヘッダー・コンテンツ・フッターはCSS Gridを使っています。(細かい内部のHTMLは省略しています)
<body class="global-layout">
<header class="global-layout__header header">...</header>
<button class="global-layout__hamburger-button hamburger-button">
...
</button>
<main class="global-layout__contents contents-layout">...</main>
<footer class="global-layout__footer footer">...</footer>
</body>
.global-layout {
display: grid;
grid-template: "header" auto "contents" 1fr "footer" auto/100%;
min-height: 100vh;
}
.global-layout__header {
position: fixed;
top: 0;
left: 0;
grid-area: header;
width: 100%;
}
.global-layout__contents {
grid-area: contents;
}
.global-layout__footer {
grid-area: footer;
}
縦に並んだレイアウトでもCSS Gridで構成するメリットととして、コンテンツの高さが小さい時にフッターが上がってきてしまう現象を防げる点です。CSS Gridでmin-height: 100vh
で最小高さとして画面の高さを設定し、コンテンツ部分の高さを1fr
と設定することで100vh
からヘッダーとフッターの高さを引いた残りがコンテンツ部分の高さとして確保されます。これによりフッターは最低でも画面の一番下に配置されます。細かいCSS Gridやfr
のしくみについては『CSS Grid Layout入門』をご参照ください。
メインコンテンツは7つのセクションが縦に並んで、各セクションは120px
ずつ離れています。これはFlexboxとgap
を組み合わせて次のように実装できます。
<main class="global-layout__contents contents-layout">
<section class="main-visual">...</section>
<section class="missions">...</section>
<section class="service">...</section>
<section class="news">...</section>
<section class="works">...</section>
<section class="company">...</section>
<section class="credits">...</section>
</main>
.contents-layout {
display: flex;
flex-direction: column;
row-gap: 120px;
padding-bottom: 120px;
}
display:flex
とflex-direction:column
で縦に並べています。Flexboxといえば横並びのイメージが強いですが、flex-direction
プロパティーを用いれば縦方向へのレイアウトも可能です。これでFlexboxによるgap
プロパティーが使えるようになり、row-gap:120px
を設定すると各セクション間に120px
ずつのアキができます。gap
プロパティーを使うと個々にmargin-bottom
やmargin-top
を設定しなくても一括で設定でき便利です。
ヘッダー
デスクトップ版のヘッダーは大きく分けてロゴ部分とナビゲーション部分に分けられます。それぞれ両端に寄っているので次のようなHTMLとCSSで配置します。
<header class="global-layout__header header">
<h1>HOGE</h1>
<nav>
<ul class="header__navigation">
<li>
<a href="#">ABOUT</a>
</li>
<li>
<a href="#">WORKS</a>
</li>
<li>
<a href="#">COMPANY</a>
</li>
<li>
<a href="#">CONTACT</a>
</li>
</ul>
</nav>
</header>
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 32px;
}
つづいて、ナビゲーション部分に注目してみます。ナビゲーションはデスクトップ版では横並びになっています。ここでもFlexboxを用いて並べています。
.header__navigation {
display: flex;
column-gap: 32px;
align-items: center;
justify-content: flex-start;
list-style: none;
}
ここでgap
プロパティーの登場です。横並びの要素間のアキはcolumn-gap
プロパティーによって実現しています。
従来であれば<li>
要素に対してmargin-left
プロパティーと1つ目の要素に余計なmargin-left
プロパティーがつかないよう:first-child
擬似クラスを用いていたかと思います。
▼従来的な方法
.header__navigation li {
margin-left: 32px;
}
.header__navigation li:first-child {
margin-left: 0;
}
Flexboxとgap
プロパティーを組み合わせればたったcolumn-gap: 32px
の1行で済みます。
Service部分
Serviceのセクションでは画像とテキストが横並びになっています。ここはそれぞれ幅50%の要素をdisplay: flex
で並べているだけなのでそこまで真新しい手法は使っていません。なお偶数行のみflex-direction
プロパティーで反転させています。
.alternating-layout__item {
display: flex;
}
.alternating-layout__item:nth-child(2n) {
flex-direction: row-reverse;
}
テキスト部分は上下左右中央に寄せていますが、これは次の2行でできます。
.alternating-layout__description {
display: grid;
place-items: center;
}
同じことはdisplay:flex
とjustify-content:center
、align-items:center
でもできますが、1行少なくかけるのでちょっとだけ省力化できます。
Works部分
Works部分は3つカードを並べるレイアウトとカード内部のレイアウトにdisplay:flex
を使用しています。まずは3つ並べるコードをみていきます。
.card-list {
display: flex;
gap: 32px 24px;
justify-content: center;
}
さきほどのナビゲーション部分と同様にカード間のアキはgap
プロパティーを使って実現しています。今回はgap
プロパティーに2つ値を与えて、row-gap
プロパティーも設定しています。現状横に3つ並ぶ場合にはrow-gap
プロパティーの影響はありませんが、将来的に2段になった場合に、上の段とアキができるよう設定しています。
カード自体のレイアウトは次のようなコードになっています。
.card {
display: flex;
flex-direction: column;
row-gap: 8px;
}
Flexboxの向きを縦方向にし、画像とタイトル、タイトルと説明文の間をrow-gap
プロパティーで空けています。縦方向のdisplay:flex
を用いると行数の変化などにも柔軟なレイアウトが可能になります。
モバイル版について
モバイル版のレイアウトは全般的に横並びだったものを縦並びにしています。Flexboxを用いているので縦並びにしたい場合はflex-direction
プロパティーの値をcolumn
に変更することで対応できます。アキもrow-gap
とcolumn-gap
プロパティー(あるいはgap
プロパティー)の値を調整します。一括指定のgap
プロパティーを用いれば、モバイル版ではPC版のmargin-left
プロパティーをキャンセルして新たにmargin-top
プロパティーを与えるような書き方をしなくて済むのでシンプルになります。
gapの利点
gap
プロパティーを使った実践的なレイアウトを紹介しましたが、gap
プロパティーで要素間のアキをつくるメリットをあげます。
親が子要素間の余白を制御できる
margin
プロパティーによるアキの制御と比べて大きな違いは子要素間のアキの制御を、Flexboxなどを使ってレイアウトを行っている親自身が制御できる点です。配置された子要素側はmargin
などの他要素のレイアウトに影響を与えるプロパティーを持つ必要がなく、自身のレイアウトに専念すれば良くなります。
このメリットはコンポーネント指向なCSS設計やフレームワークにおいて非常にメリットになります。「コンポーネントにmarginを持たせるな」というような言説を聞いたことないでしょうか?margin
プロパティーは他の要素へ影響を与えるので意図しないレイアウトの崩れやCSSが複雑になる原因になります。gap
プロパティーによるアキの制御はこれを実現できます。
本記事のデモでもレイアウトに関わるCSSは階層状に制御しています。
Adobe XDやFigmaといったデザインツールの親和性が高い
Adobe XDには「スタック」、Figmaには「Auto layout」という機能があります。この挙動はFlexboxとgap
プロパティーを組み合わせたもの近いです。デザインデータと同じような感覚でレイアウトができ、デザイナーの意図も汲み取りやすいでしょう。
リスト項目の:last-childのような例外処理を省ける
ナビゲーションのレイアウト部分でも取り上げましたが、margin
プロパティーを使った場合は不要なアキが発生する場合があります。それを回避するために:last-child
や:nth-child
などの擬似クラスを使った例外処理をすることもありますが、gap
プロパティーが要素と要素の間を制御するのでそのような記述をせずに済みます。
marginの相殺など考えなくてよい
margin
プロパティー同士が隣接した場合「marginの相殺」という挙動があります。さきほどのコンポーネントの話と関連しますが、意図しないmarginの相殺や逆に二重にマージンがついてしまうといった問題を考えなくて済みます。
gapの欠点
しかしgap
プロパティーにもデメリットがあります。
対応していないブラウザを利用している人も多い
2021年6月現在、Can I Use… の統計によるとサポート率は全体で約75%です。IEが対応していないのは仕方ないですが、モバイルで多くのシェアを占めるSafari on iOSも14.6以降の対応なのでまだ利用できないユーザーが多いです。これに関してはブラウザのアップデートとともに解消されるでしょう。
細かいアキの制御が難しい
gap
プロパティーはすべてのアキを1つのプロパティーで設定するので各要素間のアキを細かくするのは難しいです。その場合最低限のアキをgap
プロパティーで制御しつつ、余分なものは別途margin
プロパティーやpadding
プロパティーで調整する必要があるでしょう。
ラッピング要素が多くなるかもしれない
要素同士をレイアウト決定するために親要素を必要になるので、単純に並べた場合に比べ<div>
要素などが多くなるかもしれません。HTMLの階層が深くなりがちなので、開発体験に影響があるかもしれません。
まとめ
Flexbox・CSS Gridとgap
プロパティーを用いたレイアウト手法を紹介しました。デモではmargin
プロパティーを一切使わないという野心的なアプローチを取りましたが、実際にはmargin
プロパティーを使わざるを得ない場面もあるでしょう。
しかし、gap
プロパティーを活用しmargin
プロパティーへの依存を減らすことで柔軟なレイアウトを実現できます。これからはmargin
プロパティーと並んでgap
プロパティーもレイアウトで使われるメジャーなCSSプロパティーとなるのではないでしょうか。