🌟

CSSグリッドで作るコンテンツラッパー

2022/11/13に公開

はじめに

サイトコーディングをする際、コンテンツの配置を揃えるためのレイアウト要素(以下ラッパーとします)があります。
以下赤色の部分にあたる領域。

ラッパーサンプル

こちらのラッパーですが、CSSグリッドを使うことでも実装が可能です。
また従来のアプローチでは難しかったレイアウトの実装も可能になるので、今回はその解説をいたします。

サンプル

下記サンプルになります。
詳細に関してこれまでの実装と比較しながら、順を追って説明したいと思います。


従来の方法

こちらのコンテンツラッパーですが、CSSグリッドを使わなかった従来の実装方法が下記になります。
※コンテンツ幅を1080pxにした例
※あくまで一例です。

.container {
    max-width: 1080px;
    width: calc(100% - 20px * 2);
    margin-right: auto;
    margin-left: auto;

    /* width: min(1080px, 100% - 20px * 2); */
}
コードについて詳しく

width 部分に関して:

  • calc()関数を使うメリット
    • 画面幅が max-width で設けた幅よりも大きい場合には、max値まで要素の幅が伸びきってくれる
    • max-width の幅よりも画面幅が狭くなった場合に、画面の両端に指定したスペース分の余白を確保してくれること
      の2点にあります。

※コメントアウト部分:ちなみにmin関数を使うことで max-widthwidth の記述を1行で実装できます。min関数の中ではcalc関数は必要としません。

ここで作ったラッパーのスタイルを下記のような感じで使用することが多いのではないでしょうか。

<section class="about">
    <div class="container">
        <!-- コンテンツ -->
    </div>
</section>

<section class="feature">
    <div class="container">
        <!-- コンテンツ -->
    </div>
</section>
...

上記の実装によりサイト内のコンテンツを綺麗に揃えることができます。

CSS Gridを使った方法

上記の実装をCSS グリッドで実装すると下記のようなコードになります。(コンテンツ幅を1080pxにした例)

.container {
  display:grid;
  grid-template-columns: 1fr min(1080px, 100% - 20px * 2) 1fr;
}

.container > * {
  grid-column: 2;
}
コードについて詳しく
  1. ラッパーにあたる要素に対して、display: grid;grid-template-columnsを指定します。
    • grid-template-columns の指定により、画面では下記のような3列のグリッドレイアウト※が形成されます。従来の実装にあった コンテンツエリアの実装を2列目にて表現しています。実装イメージ
    • 両端に指定した1frにより、画面幅に応じてコンテンツ外にあたる領域が伸び縮みしてくれるようになります。
  2. .container > * { grid-column: 2; }
    1. ラッパー要素の直下にあたる要素すべてに対して、columnを2と指定します。これは上で形成したグリッドレイアウトの2列目をコンテンツエリアとして割り当てることを意味します。これにより、コンテンツを中央に配置することが可能になります。

※ 検証ツールにてどのようにしてグリッドエリアが作られたか確認することができます。
(検証 > レイアウト > グリッド より)
grid_sample_02

grid-template-columns の挙動に関しては、こちらの記事 下部にある「Drag to Resize」の部分を実際に動かしてみると分かりやすいと思います。

上記コードで従来の方法と同じ挙動を実装することができます。
かえって複雑になってない?と思われた方もいらっしゃるかもしれないのですが、こちらの実装がどういう場面で活きてくるのかを説明します。

実用例

対象の要素を画面幅いっぱいに表示させたいとき

ラッパー直下の子要素の一部だけ画面幅いっぱいに表示したいケースがあると想定します。

full_width

上記のレイアウトは、下記コードで実装できます。

/* 画面幅いっぱいに表示するヘルパークラス */
.full-width {
    grid-column: 1/-1;
}
<div class="grid-container">
  <div class="child">Grid-Container</div>
  <!-- コンテンツが入ります -->
  <img class="full-bleed" src="https://placehold.jp/1920x450.jpg" alt="">
</div>

grid-column: 1/-1;の指定をしたスタイルを任意の要素に当てるだけで、要素が画面幅いっぱいに表示してくれるようになります。
これは グリッドエリア上の スタートライン(グリッドエリア最初の開始位置)から エンドライン(-1:グリッドエリア末尾の位置)まで要素が表示されるという意味を指します。

従来の方法では下記コードにて実装されることが多かったと思います。

/* 従来の実装 */
.full-width {
 width:100vw;
 margin: 0 calc(50% - 50vw);
}

