見出し画像

CSS Grid でカードを横並びに配置する

今まで、自分はカードの並べ方に関して、キチンと理解しながら使い分けできていなかったので、しっかり実験をしてみました。

CSS Flexible Box だと実現がおそらくできないであろうカードの並べ方を、あえて実装して試しつつ、CSS Grid を用いたらどうやって実現出来るのかをご紹介できればと思います。


はじめに

今回実装したいレイアウトは以下のような見た目です!

画像1

一見できそうに感じるのですが、おそらく display: flex; で実現するのはかなり難しいかと思われます。

要件を言語化すると以下のような感じでしょうか。

・カードは左から右に並ぶ
・カードが横に並びきらないときは段落ちする
・段落ちしたときの最後の行のカードは左寄せである
・カードのサイズは最小幅がある(今回の実験では 240px を最小としました)
・カードのサイズは親要素の幅いっぱいまで均等に広がる

実験するにあたって、以下のような HTML を用いました。
(CSS はレイアウトに関わらなさそうなあしらい部分だけ適用したものを用いました)

<section>
 <div class="container">
   <div class="card">
     <div class="card__content">
       card1
     </div>
   </div>
   <div class="card">
     <div class="card__content">
       card2
     </div>
   </div>
   <div class="card">
     <div class="card__content">
       card3
     </div>
   </div>
   <div class="card">
     <div class="card__content">
       card4
     </div>
   </div>
   <div class="card">
     <div class="card__content">
       card5
     </div>
   </div>
   <div class="card">
     <div class="card__content">
       card6
     </div>
   </div>
   <div class="card">
     <div class="card__content">
       card7
     </div>
   </div>
 </div>  
</section>​

まずは、CSS Flexible Box を用いて挑戦してみましょう!


1-1. CSS Flexible Box で挑戦(左から右に並ばせてみる)

まずは、カードを横並びにさせたいので、とにもかくにも display: flex; 適用です!

.container{
 display: flex;
 flex-wrap: wrap;
}

.card {
 flex-basis: 240px;
 margin: .5rem;
}

display: flex; を適用することで横並びになり、 flex-wrap: wrap; を適用することで、段落ちを許容しました。カードの幅は今回の実験上 240px となっているので、それをベースに設定しています。margin はカード間の隙間を実現するために記載しています。さあ、どうなる…!

うーむ。。このままだと、【カードのサイズは親要素の幅まで均等に広がる】が実現できそうにありません。


1-2. CSS Flexible Box で挑戦(カードサイズを可変にしてみる)

伸びることを許容させるプロパティ flex-grow を適用してみましょう。

.container{
 display: flex;
 flex-wrap: wrap;
}

.card {
 flex-grow: 1; /* ←ここが追加 */
 flex-basis: 240px;
 margin: .5rem;
}

フレックスアイテムの伸び具合を指定できるプロパティ、 flex-grow を指定してみました!これでどうでしょうか?

くうううう…確かに【カードのサイズは親要素の幅まで均等に広がる】ようになったのですが、最後の要素だけ異様に間延びしてしまいました…。【段落ちしたときの最後の行は左寄せである】が実現できなくなりました。


1-3. CSS Flexible Box で挑戦(頑張って調整してみる)

ええいこうなったら、最小値・最大値で指定してみるぞ!!!

.container{
 display: flex;
 flex-wrap: wrap;
}

.card {
 flex: 1;
 flex-basis: 240px;
 min-width: 240px; /* ←ここが追加 */
 max-width: calc(33% - 1rem); /* ←ここが追加 */
 margin: .5rem;
}

段落ちを生じさせるために、 min-width: 240px; を追加して、さらにいい感じに伸びてほしいので、3列で並んだときの最大幅を max-width: calc(33% - 1rem); で設定してみました!これでどうだ!

結果は…3列表示のときはいい感じになりますが、ウィンドウサイズが狭くなって2列表示・1列表示となったときはカードが全然伸びてくれません…!それもそのはず、カードの最大幅は3列分のそれしか指定できてないですから…!


1-4. CSS Flexible Box まとめ

今回の実験を行うことで、CSS Flexible Box を用いて可変幅のカードを並べることは非常に難しいとわかりました。
具体的には、カードの枚数が列数できれいに割り切れないときの、最後のカードの制御がしにくい点がネックになってきます。

