更新日
10|CSS Gridとflexを使い分けをマスターしよう
今までレイアウトは全てdisplay: flex
を使用してきましたが、今後どんどん活用されていくであろうCSS Gridを使用したレイアウトもマスターしていきましょう。
Gridの学習の流れ
GridはCSSの中でもレベルの高い知識の1つです。実はプロのコーダーさんでもGridを使いこなせる人はそこまで多くありません。(逆に言えばGridが使えなくても仕事としては成り立ちます)
順を追って身につけていく必要があるので、以下の流れで取り組んでいきましょう。
- こちらの「CSS Grid 入門編」で基礎を身につける
- 学んだ知識を元にこの講座で実務レベルの実装を身につける
- 最後に「FlexboxとCSS Gridの違い」をおさらいする
基礎を終えたら実務に落とし込んでいこう
今回は以下の6つの例を元に実務レベルで扱えるように基礎を昇華させていきます👍






課題用のフォルダをダウンロード
このフォルダには6つの課題サンプルの課題・解答ファイルが入っています。
08_grid
>01〜06
のフォルダ01〜06
>Q
:課題用のフォルダ01〜06
>A
:解答用のフォルダ
style.css
を見比べながらコードを書いていきましょう。01|左右反転のレイアウト

動画
解説|grid-rowとgrid-columnの省略記法

入門編では、アイテムの配置場所を指定する、grid-row
とgrid-column
は
開始ライン番号 / 終了ライン番号
という書き方をしていました。(例:列のライン1番目から2番目であればgrid-column:1/2
)
しかし、隣あったラインに配置する場合は終了番号を省略することができます。
.grid__img._reverse{
grid-row: 2;
grid-column: 2;
}
反転させている画像の方のアイテムは
「列の2〜3番目のライン」と「行の2〜3番目のライン」に配置したいので左記のようなコードになっています。
解説|反転レイアウトは本来flex向き
上記の省略記法を説明するために今回のレイアウトを採用しましたが、本来反転レイアウトはflex-direction
のcolumn-reverse
やrow-reverse
を使うのが理想的です。
なぜなら今回のようにカラム幅が「1:1」の関係にないことが多いためです。CSS Gridはラインに沿って配置される特性上、2行目、3行目と行ごとの列の幅が変動するレイアウトの実装は不向きです。
つまり、子要素の幅によってカラム幅が決まるレイアウトの場合はflex
が向いています。
02|カードレイアウト

動画
解説|repeat(auto-fill,minmax(250px,1fr));
今回のポイントはまさしくこれです。
grid-template-columns: repeat(auto-fill,minmax(250px,1fr));
入門編で出てきたrepeat関数
の他にauto-fill
とminmax関数
と全部で3つの要素が絡んで実装されているのでじっくり考えないと頭がこんがらがります。
minmax()
主にgrid-template-column
で使用するこの関数にはminmax(最小幅,最大幅)
を記載します。
今回ではminmax(250px,1fr)
とあるため、アイテムは「250pxまでしか小さくならず、最大は1frまで広がる」という記述です。
最大幅が1fr
ということは、親のコンテナ幅が広がればどこまでも均等に拡大するということになります。
auto-fill
auto-fill
はrepeat関数
で使用する値の1つで、「コンテナの中にアイテムを敷き詰めるだけ敷き詰めたい時」に使います。
例えば以下のコードの場合、親要素に300px
の幅の列を作り、敷き詰めるだけアイテムを敷き詰めますが、60px
の余白にはアイテムが入り切らないため、次の行へ折り返されます。
.grid{
width: 960px;
display: grid;
grid-template-column: repeat(auto-fill,300px)
}

flex-wrap
で折り返すことに近いですね。repeat()
こちらはおさらい程度ですが、repeat関数はrepeat(繰り返す回数,繰り返す値)
という記述の仕方でしたね。
repeat(auto-fill,minmax(250px,1fr))を訳すと…
さて、ここまでの解説をまとめて実際のコードを訳していきましょう。