上記の実装ですが、下記の点で少し気になります。

  • 画像要素に当てる上で注意が必要
    • divで括る必要がある
    • img の width に直接100%指定をしないといけない
  • ネガティブマージンを使ったりとハック的な側面が強い
  • 直感的でない

従来の実装に対して、grid-columnによるアプローチの方が直感的にかつ簡潔に実装できるのではないでしょうか。

画面幅最大での水平スクロールエリアを作りたいとき

少し変わったレイアウトですが、こちらもCSSだけで実装できます。コンテンツの開始位置はラッパー幅を起点にしてかつ、画面幅全域で水平スクロールをするようなエリアです。
完全には違いますが、具体的な事例だと下記のようなレイアウトを想定しています。(Appleサイトより)
image_sample
挙動イメージ

下記が実装コードになります。

<div class="container">
    ...
  <div class="gallery">
    <div class="wrapper">
        <div class="card">
        <img src="https://placehold.jp/350x250.jpg" width="350" height="250" alt="">
        <p>タイトルが入ります。</p>
        </div>
        <div class="card">
        <img src="https://placehold.jp/350x250.jpg" width="350" height="250" alt="">
        <p>タイトルが入ります。</p>
        </div>
        <div class="card">
        <img src="https://placehold.jp/350x250.jpg" width="350" height="250" alt="">
        <p>タイトルが入ります。</p>
        </div>
        <div class="card">
        <img src="https://placehold.jp/350x250.jpg" width="350" height="250" alt="">
        <p>タイトルが入ります。</p>
        </div>
        <div class="card">
        <img src="https://placehold.jp/350x250.jpg" width="350" height="250" alt="">
        <p>タイトルが入ります。</p>
        </div>
        (コンテンツ繰り返し任意)
    </div>
  </div>
</div>

:root {
  --max-width:1080px;
  --offset: 20px;
}


.container {
  display:grid;
  grid-template-columns: 1fr min(var(--max-width), 100% - var(--offset) * 2) 1fr;
}

.container > * {
  grid-column: 2;
}

.gallery {
  display: grid;
  grid-column: 1/-1;
  grid-template-columns: inherit;
  overflow-x: scroll;

  /* 下記は任意 */
  /* overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scrollbar-width: none; */
}

.wrapper {
  display:flex;
  grid-column: 2;
}

.wrapper::after {
  content:"";
  padding-inline-end: var(--offset);
}
コードについて詳しく
  1. スクロールエリア(wrapper)を包括する親要素を新たに作成。(.gallery
  2. .gallery を画面幅いっぱいに表示させるために grid-column: 1/-1; を指定。
  3. 直下の子要素(スクロールエリア:.wrapper)の開始位置をラッパーエリアの頭に揃えるために下記の実装が必要になる。
    1. .gallerydisplay: gridgrid-template-columns: inherit; と指定する。親のグリッドレイアウトを継承するグリッドエリアを再定義する。
    2. 1により、直下の要素(.wrapper)にてgrid-columnが機能するようになる。該当の要素(.wrapper)に対して開始位置を2と指定すること(grid-column:2;)で、要素の開始位置をラッパーの頭に揃えることができる。
    3. これだけだとスクロールエリアの末尾に余白がつかないので、擬似要素にて期待する余白の分だけpadding-inline-endを指定する。

こちらのレイアウトのなにが難しいのかというと、コンテンツの開始位置をラッパー幅に合わせる ところです。

コンテンツの開始位置は、paddingやmarginで余白を取って合わせることができますが、画面幅をリサイズした際にそのほかの要素と同様ni位置感に配置を揃えることが難しくなってくるため、ここの挙動をカバーするためにはおそらくJSによる実装が必要になってくるのではないかと考えています。
ですが、この実装もCSSグリッドを使うことで実装が可能になります。

こちらの実装ですが少々複雑になるので、詳細はこちらの記事を参考にすると良いと思います。

おわりに

よくあるタイルレイアウトを作る際にCSSグリッドが使われるのは基本ですが、今回のような場面でも実用ができる点で改めてCSS Gridの奥深さや柔軟性を知ることができました。

CSSも日々進化しているため今後もっと便利に、そしてJSを使わないアプローチがも加速してきている匂いを感じています。

本記事が誰かのお役に立てれば幸いです😊

参考

https://www.joshwcomeau.com/css/full-bleed/
https://ryanmulligan.dev/blog/x-scrolling-centered-max-width-container/

GitHubで編集を提案

Discussion