ここを意識したらCSS Gridの扱いが少しだけ上手くなった話
少し前まで苦手意識のあったCSS Grid Layoutですが、ここ半年くらいで実務で使用する機会が増え気がついたら苦手意識が薄まっていました。
間違いなく「作って学ぶ HTML+CSSグリッドレイアウト」を読んだおかげだと思いますが、Gridを使う上で注意点やコツみたいなものはあるのかなと感じています。
CSS Gridを使用する時に意識していることや、知っていると便利だなと思うことをまとめてみます。
autoと1frの動作の違いを理解する
auto
と1fr
はどちらもCSS Gridレイアウトで列幅や行幅を指定する際に使用されますが、それぞれの用途や動作は似ているようで異なります。以下の違いを理解していると扱いやすくなります。
特徴 | auto | 1fr |
---|---|---|
サイズの決定基準 | コンテンツのサイズ | 利用可能なスペース(を分割) |
コンテンツが多い場合の動作 | 列(行)幅が広がる | 列(行)幅は固定(スクロールが発生することもある) |
特にサイズの決定基準の理解が重要です。以下はauto
と1fr
のコンテンツサイズが決定されるそれぞれの基準を表した例です。
①のようにauto
と1fr
を同時に指定した場合それぞれの幅は、auto
はコンテンツサイズに、1fr
は残りの利用可能なスペース全体となります。
一方で②③のように、auto
と1fr
を連続して指定した場合は以下のようになります。
grid-template-columns: auto auto
では各列の幅はその列に含まれるコンテンツ(今回はテキスト)の長さに基づいて決定されます。「短いテキスト」の幅は短くなり、「非常に長いテキスト」の幅は長くなります。
grid-template-columns: 1fr 1fr
では2つの列が利用可能なスペースを均等に分割します。コンテンツの長さに関係なく、両方の列は同じ幅を持ちます。frの比率が異なれば、その比率で利用可能なスペースを分割します。
まとめるとauto
は中のコンテンツに依存して、1fr
はグリッドコンテナの利用可能なスペースに依存して列幅や行幅が決定されます。
全てのトラックサイズにautoを指定する際の注意点
グリッドを使用していると予期せずに行幅(または列幅)が広がってしまうようなケースがあります。例えば以下のような事例です。
タイトルの高さはコンテンツの高さに依存するのが自然だと思いますが、タイトルの領域を表す水色のエリアが広がってしまっています。
このケースでは、グリッドコンテナの高さは画像のアスペクト比と幅に基づいて決定しています。そしてタイトルとテキストのgrid-template-rows
にauto
が指定されていると、.title
と.text
の高さはコンテンツに基づきつつ、利用可能な高さに合わせて分配されるという挙動をとります。
.title
の高さをコンテンツの高さと同じにしたい場合は以下のように.text
のトラックサイズに1r
を指定します。
グリッドアイテムの片方が画像などの場合は高さ(または幅)が画像にもとづいて決定するため、もう片方の列(または行)のいずれかのグリッドアイテムで1fr
を指定することが重要です。
意外とハマるポイントなので基礎をしっかり理解しておくと良いでしょう。
コンテンツのオーバーフローに注意する
CSS Gridレイアウトを使用していると、思いがけずグリッドのコンテンツがオーバーフロー(body要素をはみ出て横スクロール)することがあります。例えば以下のような事例です。
上記はpre
タグを使用した事例ですがその他にもグリッドアイテムの中に、カルーセルや大きな画像がある場合にオーバーフローが発生しやすい印象です。
この現象はgrid-template-columns
に1fr
またはauto;
を指定していることが原因です。次のようにコードを変更することでオーバーフローを防ぐことができます。
1frでオーバーフローが発生する原因
1frはminmax(auto, 1fr)
と等価であり、autoの最小値は、グリッドアイテムのmin-contentサイズ(=内容が折り返されずに表示される最小サイズ)となります。
preタグは内容が折り返されないため、min-contentサイズ=テキスト全体の幅となります。その結果、グリッドトラックの幅が拡大し、オーバーフローが発生します。
一方でgrid-template-columns: minmax(0, 1fr);
では最小値を0pxと明示的に指定しています。最小値はmin-contentサイズではなく0pxが採用され、結果としてpreタグの内容がはみ出してもグリッドトラック自体はコンテナ幅に収まります。
Tailwindcssでもグリッドのトラックサイズにはminmax(0, 1fr)
が採用されています。
1fr
を使用する際には明示的に最小値を指定した方が無難なようです。
frは中途半端な数値でOK
gird-template-column
等で使用するfr
は簡約化した数値でなくても動作します。そのためデザインカンプに記載されている数値をそのままfrに当てはめると、その比率を保ったまま拡大・縮小してくれます。
以前はなぜか最大公約数で簡約化した数値を記述する必要があると思い込んでいたため、比率計算機などでわざわざ計算を行った上で入力していました。
ただ実際には簡約化した数値でなくても動くので、そのままの値を当てはめるようにしています。
grid-templateで一括指定を行う
grid-template
を使用することで以下のプロパティを一括で指定することができます。
- grid-template-areas
- grid-template-columns
- grid-template-rows
よくあるカードのコンポーネントで、一括指定を行わなかった場合は以下のようになります。
これをgrid-template
で一括して書くと次のようになります。
かなりスッキリしましたよね。
エリアの記述の下側と右側にそれぞれのトラックサイズを書くので、別々に書いた時よりもトラックサイズが読み取りやすく感じます。gridでエリアを定義する際にはgrid-template
の一括指定を積極的に使用するようにしています。
grid-templateでエリアを定義する基準
display: gird;
を使用する際にはgrid-template-areas
を使用してエリアを定義する方法としない方法の2種類の記述パターンがあります。この違いについて見ていきます。
以下は各セルのエリアを定義した例です。
一方で下記は各セルのエリアを定義しない例です。grid-column
とgrid-row
を使って各アイテムの割り当てられるトラックを指定しています。
直感的に見て理解しやすいのは「エリアを定義した例」かなと感じます。エリアを定義した場合のメリットとデメリットについて考えてみました。
メリット | デメリット |
---|---|
|
|
エリアの定義はシンプルなレイアウトでは過剰になってしまいますが、複雑なレイアウトではメリットが多いと感じます。私の場合、次のような使い分けをしています。
- 半数以上のグリッドアイテムにトラックを指定する必要がある:エリアを定義する
- 上記以外:エリアを定義しない(
columns/rows
またはorder
で対応)
要素の自動配置のみでレイアウトの大部分を組める場合は、エリアを定義しないことが多いです。
実際にはその他にも、レスポンシブでレイアウトが変わるかや、将来変更の可能性があるのかなども考慮し総合的に判断しています。
グリッドアイテム間の余白をピリオドで設定する
グリッドアイテムの間の余白を設定するには以下のようにgap
を使用することが多いと思います。
よくあるのが1〜2行目の間の余白と、2〜3行目の間の余白を異なる値で設定したいというパターンです。
この場合row-gap
を指定したままグリッドアイテム側で余白を調整する方法も良いですが、以下のようにtemplateの中でピリオドを使用することで個別に余白を設定することもできます。
余白の値が共通の場合にはrow-gap
またはcolumn-gap
を、余白の値が異なる場合にはピリオドを使うと良いかもしれません。
まとめ
今回紹介したレイアウトの大部分はdisplay: flex;
などの他のプロパティでも代用可能です。ただレイアウトの再現方法については選択肢が多いに越したことはないと考えています。
複数ある選択肢の中からそのデザインに最適なプロパティを選び実装することが、コーダーの仕事の醍醐味のような気がします(この辺りが難しくもあり楽しくもあるコーダーの役割だと感じています)。
特定のプロパティに依存せず様々なレイアウトを表現できるよう日々勉強です。