2 ポイント 投稿者 GN⁺ 2024-08-17 | 1件のコメント | WhatsAppで共有
  • 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本多い
    • 3列なら4本のグリッドラインができる
  • 名前付きグリッドラインは、デフォルトの数字の代わりにラインへ固有名を付ける方法
.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-endmain-startが同じライン
  • aside-startaside-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;
}
  • 同じasidemainfooter名を維持したまま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を指定する必要がある
  • 領域名を使えば各子要素をlogonavactionsにマッピングし、ビューポートごとに親の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;
  }
}
  • 900px以上では1行レイアウトに戻せる
@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スタイルのセクション例では、featuresecondarymost-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;
}
  • 両側の空列は主に左右の余白として機能する
  • レスポンシブ版では小さい画面でfeaturesecondarymost-readを縦に積み、500px900px以上で列と領域定義を変更する
  • grid-template-rowsは領域定義で代替できるため、必須ではない

Threadsスタイルの投稿コンポーネント

  • Threads app by MetaのCSSを見直すと、投稿コンポーネントにおけるCSS Gridの使用例を確認できる
  • 元の例ではgrid-template-columnsgrid-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-1thumb-2のように新しい領域名を定義し、grid-columngrid-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-selfjustify-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件のコメント

 
GN⁺ 2024-08-17
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はまだ微妙だ。記事ではブレークポイントごとのレイアウト調整や「チームメンバーがレイアウトを変える必要があるとき」に簡単だという根拠を挙げているが、単に自分が想定読者ではないのかもしれない
    • Gridを学ぶのに向いた楽しいツール: https://cssgridgarden.com/
    • Flexは大半の場合で十分だが、Gridにも使いどころはある。小さい画面で1行1カードに縮む3列カードレイアウト、専用サイドバーのあるページ構造、サイズ変更中でも特定の配置を保つ必要がある要素などがそうだ
      必要に応じて両方使えばよく、組み合わせると非常に強力なレイアウトツールになる
    • 「いいえ、Flexboxは『十分に良い』わけではありません」
      https://www.youtube.com/watch?v=MWWcZzossc8
  • ちょっと待って、1/3は分数ではなく1から3までの範囲なのか? なぜわざわざそんな文法を選んだのか理解できない。プログラミングには範囲を表す合理的でよく知られた文法がいろいろあるのに、たとえば1..3のようなものだ

    • CSSについて最初に知るべきことは、CSSがプログラミング言語の表現ではなく印刷レイアウトの言語を使っているということだ
      プログラマーがCSSで苦労する問題のかなりの部分は、この誤解に由来しているように思う
    • Gridの機能は好きだが、文法はほかのCSSとかなり違うので混乱する。この一角だけを切り離して作ったか、「今回はちゃんとやろう」という感じで進めた痕跡のように見える
    • スラッシュの両側に来るのは数字だけではなく、複数の単語も来られる: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column...
    • プログラマーに対して、CSSの!importantは「重要ではない」ではなく「重要だ!」という意味だと伝えると想像してみればいい
  • GridもFlexboxもちゃんと身についていなかった。最後にそれなりに使いこなせていたレイアウト戦略はfloatだった。この記事を見て、次のレイアウトを設計するときはCSS Gridを使ってみようという自信がついたし、記事自体もよくできていた

    • floatを扱ったことがあるなら、GridとFlexはずっと簡単なはずだ。1日か2日ほど時間を取って、いくつかレイアウトを作ってみればいい。昔のブラウザと格闘していた頃と違って、この2つはただ普通に動くし、実際ちょっと退屈なくらいだ
      良い記事はたくさんあるが、CSS-Tricksのこの2本を読むだけでも十分だった
      https://css-tricks.com/snippets/css/a-guide-to-flexbox/
      https://css-tricks.com/snippets/css/complete-guide-grid/
    • Gridは学ばなくてもある程度はやっていけるが、Flexboxは必須に近い。昔のfloatハックよりずっと楽で、モダンCSS全体に浸透しているからだ
      すでに出ている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は、floatベースのグリッドシステムをきちんと作り直した版のように感じる。子要素を縦にも横にも配置できるが、clearfixハックを使っていたfloatは実質的に行単位の配置しかできなかった
      ただしFlexboxも、折り返し可能な1次元レイアウトに近いので、ネストして積み重ねるのがコツだ。Gridはテンプレートがあり、直接の子要素があらかじめ決められた領域を占める方式なので、Flexboxより静的で、内側にネストされた要素には影響しないという限界がある。それでもsubgridがその部分を変えてくれそうだ
    • floatを理解していたなら、それだけでも珍しい達成なので、Flex/Gridもかなり楽しく使えるはずだ
    • Gridは機能が多いが、Flexは十分にシンプルでありながら今でも非常に強力だ
  • 関連資料:
    Grid Garden https://cssgridgarden.com
    Flexbox Froggy https://flexboxfroggy.com

  • この文書よりもっと基礎的な「入門前」バージョンがあるとよさそう。導入部分からつまずいて先に進めなかった。特に「Grid の中で子アイテムを配置するには grid-column で各アイテムの線番号を指定しなければならない」という部分で、ここでいう 線番号 が何なのかわからなかった
    文脈で理解できるだろうと読み進めたが、すぐに 1 / 31 / 4 のような表現でまた詰まった

    • 線番号は列の間を区切る線のこと。列が 2 つなら線は 3 本ある: |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.htmlCD を押して、ウィンドウサイズを変えてみるとよい。一緒に見る YouTube 動画もある: https://www.youtube.com/watch?v=qNtJ5p3h2A4
    本当にすばらしい
    auto flow でまだ理解したいのは、空きスペースを検出して埋める方法。サイズがバラバラのオブジェクトがランダムにあるとき、auto flow が空きを残さないようにするにはどうすればいいのだろう? それとも空きを検出して何かを追加できるのだろうか?

  • ばかげた質問でなければいいけれど、なぜ文法を 1/23/4 のようにスラッシュで定義したのだろう? 1-2 のように 1 列目から 2 列目まで、あるいは 1->2 のような形のほうが自然では?

    • 列番号は負の数にもなり得る。そうすると 1--11->-11/-1 のどれがよいだろう?
    • 矢印も可能かもしれないが、両側に空白があるのには理由がある。数値しか来ないわけではない: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column...
      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 を本当に理解できたのは今回が初めてかもしれない。番号表示 が本当に役に立ったし、今後もよく参照する記事になりそう

    • Firefox の開発者ツールでもその番号をデフォルトで表示できる。Chrome は確かではない
  • CSS Grid とエリアの概念は好きだが、依然としていちばん難しいのは、特にモバイルとデスクトップサイズの間での レスポンシブ管理 だと思う。どの UI 要素を開くべきか、全画面にするべきかによって、grid-template-area と行・列サイズを JavaScript で更新しなければならないことが多い
    minmaxcalc で解決できる世界をまだ見つけられていないので、誰か答えを知っているといいのだが

    • https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_contain...
    • こういうときは Grid はそのまま使い、行・列の値を固定値ではなく var(--foo) のような CSS 変数 にしておくやり方がよい
      そうすれば JavaScript ではその変数の値だけ更新すればよく、Grid がその値を反映してくれる
    • 記事でテンプレートエリアがレスポンシブにどれだけ向いているかを強調していなかったのは意外だった。メディアクエリを使えば画面ごとに異なる Grid エリアを定義でき、各要素をその画面サイズに合った望みの配置へ移動できる。特定サイズで要素を隠す必要があれば、そのサイズで display: none を指定すればよい
      とても単純な例: https://codepen.io/trescenzi/pen/vYqpjvL
    • そういうケースのかなり多くは :has()subgrid で解決できそうだが、実体験はあまりない
    • メディアクエリは使ってみましたか?