また、カード間の隙間を作るために用意する margin が、周囲の .containerと接する箇所にも適用されてしまうため、960 Grid System 等のグリッドレイアウトを適用したい際にも、別途調整が必要そうです。

スクリーンショット 2020-12-08 1.31.07


2-1. CSS Grid で挑戦

それでは、CSS Grid で挑戦してみましょう!今回記載したCSSは以下です!

.container{
 display: grid;
 grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
 gap: 1rem;
}

たった三行で実現できてしまいました…!!!(一行えげつないやつがいますが…)この記述で、今回満たしたい要件をすべて満たすことができました!やったー!!

以下で詳しく見てみましょう。


repeat() 関数

CSS の関数で、トラックリスト内での繰り返し部分を表し、列や行の繰り返しをよりコンパクトに書くことができます。
repeat() - CSS: カスケーディングスタイルシート | MDN

グリッドのカラムの指定方法が繰り返されるような時に使用できるプロパティです。 repeat([何回], [長さ]) という記法で記載します。(IE非対応…


auto-fit 値

repeat([何回], [長さ]) の [何回] 部分に適用出来る値です。グリッドアイテムが入り込めそうな幅があったらその回数分繰り返すよという指定方法です。今回は【カードのサイズは親要素の幅まで均等に広がる】の要件を満たすために使用した値です。


minmax() 関数

minmax() は CSS の関数で、寸法の範囲を min 以上、 max 以下で定義します。 CSS グリッドで使用されます。
minmax() - CSS: カスケーディングスタイルシート | MDN

CSS Grid の長さに関する記述の際に使用出来る関数です。その名の通り、最小値と最大値を指定することができます。今回は repeat([何回], [長さ]) の [長さ] 部分に適用しております。(IE非対応…


min() 関数

min() は CSS 関数で、 CSS プロパティの値としてカンマで区切られた式のリストから最小の (最も負である) 値を設定できます。
min() - CSS: カスケーディングスタイルシート | MDN

引数のうち、最も小さいものを選択してくれる関数です。今回は、240px というカード幅を便宜上設定しておりましたが、480px 等の大きいカードの場合、 `ウィンドウサイズ < カード幅` となるケースがあるため、その補完として使用しております。(IE非対応…

※ 注意: この関数は現在 WD(Working Draft) です。仕様が変更される可能性があるため、使う際は十分ご注意ください。



以下に grid の長さのプロパティについてまとめた codepen を用意しておりますので、よかったら参考にしてください!

これで無事、要件通りにカードを横並びに配置することができました!よかったー!


2-2. (おまけ)CSS Grid 正方形カード

CSS Grid で横並びにさせると、カードを正方形にさせたまま可変にすることもできます。

.container{
 display: grid;
 grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
 gap: 16px;
}

.card {
 height: 0;
 padding-bottom: 100%;
 position: relative;
}

.card__content {
 position: absolute;
 top: 0;
 left: 0;
 width: 100%;
 height: 100%;
}

※ 完全には仕様を読み解けてない、かつ、今後 UA の解釈によっては変わる可能性がありそうな箇所のため自信がありません…一応仕様を読んだときのログを貼っておきます。


なぜ正方形になるのか?

相対値の padding は包含ブロックの論理幅を基準に適用されるため。

Percentages: refer to logical width of containing block
CSS Box Model Module Level 3


なぜ親要素である `.container` の幅100%ではなく、グリッドアイテムの幅を100%とみなすのか?

(自信なし…)グリッドエリアにそれぞれ暗黙的に独立した包含ブロックを形成しているため。

A grid item’s grid area forms the containing block into which it is laid out. Grid items placed into the same grid area do not directly affect each other’s layout.
CSS Grid Layout Module Level 1
As adjacent grid items are independently contained within the containing block formed by their grid areas, the margins of adjacent grid items do not collapse.
CSS Grid Layout Module Level 1
Percentage margins and paddings on grid items, like those on block boxes, are resolved against the inline size of their containing block, e.g. left/right/top/bottom percentages all resolve against their containing block’s width in horizontal writing modes.
CSS Grid Layout Module Level 1


まとめ

今回文章にしたことで、自分の頭の中でもふわっとしていた箇所について整理ができてよかったです!

今後、IE対応しなくていい案件が来たら、この CSS Grid を使っていきたいな…!


この記事は GMOペパボデザイナー Advent Calendar 2020 8日目の記事でした。いろんなテーマの人がいておもしろいいいいい!!!


この記事が気に入ったらサポートをしてみませんか?