- CSS Gridは2017年3月から主要ブラウザで広くサポートされているが、
grid-template-areasはいまだ利用が少なく、ライン番号を計算せずにレイアウトをCSS内で視覚的に表現できる
grid-template-areasは文字列で領域名を配置し、子要素にgrid-areaを結び付ける方式で、同じ名前は必ず長方形の領域を構成しなければならない
- 名前付きグリッドラインは
content-start / content-endのようにラインへ名前を付ける方式で、*-startと*-endパターンはブラウザが暗黙の領域のように活用できる
- カード反転、レスポンシブヘッダー、エディトリアルレイアウト、Threads風の投稿、重なり合うカード、
:has()による条件付きレイアウトでは、親の領域文字列だけを変えて配置バリエーションを管理できる
- Chrome、Safari、FirefoxはすべてGrid領域向けDevToolsを提供しており、Firefoxは領域名の表示と
grid-area編集中に使えるライン名の自動補完が特に便利
grid-template-areasでレイアウトを読みやすくする
- 従来のCSS Grid配置は
grid-column: 1 / 3のようにライン番号を直接指定する方法が一般的
- ライン番号はDevToolsで確認するまで見えず、列と行が同時にあるレイアウトでは正しい番号を頭の中で計算するのが難しい
grid-template-areasは各セルに名前を付けて配置するため、CSSコード自体がレイアウトの地図のように見える
.page {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-areas: "item1 item2";
gap: 1rem;
}
.item-1 {
grid-area: item1;
}
.item-2 {
grid-area: item2;
}
- 子要素は
grid-areaで名前付き領域にマッピングされる
- カードの例では
"featured featured article"のように定義し、featured領域が2列、article領域が1列を占めるようにできる
構文と必ず守るべきルール
grid-template-areasは1つ以上の領域文字列を受け取る
- 文字列1つは列だけを持つ1次元レイアウトを作る
- 複数の文字列は行と列を持つ多次元レイアウトを構成する
- 複数の文字列を使うときは改行して書くとレイアウトをより読みやすくできる
.element {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-areas:
"aside main"
"footer footer";
}
- 各文字列はグリッドの1つの行を表し、表のように考えられる
- 同じ名前で定義された領域はコード上で長方形でなければならない
/* invalid */
.layout {
grid-template-areas:
"header header"
"sidebar main"
"header footer";
}
/* valid */
.layout {
grid-template-areas:
"header header"
"sidebar main"
"sidebar footer";
}
- すべての行は同じ数の領域トークンを持つ必要があり、使わない領域があってもセル自体は定義しなければならない
- 空セルはドット1つまたは複数で表現できる
.element {
grid-template-areas:
"aside main"
"... footer";
}
- ドットはCSS Grid仕様におけるnull cell tokenで、空のグリッドセルを表す
名前付きグリッドラインと領域の関係
- CSS Gridでは列数よりライン数が1本多い
- 名前付きグリッドラインは、デフォルトの数字の代わりにラインへ固有名を付ける方法
.layout {
grid-template-columns:
[full-start] 1fr
[content-start] 2fr
[content-end] 1fr [full-end];
}
.item {
grid-column: content-start / content-end;
}
- ラインに名前を付けても数字のラインは引き続き使える
- トラックサイズ(track size)は列または行のサイズを意味し、
1fr 1frのような値がトラックサイズに当たる
- 名前付きラインではトラックの周囲にあるラインへ名前を付ける
.layout {
display: grid;
grid-template-columns:
[aside-start] 200px [aside-end main-start] 1fr [main-end];
}
- 1本のラインは複数の名前を共有でき、上の例では
aside-endとmain-startが同じライン
aside-startとaside-endのように*-start / *-end名を定義すると、ブラウザは暗黙のライン名を通じてgrid-column: asideのような指定を許可する
- すべてのラインに名前を付ける必要はなく、重要なラインだけを選んで付けられる
- 配置時には数字と名前を混在させて
grid-column: 1 / content-endのようにも使える
親の領域文字列で配置バリエーションを管理する
grid-template-areasを使うと、子要素のgrid-area名はそのままにして、親の領域文字列だけを変えることで全体レイアウトを変更できる
.aside {
grid-area: aside;
}
.main {
grid-area: main;
}
.footer {
grid-area: footer;
}
- 同じ
aside、main、footer名を維持したままgrid-template-areas文字列だけを変えれば、全体配置が自動的に更新される
- レイアウト制御が親の領域定義へ集約されるため、調整しやすくなる
カードの向きを反転する
- Flexboxでは
flex-direction: row-reverseでカードの向きを簡単に反転できる
- CSS Gridでライン番号を使う場合、向きを変えるには列定義と各子要素の
grid-column値を一緒に修正しなければならない
- 名前付き領域を使えばカードの画像とコンテンツを領域に指定し、親の領域順だけを入れ替えればよい
.card {
display: grid;
grid-template-columns: 150px 1fr;
grid-template-areas: "thumb content";
}
.card__thumb {
grid-area: thumb;
}
.card__content {
grid-area: content;
}
.card--flip {
grid-template-columns: 1fr 150px;
grid-template-areas: "content thumb";
}
grid-template短縮プロパティでも同じ配置を表現できる
.card--flip {
grid-template: "content image" / 1fr 150px;
}
- 短縮プロパティは任意であり、長いプロパティのほうが読みやすいと考える選択もある
レスポンシブなヘッダーレイアウト
- ロゴ、ナビゲーション、アクション領域を持つヘッダーは、最初は同じ幅の3列で構成できる
- ライン番号を使う場合、
.logo、.nav、.actionsそれぞれにgrid-columnを指定する必要がある
- 領域名を使えば各子要素を
logo、nav、actionsにマッピングし、ビューポートごとに親のgrid-template-areasだけを変えればよい
.header {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-areas: "logo nav actions";
}
.logo {
grid-area: logo;
}
.nav {
grid-area: nav;
}
.actions {
grid-area: actions;
}
- 小さいサイズではロゴとアクションを1行目に置き、ナビゲーションを全幅にできる
.header {
grid-template-areas:
"logo logo actions"
"nav nav nav";
}
380px以上ではロゴを左に置き、ナビゲーションとアクションを右側へ配置できる
@media (min-width: 380px) {
.header {
grid-template-areas:
"logo nav nav"
"logo actions actions";
}
.nav {
justify-self: end;
}
}
@media (min-width: 900px) {
.header {
grid-template-areas: "logo nav actions";
}
}
- 領域文字列はCSS内でUI配置を直接示すマッピングのように機能する
エディトリアルレイアウトの例
- タイトル、テキスト、画像を含むセクションは
grid-template-areasでビューポートサイズごとの配置を簡単に変えられる
.section {
display: grid;
grid-template-areas:
". title title"
"thumb content content";
gap: 1rem;
}
- タイトル左側のドットは空白を表すnull cell token
- time.comスタイルのセクション例では、
feature、secondary、most-read領域と両側の空列を一緒に使っている
.featured-section {
display: grid;
grid-template-areas:
". feature most-read ."
". secondary most-read .";
grid-template-columns: 1fr minmax(auto, 57.313rem) minmax(12rem, 18.75rem) 1fr;
grid-template-rows: minmax(auto, 25rem) 1fr;
}
- 両側の空列は主に左右の余白として機能する
- レスポンシブ版では小さい画面で
feature、secondary、most-readを縦に積み、500pxと900px以上で列と領域定義を変更する
grid-template-rowsは領域定義で代替できるため、必須ではない
Threadsスタイルの投稿コンポーネント
- Threads app by MetaのCSSを見直すと、投稿コンポーネントにおけるCSS Gridの使用例を確認できる
- 元の例では
grid-template-columns、grid-template-rowsとライン番号でアイテムを配置している
.postBody {
grid-column-start: 2;
grid-row-start: 2;
grid-row-end: span 3;
}
- 複数の投稿バリエーションは
grid-template-areasでより簡単に表現できる
.post {
grid-template-areas:
"avatar header"
"avatar body"
". body"
". footer";
}
.post--reply {
grid-template-rows: 40px max-content max-content;
grid-template-areas:
"avatar header"
"body body"
"body body"
"footer footer";
}
.post--nested {
grid-template-areas:
"avatar header"
"avatar body"
"line body"
"footer footer";
}
- 通常、返信、ネストされた投稿の各バリエーションを領域文字列を見るだけで見分けられる
重なり合うアイテムと領域名
grid-template-areasでは同じ名前は長方形でなければならないため、非長方形の形で同じ名前を繰り返すと無効になる
.layout {
display: grid;
grid-template-columns: 1fr 1fr fr;
grid-template-areas:
"card tag tag"
"title title card";
}
- 上の例は
cardが長方形を構成していないためinvalid
- 重なり合うカードでは
thumb-1、thumb-2のように新しい領域名を定義し、grid-columnとgrid-rowでその名前を使える
.card {
display: grid;
grid-template-columns: auto auto 1fr;
grid-template-rows: auto auto;
grid-template-areas:
"thumb-1 tag"
"title thumb-2";
}
.thumb {
grid-column: thumb-1 / thumb-2;
grid-row: thumb-1 / thumb-2;
}
- タグとタイトルはそれぞれ
grid-areaに配置し、align-self、justify-selfで整列できる
- 必要に応じて子要素の重なり順を管理する必要があるが、例ではソース順だけで解決している
- この重なり例はOddbird’s Cascading Layoutsページから着想を得ている
:has()と条件付きレイアウト
- CSS
:has()セレクタとGrid領域を組み合わせると、特定要素の有無に応じてレイアウトを変えられる
<figure>内にfigcaptionがある場合とない場合で、異なる領域定義を適用できる
figure {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-areas: "img img img";
gap: 0.5rem;
}
img {
grid-area: img;
}
figcaption {
grid-area: caption;
}
.card:has(figcaption) {
grid-template-areas:
"img img caption"
"img img .";
}
LTR/RTL多言語方向サポート
- CSS Gridはページ方向がLTRかRTLかに応じてレイアウトを調整する
- 名前付きグリッド領域もページ方向に従うため、RTL設定ではレイアウトが反転する
- RTLレイアウト作成に関する資料としてRTL Styling 101ガイドを参照できる
DevToolsサポートの違い
- Chrome、Safari、FirefoxはいずれもGrid領域向けのDevToolsを提供している
- Chrome
- 領域名を左上に配置するため、領域が重なって見えることがある
- カラーオーバーレイによってデザインの見え方が変わることがある
- Safari
- ピンク色のアウトラインがアイテムを明確に区別する助けになる
- ライン名を確認できる
- ライン名の表示方法には、行を積み重ねて見せるような改善の余地がある
- Firefox
- 領域名がChromeやSafariよりも明確に見える
*-start、*-endのようなカスタム名を入力すると自動補完リストを提供する
- 子アイテムの
grid-areaを編集すると、使えるすべてのライン名の一覧を表示する
参考資料
1件のコメント
Hacker Newsの意見
アニメーションのデモはかなり面白かったが、調べてみるとそのアニメーションはCSSで作られたものではなくて残念だった
Flexboxを学んだあと「これで十分だ」と考え、Gridは深く学ばなかった。Gridを好む理由はありそうだが[0]、全体としてGridでしかできない作業が必要だったことはなく、データテーブルには実際の
<table>を使っている[0] https://css-tricks.com/css-grid-replace-flexbox/
Gridは、https://labs.jensimmons.com/2016/examples/image-gallery-grid... のようなレスポンシブな画像ギャラリーや、https://every-layout.dev/demos/grid-cards/ のようなレスポンシブカードにしか使ったことがない。
grid-template-columnsは学ぶ価値があり、grid-gapは便利だ。それ以外はFlexboxでほとんど対応できる感じだただし
grid-template-areasはまだ微妙だ。記事ではブレークポイントごとのレイアウト調整や「チームメンバーがレイアウトを変える必要があるとき」に簡単だという根拠を挙げているが、単に自分が想定読者ではないのかもしれない必要に応じて両方使えばよく、組み合わせると非常に強力なレイアウトツールになる
https://www.youtube.com/watch?v=MWWcZzossc8
ちょっと待って、
1/3は分数ではなく1から3までの範囲なのか? なぜわざわざそんな文法を選んだのか理解できない。プログラミングには範囲を表す合理的でよく知られた文法がいろいろあるのに、たとえば1..3のようなものだプログラマーがCSSで苦労する問題のかなりの部分は、この誤解に由来しているように思う
!importantは「重要ではない」ではなく「重要だ!」という意味だと伝えると想像してみればいいGridもFlexboxもちゃんと身についていなかった。最後にそれなりに使いこなせていたレイアウト戦略はfloatだった。この記事を見て、次のレイアウトを設計するときはCSS Gridを使ってみようという自信がついたし、記事自体もよくできていた
良い記事はたくさんあるが、CSS-Tricksのこの2本を読むだけでも十分だった
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
https://css-tricks.com/snippets/css/complete-guide-grid/
すでに出ているCSS-Tricksの記事に加えて、MDNのFlexboxガイド[1]と「CSS flexible box layout」のリファレンス[2]も勧めたい。Flexboxの背後にある概念やプロパティ、短縮プロパティが実際にどう動くのかを、より深く理解するのに役立った
[1]: https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layou...
[2]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexibl...
ただしFlexboxも、折り返し可能な1次元レイアウトに近いので、ネストして積み重ねるのがコツだ。Gridはテンプレートがあり、直接の子要素があらかじめ決められた領域を占める方式なので、Flexboxより静的で、内側にネストされた要素には影響しないという限界がある。それでもsubgridがその部分を変えてくれそうだ
関連資料:
Grid Garden https://cssgridgarden.com
Flexbox Froggy https://flexboxfroggy.com
この文書よりもっと基礎的な「入門前」バージョンがあるとよさそう。導入部分からつまずいて先に進めなかった。特に「Grid の中で子アイテムを配置するには
grid-columnで各アイテムの線番号を指定しなければならない」という部分で、ここでいう 線番号 が何なのかわからなかった文脈で理解できるだろうと読み進めたが、すぐに
1 / 3、1 / 4のような表現でまた詰まった|a||b|で中央の 2 本の線は実際には 1 本として数えるので、合計 3 本になるだから
1/3は分数ではなく 列の範囲 で、「1 番線から始まって 3 番線で終わる」という意味「線番号」という名前はあまりよくないけれど、実際には列の端のこと。だから
1/3は 1 番目の端、つまり一番左から 3 番目の端までを指し、列 2 つぶんを占めるCSS Grid なら Jen Simmons Mondrian with auto flow を強くおすすめする。https://labs.jensimmons.com/2017/01-011.html で
CとDを押して、ウィンドウサイズを変えてみるとよい。一緒に見る YouTube 動画もある: https://www.youtube.com/watch?v=qNtJ5p3h2A4本当にすばらしい
auto flow でまだ理解したいのは、空きスペースを検出して埋める方法。サイズがバラバラのオブジェクトがランダムにあるとき、auto flow が空きを残さないようにするにはどうすればいいのだろう? それとも空きを検出して何かを追加できるのだろうか?
ばかげた質問でなければいいけれど、なぜ文法を
1/2、3/4のようにスラッシュで定義したのだろう?1-2のように 1 列目から 2 列目まで、あるいは1->2のような形のほうが自然では?1--1、1->-1、1/-1のどれがよいだろう?CSS ではすでに空白で値を区切っているし、ダッシュが入ったときに私たちが一般に抱く直感まで考えると、次のほうがずっとよい
grid-column: 4 somegridarea / 6;grid-column: span 3 / 6;grid-column: span somegridarea / span someothergridarea;次のように書くよりはるかに明確
grid-column: 4 somegridarea-6;grid-column: span 3-6;grid-column: span 3->6;grid-column: span somegridarea-span someothergridarea;grid-column: span somegridarea->span someothergridarea;こういう インタラクティブなブログ記事 は本当に良い。Grid は https://grid.layoutit.com/ のようなツールで grid area を直接触ってみるのを強くおすすめする
もう何年もそのサイトで Grid レイアウトを設計しているが、間隔の調整がずっと楽になった
CSS Grid を本当に理解できたのは今回が初めてかもしれない。番号表示 が本当に役に立ったし、今後もよく参照する記事になりそう
CSS Grid とエリアの概念は好きだが、依然としていちばん難しいのは、特にモバイルとデスクトップサイズの間での レスポンシブ管理 だと思う。どの UI 要素を開くべきか、全画面にするべきかによって、
grid-template-areaと行・列サイズを JavaScript で更新しなければならないことが多いminmaxやcalcで解決できる世界をまだ見つけられていないので、誰か答えを知っているといいのだがvar(--foo)のような CSS 変数 にしておくやり方がよいそうすれば JavaScript ではその変数の値だけ更新すればよく、Grid がその値を反映してくれる
display: noneを指定すればよいとても単純な例: https://codepen.io/trescenzi/pen/vYqpjvL
:has()や subgrid で解決できそうだが、実体験はあまりない