- まず、
repeat()
で繰り返される回数はauto-fill
なので「敷き詰める要素の数だけ繰り返し」となります。 - 次に
repeat()
で繰り返される値(=サイズ)は最小250px
、最大1fr
- つまり、親要素のサイズ(
960px
)を縮小した場合は、1つのアイテムが250px
まで縮むが、それ以下には縮まないため、要素が折り返される - 逆に、親要素のサイズ(
960px
)を拡大した場合は、250px
以上のサイズで敷き詰められるだけアイテムが敷き詰められる
となります。(言葉にすると結構ややこしいですね…)
例えば、コンテナの幅が750px
の場合は、750px÷250px
で3列になりそうですよね。
しかし、gap: 15px
の余白が設定されているため、3列表示するには最低でも余白を含んだ250px × 3 + 15px × 2 = 780px
の幅が必要となり、アイテムを並べる幅が足りず2列となります。
そして、minmax(250px,1fr)
で最大幅が均等に広がるようになっているため、親要素いっぱいにアイテムが広がるというわけです。

1列の時も同じ理屈です。以下はブラウザ幅が500px
ですので2列並びそうですが、.l-container
にpadding: 0 1.6rem
の左右の余白がついている関係で2列表示する幅が足りません。
そのため1列の表示になりますが、minmax(250px,1fr)
で親要素いっぱいに自動で広がるというわけです。

弱点はブレイクポイントのコントロールが難しいこと
この実装方法は実に画期的で、こだわらなければグリッドレイアウトがたった一行で済みます。
しかし、「1024px以下は3列で、768px以下は2列にしたい」というブレイクポイントごとの指定はできないという課題があります。
そのため、任意のブレイクポイントでのカラム数をコントロールしたい場合は繰り返しにauto-fit
を使わず明示的な数字を入れる必要があります。
@media screen and (min-width: 768px),print { /*768px以上は2列*/
.cardList {
grid-template-columns: repeat(2,1fr);
}
}
@media screen and (min-width: 992px),print { /*992px以上は3列*/
.cardList {
grid-template-columns: repeat(3,1fr);
}
}
@media screen and (min-width: 1200px),print { /*1200px以上は4列*/
.cardList {
grid-template-columns: repeat(4,1fr);
}
}
03|グリッドの端にそれぞれの要素を配置

- 画像は左
- テキストは右上
- ボタンは右下
という配置になっていますよね。
また、このレイアウトの良いところは少ないHTMLでテキストの増量やレスポンシブ時のボタンの折り返しなど様々なケースにおいて綺麗な見た目を保てることです。

動画
解説|ボタンをセルの右下に配置する
.c-product{
grid-template-columns: 336px 1fr;
}
まず列の幅ですが、画像は常に幅を336px
で固定し、テキストやボタンの列が縮小するようにしています。
@media (min-width: 768px) {
.c-product__footer{
align-self: flex-end; /* 高さを要素の高さにしトラックの下に配置 */
}
}
続いて、align-self: flex-start
ですが、flexの時にコンテナの高さに合わせてアイテムが伸びることを防ぐ時に使用しますよね。
Gridでも同じ目的で使用します。

デフォルトでは、アイテムの幅と高さはラインのエリアに沿って決まるため、align-self: flex-start
を設定していない場合、ボタンは上記のようにエリアいっぱいに広がってしまいます。
実は、flex
でしか使えないと思っていたプロパティのいくつかはgrid
でも使用することができます。
プロパティ | 対象 | 役割 |
---|---|---|
justify-content | コンテナ(親) | セル自体の主軸に対しての水平方向の位置 |
align-items | コンテナ(親) | セル内の主軸に対しての垂直方向の位置 |
align-self | アイテム(子) | 個別アイテムのセル内の垂直方向の位置 |
order | アイテム(子) | アイテムの順番の指定 |
このあたりはよく使用しますよね。他にもflex
で使っている配置系のプロパティはgrid
でも使用することができることを覚えておきましょう。
解説|ボタンの2列→1列をスマートに実装する
.c-product__footer{
display: grid;
justify-content: space-between;
grid-template-columns: repeat(auto-fill, minmax(250px,1fr));
gap: 1.6rem;
}
02|カードレイアウトでも出てきたrepeat(auto-fill, minmax(250px,1fr))
をボタンにも使うことで、2列に並んだボタンのサイズが250px
以下になるタイミングで1列になるようにしています。

またgap: 1.6rem
で余白を確保することで、横並びから縦並びになったときでも、margin
を設定し直すなどの手間がありません。
04|タイル風レイアウト

ブラウザ幅が広い時は複雑なレイアウトですが、縮小するに連れて4列→3列→2列→1列と変化していきます。
動画
解説|ボックスのタイル風→4列→3列→2列→1列のレイアウト変化
.sns{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px,1fr));
}
@media (min-width:768px) {
.sns{
grid-template-rows: repeat(3,160px);
}
}
メディアクエリを使用して、
- デスクトップの時は
3列*160px
で固定 - タブレット以下はお馴染みの
repeat()
で自動折り返し
という制御をしています。このように、メディアクエリを活用することで柔軟なレイアウトに対応することができます。
解説|auto-fitとauto-fillの違い
気付きづらいのですが、今回はauto-fill
ではなくauto-fit
になっています。
grid-template-columns: repeat(auto-fit, minmax(160px,1fr));
混乱を防ぐために「敷き詰める時はauto-fill
」と解説しましたが、実はauto-fit
が適切な場合もあります。
まず、今回auto-fill
とした場合にどう変化するかを見てみましょう。

セルが広がらず、最小幅として設定している160px
の3列表示になってしまいました。
grid-template-columns: repeat(auto-fit, minmax(160px,1fr));
一見バグのように見えますが、これがauto-fill
の本来の挙動です。グリッドを可視化するとわかるのですが、160px
の空白セルが敷き詰められて実質6列表示になっています。

次にauto-fit
の場合のグリッドを可視化してみます。

セルがいっぱいに広がってきちんと表示されていますよね。
違いを整理すると…
値 | 余白の埋め方 |
---|---|
auto-fill | 中身がないとしてもセルを埋めるだけ埋める |
auto-fit | 余白を埋めるように、アイテムの入ったセルが広がる |
ここまででピンとこない方は「auto-fill auto-fit 違い」で検索してみてください。この違いについては解説記事が多くあるのですぐ理解できるはずです👍
解説|Gridを使用した上下左右中央配置

上下左右の中央配置と言えば、現在は以下が主流ですよね。
.grid{
display:flex;
justify-content: center;
align-items: center;
}
しかし、これもCSS Gridを使えば簡単に再現することができます。
.sns__box{
display: grid;
place-items: center; /* 上下左右中央寄せ */
aspect-ratio: 1/1; /* 正方形に保つ */
border: solid 0.5px #B4B7BB;
height: 100%;
}
3行目のplace-items: center
がそうです。
実はこのコードはアイテム内の配置をコントロールする
align-items: center
(交差軸)justify-items: center
(主軸)
の2つのプロパティの一括指定となります。(表記が全く違うのがややこしい…)
そのため、以下でも同じように上下左右中央寄せになります。
.sns__box{
display: grid;
align-items: center
justify-items: center;
}
05|サイドバー+記事レイアウト
よくあるサイドバーと記事部分に分かれたレイアウトに加えて、スクロール固定のSNSシェアボタンがついているパターンです。

動画
解説|grid-template-areas
でエリアに名前をつける
入門編で出てこなかったgrid-template-areas
というプロパティが出てきましたね。
.post{
grid-template-areas: "side main sns";
grid-template-columns: 240px 1fr 56px;
}

一旦「エリア」のおさらいです。
grid
で分けた任意のセルの範囲をエリアと呼ぶのでしたね。
このエリアに名前をつけてアイテムを配置するのがgrid-template-areas
の役割です。
以前までは、grid-row
やgrid-column
を使い、何番目のライン間にアイテムを配置するかを指定していました。先ほどのタイル風レイアウトを例に上げると以下の通りですね。

しかし、このエリアごとに名前をつけて配置場所を名前で指定できるとしたらどうでしょうか?
まずは9分割されたセルのエリアに名前をつけてみます。

.sns{
grid-template-rows: repeat(3,160px);
grid-template-areas:
"tiktok instagram twitter"
"youtube instagram pinterest"
"youtube facebook facebook";
}
上記と同じようにエリアを分けたい場合、コンテナに対してgrid-template-areas
で次のように記述します。
エリア名は必ず文字列で記載し、区切ったセルの数だけエリア名を書くことに注目してください。
youtube
やfacebook
のようにセルが複数またいでいる場合は複数記述します。
.sns__box._tiktok{
grid-area: tiktok;
}
.sns__box._instagram{
grid-area: instagram;
}
.sns__box._twitter{
grid-area: twitter;
}
.sns__box._pinterest{
grid-area: pinterest;
}
.sns__box._youtube{
grid-area: youtube;
}
.sns__box._facebook{
grid-area: facebook;
}
コンテナにエリア名をつけたら、次はアイテムごとにどのエリアに配置するかを指定します(これがgrid-row
、grid-column
と同じ役目)
例えば「instagram」のエリアに配置したい場合はgrid-area: instagram
と書きます。
ポイントはこの時はエリア名をクォーテーションで囲まないことです。
こうすることでgrid-row
やgrid-column
で配置した時と同じような配置を再現できます。

だいぶ遠回りをしてしまいましたが、改めて05のコードに戻ります。
.post{
grid-template-areas: "side main sns";
grid-template-columns: 240px 1fr 56px;
}
これまでの内容を踏まえるとこのコードは"side main sns"
という3つのエリア分けをしていることになります。
この実装の良いところは、エリア名をつけて配置しているため、HTMLの構造を気にしなくてもいいところです。
スマホの時に「SNSボタン → 記事 → サイドバー」という縦の並びにしたいためHTMLの構造は以下のようになっています。
<div class="post__sns">
~~~~~~~
</div>
<div class="post__main">
~~~~~~~
</div>
<div class="post__side">
~~~~~~~
</div>
この構造の場合、デスクトップ横並びにしようとすると、通常ならSNSボタンが左、記事が真ん中、サイドバーが右となってしまいますが、気にすることなく好きな場所に配置することができます。
解説|グリッドレイアウトの中だけ追尾される要素
スクロール固定されたSNSシェアボタンはグリッドレイアウトの領域でのみついてくるため、スクロール値の取得などの煩わしい処理が必要ありません。

サイドバーの高さが記事の高さに合わせて伸びるため、そのサイドバーの中でposition: sticky
をつけることで、グリッドレイアウトの中だけ追尾させることができます。

06|コンテンツが少なくてもページの高さを保持
これもGridを使う上で結構有名な実装方法の一つです。
例えばお問い合わせの完了ページなどでコンテンツの量が少ないと高さが足りず、ちょっとカッコ悪い見た目になってしまいます。


grid
を使用することで、どんなにコンテンツが少ない状況でも最低限、ページの高さ異常はキープしてくれるように実装することができます。
動画
解説|auto
と1fr
を組み合わせる
このレイアウトの仕組みは非常にシンプルです。
縦に並べた要素の幅をコントロールするので、今回はgrid-template-rows
で行の幅を指定します。

この時にポイントとなるのが、auto
と1fr
の違いです。
1fr
は列や行の幅に合わせて自動で広がるものですが、auto
は要素の高さ分までしか広がりません。
.l-wrapper{
display: grid;
grid-template-rows: auto 1fr auto; /* header main footer */
min-height: 100vh;
}
コードを見ると、コンテナの高さにmin-height:100vh
(= 最低でもブラウザの高さを保持)を指定指定しています。
そのため、header
とfooter
に関しては要素が持つ高さ分となりますが、余った分はmain
の領域が広がり、コンテンツの中身が足りていなくても高さを保持できるというロジックです。
横幅も設定しておくとベター
<pre>
が含まれているとはみ出すので、grid-template-columns: 100%
で列も指定してあげることで回避できます。