【翻訳】モダンなCSSレイアウト: フレームワークは必要ないかも(Brecht De Ruyte, Smashing Magazine)

www.smashingmagazine.com

CSSフレームワークやライブラリの海に迷い込むのは簡単です。しかし、このような氾濫の中で、今日ある最新のCSS機能は、依存関係や抽象化を追加することなく、よりシンプルで柔軟なアプローチを提供します。Brecht De Ruyteは、GridやFlexboxなど、特定のレイアウトが必要なときに、設定可能なオプションを使って、実質的にどこでも使えるようにするテクニックを使って、4つのCSSユーティリティクラス(ボーナスもあり)を紹介します。

CSSでレイアウトを確立することは、開発者として、最も使いやすいフレームワークに委ねることが多いものです。そして、フレームワークから必要なものだけを得るために設定することは可能だとしても、レイアウト機能のためだけにCSSライブラリ全体を統合したことがどれほどあるでしょうか?960.gs、Bootstrap、Susy、Foundationの時代までさかのぼれば、多くの人がどこかでやったことがあると思います。

最新のCSS機能は、レイアウトのためだけにフレームワークに手を伸ばす必要性を大幅に減らしています。しかし、私はそのような状況を見続けています。あるいは、同じGridやFlexboxレイアウトを何度も作り直している同僚の多くに共感します。

この記事では、ウェブレイアウトをよりコントロールできるようになります。具体的には、4つのCSSクラスを作成します。このクラスは、特定のレイアウトが必要なプロジェクトや場所で、すぐに使用することができます。

この講座で扱うコンセプトは重要ですが、この講座から本当に得てほしいのは、私たちが自分自身で行うことを避けがちなことにCSSを使う自信です。レイアウトは、フォームコントロールのスタイリングと同じレベルの課題でした。ある種の独創的なレイアウトは今でも難しいかもしれませんが、今日のCSSの設計方法は、私たちが長年にわたって外注したり作り直したりしてきた、確立されたレイアウトパターンの負担を解決してくれます。

私たちが作っているもの

それぞれ異なるレイアウトアプローチを持つ4つのCSSクラスを作成します。例えば、Flexboxに基づいた流動的なレイアウトが必要な場合、その準備ができるというものです。他の3つのクラスも同様です。

これらのクラスは一体何なのでしょうか?そのうちの2つはFlexboxレイアウトで、残りの2つはグリッドレイアウトです。グリッドレイアウトを拡張して、CSS Subgridを活用することもできます。

1つは利用可能なスペースを自動的に埋めるもので、「Fluid=流動的」レイアウトと呼んでいます。もう1つは列と行をより細かく制御できるもので、「Repeating =反復的」レイアウトと呼んでいます。

最後に、CSSコンテナクエリを統合して、これらのレイアウトがビューポートのサイズではなく、レスポンシブ動作のために自身のサイズに反応するようにします。カスケード・レイヤーを使用することで、特殊性のレベルを制御し、独自のCSSとのスタイルの衝突を防ぐことができます。

セットアップ カスケードレイヤーとCSS変数

私が何度か使ったことのあるテクニックは、スタイルシートの最初にカスケードレイヤーを定義することです。このアイデアが好きな理由は、スタイルをすっきりと整理整頓できるだけでなく、特定の順序でレイヤーを整理することで、各レイヤーのスタイルの特異性に影響を与えることができるからです。これらによって、私たちが作っているユーティリティ・クラスは、メンテナンスが容易になり、特異性の争いに巻き込まれることなく、自分の仕事に統合することができます。

この作品では、以下の3つのレイヤーで十分だと思います:

@layer reset, theme, layout;

順番に注目してください。resetレイヤーが最初に来るので、このレイヤーの中では最も特殊なレイヤーになります。layoutレイヤーは最後に来るので、他の2つのレイヤーのスタイルよりも優先順位が高くなります。レイヤーのないスタイルを追加する場合、そのスタイルは最後に追加されるため、最も特殊性が高くなります。

図1: ChromeのDevToolsでカスケードレイヤーを検査します。(大きなプレビュー)

関連: 「Getting Started With Cascade Layers」 by Stephanie Eckles.

各レイヤーをどのように使うか、簡単に説明しましょう。

リセットレイヤー

resetレイヤーには、「リセット」したいユーザーエージェントのスタイルが含まれます。ここに独自のリセットを追加することもできますし、プロジェクトにすでにリセットがある場合は、このレイヤーを使用せずに先に進むこともできます。ただし、レイヤー化されていないスタイルは最後に読み込まれることを覚えておいてください。

CSSボックスモデルに従って、すべての要素がborder-boxによって一貫したサイズになるようにするよく使われるbox-sizing宣言を入れておきます。

@layer reset {
  *,
  *::before、
  *::after {
    box-sizing: border-box}

  body {
    margin: 0}
}

テーマレイヤー

このレイヤーは、:root要素にスコープされた変数を提供します。というのも、今回作成するユーティリティクラスのようなレイアウトコンテナは、多くの場合、他の要素のラッパーであり、グローバルスコープを使用することで、変数が必要な場所で使用できるようになるからです。とはいえ、必要であればローカルで別の要素にスコープすることも可能です。

さて、変数のデフォルト値を 「良い 」ものにするかどうかは、プロジェクト次第です。特定の値を設定するつもりですが、その値に固執しなければならないとは思わないでください - これは非常に設定可能なシステムであり、あなたのニーズに合わせることができます。

以下は4つのレイアウトに必要な3つの変数です:

@layer theme {
  :root {
    --layout-fluid-min: 35ch;
    --layout-default-repeat: 3;
    --layout-default-gap: 3vmax}
}

順を追って説明すると、以下のようになります:

注意これは、レイアウト固有の値の識別子として使用しています。これは、この作業を構造化するための私の個人的な好みですが、あなたのメンタルモデルに合う命名規則を選んでください - 物事の命名は難しいものです!

レイアウトレイヤー

このレイヤーはユーティリティクラスのルールセットを保持します。グリッドについては、グリッドコンテナ内でCSS Subgridを使用するための5番目のクラスを含める予定です。

@layer layout {  
  .repeating-grid {}
  .repeating-flex {}
  .fluid-grid {}
  .fluid-flex {}

  .subgrid-rows {}
}

これですべてのレイヤーが整理され、変数が設定され、ルールセットが定義されました。まずは「反復的」レイアウトから始めましょう。1つはCSS Gridをベースとし、もう1つはFlexboxを使用します。

反復的グリッドとFlexレイアウト

最もシンプルな」レイアウトから始めて、そこから複雑さをスケールアップしていくのが良いアイデアだと思います。そこで、他のレイアウトで使用する包括的なテクニックの導入として、まず「反復的グリッド」レイアウトに取り組みます。

反復的グリッド

layoutレイヤーに移動すると、.repeating-grid`ルールセットがあり、ここにこのレイアウトのスタイルを記述します。基本的には、これをグリッドコンテナとして設定し、作成した変数を適用してレイアウトのカラムとカラム間の間隔を設定します。

.repeating-grid {
  display: gridgrid-template-columns: repeat(var(--layout-default-repeat), 1fr);
  gap: var(--layout-default-gap)}

ここまではそれほど複雑ではありませんよね?これで、3つの等しい大きさのカラムが、利用可能なスペースの1分の1(1fr)を占め、その間にギャップがあるグリッドコンテナができました。

しかし、さらに一歩進んで、列の数とギャップの大きさを設定できるシステムにしたいと思います。このグリッドにスコープされた2つの新しい変数を紹介します:

  • --_grid-repeat: グリッドの列数。
  • --_grid-repeating-grid-gap:グリッドの列数: グリッドアイテム間のスペース。

これらの変数の前にアンダースコアをつけていることにお気づきでしょうか?これは constlet ができる前の JavaScript の慣習で、「プライベートな」 変数、つまりローカルにスコープされた変数を指定するためのものです。これらは自由に名前を変えて構いませんが、なぜアンダースコアがついているのか不思議に思うかもしれないので、先に書いておきます。

.repeating-grid {
  --_grid-repeat: var(--grid-repeat, var(--layout-default-repeat));
  --_repeating-grid-gap: var(--grid-gap, var(--layout-default-gap));

  display: gridgrid-template-columns: repeat(var(--layout-default-repeat), 1fr);
  gap: var(--layout-default-gap)}

Notice: これらの変数は @theme レイヤーの変数に設定されています。私は、グローバル変数をローカルにスコープされた変数に代入するというアイデアが好きです。こうすることで、@themeで設定したデフォルト値を活用することができますが、グローバル変数が使用されている他の場所に干渉することなく、簡単に上書きすることができます。

それでは、これらの変数を同じ .repeating-grid ルールセット内の先ほどのスタイルルールに適用してみましょう:

.repeating-grid {
  --_grid-repeat: var(--grid-repeat, var(--layout-default-repeat));
  --_repeating-grid-gap: var(--grid-gap, var(--layout-default-gap));

  display: gridgrid-template-columns: repeat(var(--_grid-repeat), 1fr);
  gap: var(--_repeating-grid-gap)}

ここから、HTMLの要素に .repeating-grid を適用するとどうなるでしょうか?次のような簡略化したマークアップで作業しているとしましょう:

<section class="repeating-grid">
  <div></div>
  <div></div>
  <div></div
</section>

これらの div に background-colorheight を適用すると、3 つの等しい大きさのカラムに配置され、最初の行に収まらない div は自動的に次の行に折り返される、素敵なボックスのセットができます。

Pen Layout Utility: Geoff Graham による Repeating Grid [forked] をご覧ください。

もちろん、3列だけである必要はありません。同じHTMLを使って、2vwから3vwgapを更新しながら、3から5に反復的カラムを変更したい商品グリッドがあるとします。

<section class="repeating-grid products-grid">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</section>

どのように構成されているかわかりますか?グローバルにスコープされた変数のセットに基づいてグリッドレイアウトを作成し、ユーティリティクラスにローカルにスコープされた変数に再割り当てし、さらに独自のクラスでカスタマイズして、要素の目的にコンテキストを追加し、レスポンシブな動作を調整できるようにしました。

.products-grid { --grid-repeat: 2
  --grid-repeat: 2;
  --grid-gap: 2vw;

  media (width >= 1000px) { --grid-repeat: 3
    --grid-repeat: 3;
    --grid-gap: 3vw}
}

Pen Layout Utility: Geoff Graham による Repeating Grid [forked] を参照してください。

この利点は、余計なクラスでHTMLを汚すことなく、デフォルト値を上書きできることです。これは、他の3つのレイアウトクラスでも使用する包括的なアプローチです。次は先ほど作ったものの 「反復的フレックス 」バージョンです。

反復的フレックス

Repeating Gridレイアウトは素晴らしいものですが、常に同じ大きさのカラムが欲しいとは限りません。CSS Gridは確かに空いているスペースで要素を自動で埋めることができますが、Flexboxはそれを非常に得意としています。

先ほどと同じ 5 つの div があるとします。この場合、2 行目の右側の空の列の隣に 2 つの div が残ります。おそらく、最後に残った 2 つの div が伸びて、空の列のスペースを占めるようにしたいのでしょう。

図2: フレキシブルなアイテムは、2行目の空の3列目として表示される残りのスペースを自動的に埋めます。(大きなプレビュー)

Repeating Gridレイアウトで確立したプロセスを、このRepeating Flexレイアウトで使用する時が来ました。今回は、@layoutレイヤーの.repeating-flexルールセットにプライベート変数を定義します。

.repeating-flex {
  --_flex-repeat: var(--flex-repeat, var(--layout-default-repeat));
  --_repeating-flex-gap: var(--flex-gap, var(--layout-default-gap))}

ここでも、グローバルにスコープされた変数に割り当てられたデフォルト値を上書きするために、ローカルにスコープされた2つの変数が使用されています。次に、これらをスタイル宣言に適用します。

.repeating-flex {
  --_flex-repeat: var(--flex-repeat, var(--layout-default-repeat));
  --_repeating-flex-gap: var(--flex-gap, var(--layout-default-gap));

  display: flexflex-wrap: wrap;
  gap: var(--_repeating-flex-gap)}

今のところ、フレックスアイテム間のギャップサイズを設定するために変数の1つだけを使用していますが、これはすぐに変更されるでしょう。今のところ重要なのは、flex-wrap プロパティを使って Flexbox に、レイアウト内の追加項目を 1 行にまとめるのではなく、複数行に折り返しても問題ないことを伝えていることです。

しかし、そうすると、フレックスアイテムがどのように縮小または拡大されるかを、残りの利用可能なスペースに基づいて設定する必要があります。これらのスタイルを親ルールセット内にネストしてみましょう:

.repeating-flex {
  --_flex-repeat: var(--flex-repeat, var(--layout-default-repeat));
  --_repeating-flex-gap: var(--flex-gap, var(--layout-default-gap));

  display: flexflex-wrap: wrap;
  gap: var(--_repeating-flex-gap);

  > * {
    flex: 1 1 calc((100% / var(--_flex-repeat)) - var(--_gap-repeater-calc));
  }

なぜユニバーサルセレクタ (*) を使っているのか不思議に思うかもしれませんが、レイアウトアイテムが常に div であると仮定することはできないからです。もしかしたら、それらは <article> 要素かもしれませんし、 <section> 要素かもしれませんし、まったく別のものかもしれません。子コンビネータ (>) は、ユーティリティクラスの 直接 の子である要素のみを選択するようにし、他の祖先スタイルへの漏れを防ぎます。

flex省略記法プロパティは、もう何年も前からあるプロパティのひとつですが、いまだに私たちの多くが謎に包まれているようです。これを解く前に、ローカルにスコープされた新しい変数 --_gap-repeater-calc を定義する必要があることに気づきましたか?これをやってみましょう:

.repeating-flex {
  --_flex-repeat: var(--flex-repeat, var(--layout-default-repeat));
  --_repeating-flex-gap: var(--flex-gap, var(--layout-default-gap))/* 新しい変数 */
  --_gap-count: calc(var(--_flex-repeat) - 1);
  --_gap-repeater-calc: calc(
    var(--_repeating-flex-gap) / var(--_flex-repeat) * var(--_gap-count)
  );
  
  display: flexflex-wrap: wrap;
  gap: var(--_repeating-flex-gap);

  > * {
    flex: 1 1 calc((100% / var(--_flex-repeat)) - var(--_gap-repeater-calc));
  }

おっと、実は --_gap-repeater-calc が 3 番目の flex 値を適切に計算するために使用できる 2 番目の変数を作成しました。

上のコードから変数の抽象化を取り除くと、このようになります:

.repeating-flex {
  display: flexflex-wrap: wrap;
  gap: 3vmax

  > * {
    flex: 1 1 calc((100% / 3) - calc(3vmax / 3 * 2));
  }
}

うまくいけば、ブラウザがレイアウトのフレキシブルなアイテムのサイズを決めるためにどのような計算をしなければならないかがわかるでしょう。もちろん、変数の値が変わればその値も変わります。しかし、要するに、.repeating-flexユーティリティクラスの直接の子である要素は、各フレックスアイテムの初期サイズ(つまり、flex-basis)が calc()で計算された値に等しいことをブラウザに通知しながら、利用可能なスペースの量に基づいて成長(flex-grow: 1)と縮小(flex-shrink: 1)することができます。

ここにたどり着くまでにいくつかの新しい変数を導入しなければならなかったので、少なくともそれらが何をするのかを説明したいと思います:

  • gap-count: これは、--_flex-repeat`から1を引いた、レイアウトアイテム間のギャップの数を格納します。最初のアイテムの前や最後のアイテムの後にはギャップがないので、アイテム数のギャップは1つ少なくなります。
  • --_gap-repeater-calc: これは個々のアイテムのギャップサイズとアイテム間のギャップ数の合計から、合計ギャップサイズを計算します。

そこから、以下の式でより効率的に合計ギャップサイズを計算します:

calc(var(--_repeating-flex-gap) / var(--_flex-repeat) * var(--_gap-count))

これは変数が他の変数を参照している例なので、さらに分解してみましょう。この例では、すでにリピートカウントのプライベート変数を用意しており、--layout-default-repeat変数を設定することでデフォルトのリピーターにフォールバックします。

なぜなら、フレキシブルコンテナでは、コンテナの直接の子要素の flex 動作を定義する必要があるからです。

次に、個々のギャップサイズ(--_repeating-flex-gap)を反復的回数(--_flex-repeat))で割って、レイアウトの各アイテム間のギャップサイズを均等にします。そして、そのギャップサイズに1からギャップの総数を引いた値を --_gap-count 変数で掛けます。

これで反復的グリッドの完成です!かなり楽しい、少なくとも面白いでしょう?私はちょっとした数学が好きです。

これから作る最後の2つのレイアウト・ユーティリティ・クラスに移る前に、なぜ同じ変数をこんなにたくさん抽象化したいのか不思議に思うかもしれません。グローバルにスコープされた変数がローカルにスコープされた変数から参照され、その変数がまた別のルールセットにローカルにスコープされた変数から参照され、オーバーライドされるからです。単純にずっとグローバル変数で作業することもできますが、抽象化のために余計なステップを踏んでいます。

私がこの方法が好きな理由は以下の通りです:

  1. HTMLを覗いて、どのレイアウトアプローチが使われているかを即座に確認できるからです:.repeating-grid.repeating-flexか。
  2. 特定のコンフリクトに陥ることなく、スタイルを整然と保つことができます。

マークアップがどれだけ明確でわかりやすいか見てみましょう:

<section class="repeating-flex footer-usps">
  <div></div>
  <div></div>
  <div></div
</section

対応する CSS は、セマンティックな .footer-usps クラスのためのスリムなルールセットで、変数の値を更新するだけです:

.footer-usps {
  --flex-repeat: 3;
  --flex-gap: 2rem}

これで、レイアウトの種類、用途、変数の場所など、必要なコンテキストがすべてわかります。これは便利だと思いますが、物事を少し効率化したいのであれば、抽象化を追加することなくやり過ごすこともできるでしょう。

流動的グリッドとフレックスレイアウト

コンテナクエリとメディアクエリで反復的の数を操作できます。しかし、手作業で列を繰り返すのではなく、レイアウトコンテナ内の空いたスペースを自動的に埋める流動的レイアウトを使って、ブラウザに仕事をさせてみましょう。この2つのユーティリティを使うことで、わずかなコントロールは犠牲になるかもしれませんが、いくつかのCSSヒントを使うことで、レイアウトアイテムを「インテリジェントに」配置するブラウザの能力を活用することができます。

流動的グリッド

もう一度、変数から始めて、計算とスタイルルールに取り組んでいます。具体的には、列の最小幅を管理する --_fluid-grid-min という変数を定義します。

ちょっとつまらない例ですが、グリッドカラムの幅を少なくとも400px、隙間を20pxにしたいとします。この状況では、コンテナの幅が 820px より大きい場合、基本的に2カラムのグリッドを使用することになります。コンテナの幅が 820px より狭い場合、カラムはコンテナの幅いっぱいに広がります。

代わりに3カラムグリッドにしたい場合は、コンテナの幅を 1240px 程度にする必要があります。ギャップの最小値をコントロールすることが重要なのです。

.fluid-grid {
  --_fluid-grid-min: var(--fluid-grid-min, var(--layout-fluid-min));
  --_fluid-grid-gap: var(--grid-gap, var(--layout-default-gap))}

これで、.fluid-gridレイアウトのスタイルの計算と設定に必要な変数が確立されました。これが解凍したコードです:

 .fluid-grid {
  --_fluid-grid-min: var(--fluid-grid-min, var(--layout-fluid-min));
  --_fluid-grid-gap: var(--grid-gap, var(--layout-default-gap));

  display: gridgrid-template-columns: repeat(
    auto-fit、
    minmax(min(var(--_fluid-grid-min), 100%), 1fr)
  );
  gap: var(--_fluid-grid-gap)}

displaygrid に設定され、アイテム間の gap--fluid-grid-gap 変数に基づいています。マジックは grid-template-columns 宣言で行われます。

このグリッドは .repeating-grid ユーティリティと同じように repeat() 関数を使用します。関数の中で auto-fit と宣言することで、ブラウザは自動的にレイアウトコンテナの利用可能なスペースに可能な限り多くのカラムを詰め込みます。行に収まりきらないカラムは次の行に折り返され、そこで利用可能なスペースがすべて使用されます。

次に、列の幅の最小値と最大値を設定する minmax() 関数があります。ここで特別なのは、minmax()(これは repeat()関数にネストされていることを覚えておいてください)の中に min() という別の関数をネストしていることです。これは、各カラムの幅の最小値を --_fluid-grid-min から 100% の間のどこかに設定するロジックの一部で、100%--_fluid-grid-min が未定義、または 100% より小さい場合のフォールバックです。言い換えると、各カラムは少なくともグリッドコンテナの幅100%いっぱいになります。

minmax()maxの半分を 1fr に設定することで、各カラムが比例して大きくなり、等しいサイズのカラムが維持されるようになります。

Pen [Fluid grid forked] by utilitybend を参照してください。

Fluid Gridレイアウトについては以上です!とはいえ、これは 強いグリッド であり、特に ch などの現代的な相対単位と組み合わせると、コンテンツのサイズに基づいて1カラムから複数カラムに拡大縮小されるだけのグリッドになることに注意してください。

流動的フレックス

Repeating Flexレイアウトで書いたコードをFluid Flexレイアウトでも再利用できますが、各カラムのflex-basisをカラム数ではなく最小サイズで設定するだけです。

.fluid-flex {
  --_fluid-flex-min: var(--fluid-flex-min, var(--layout-fluid-min));
  --_fluid-flex-gap: var(--flex-gap, var(--layout-default-gap));

  display: flexflex-wrap: wrap;
  gap: var(--_fluid-flex-gap);

  > * {
    flex: 1 1 var(--_fluid-flex-min)}

これで4つ目のレイアウトユーティリティは完成です。しかし、ボーナスクラスが1つあり、Repeating GridとFluid Gridユーティリティと一緒に使うことで、それぞれのレイアウトをさらにコントロールできます。

オプション サブグリッドユーティリティ

Subgrid は便利なユーティリティで、任意のグリッドアイテムをそれ自身のグリッドコンテナに変換し、親コンテナのトラックサイジングを共有します。これはフルブラウザサポートを持ち、私たちのレイアウトシステムをより強固なものにしてくれます。そのため、子要素をレイアウトするためのグリッドコンテナとしてレイアウトアイテムが必要な場合、Repeating GridとFluid Gridレイアウトで使用するユーティリティとして設定することができます。

さあ、始めましょう:

.subgrid-rows {
  > * {
    display: grid;
    gap: var(--subgrid-gap, 0)grid-row: auto / span var(--subgrid-rows, 4)grid-template-rows: subgrid}
}

もちろん、2つの新しい変数があります:

  • subgrid-gap: グリッドアイテム間の垂直方向のギャップ。
  • subgrid-rows: グリッドの行数 デフォルトは 4 です。

ちょっとした課題があります: 行のサブグリッドアイテムをどのようにコントロールするか?

方法1: インラインスタイル

技術的にはインラインスタイルとしてHTMLで直接使用できる変数がすでにあります:

<section class="fluid-grid subgrid-rows style="--subgrid-rows: 4;">
  <!-- アイテム -->
</section>

変数がサブグリッドにどれだけ成長できるかを通知するので、これは魅力的に機能します。

方法2: :has() 疑似クラスを使う

このアプローチは冗長なCSSにつながりますが、簡潔さを犠牲にすることで、マークアップ内のインラインスタイルを更新することなく、レイアウトを自動化することができます。

これを見てください:

.subgrid-rows {
  &:has(> :nth-child(1):last-child) { --subgrid-rows1; }
  &:has(> :nth-child(2):last-child) { --subgrid-rows: 2; }.
  &:has(> :nth-child(3):last-child) { --subgrid-rows3; }
  &:has(> :nth-child(4):last-child) { --subgrid-rows4; }
  &:has(> :nth-child(5):last-child) { --subgrid-rows5; }
  /* etc. */

  > * {
    display: grid;
    gap: var(--subgrid-gap, 0)grid-row: auto / span var(--subgrid-rows, 5)grid-template-rows: subgrid}
}

セレクタ :has() は、サブグリッドの行がコンテナの最後の子項目であるかどうかをチェックします。例えば、2番目の宣言です:

&:has(> :nth-child(2):last-child) { --subgrid-rows: 2; }.

これは、「もしこれが2番目のサブグリッドアイテムで、たまたまコンテナの最後のアイテムであった場合、行数を 2 に設定しなさい 」 と言っているようなものです。

これが強引すぎるかどうかはわかりませんが、CSSでこれを実行できるのは素晴らしいことです。

最後に必要なのは、子要素にコンテナを宣言することです。カラムには一般的なクラス名である .grid-item をつけ、必要であればオーバーライドできるようにします。その一方で、それぞれのカラムを container として設定し、(メディアクエリでビューポートのサイズに対応するのではなく)特定のサイズになったらレイアウトを更新するようにします。

:is(.fluid-grid:not(.subgrid-rows),
.repeating-grid:not(.subgrid-rows),
.repeating-flex, .fluid-flex) {
    > * {
    container: var(--grid-item-container, grid-item) / inline-size;
  }
}

乱暴なセレクタですが、:is()擬似クラスのおかげで冗長さは最小限に抑えられています。これは基本的に、.subgrid-rowsに漏れて不用意に直接の子を選択することなく、他のユーティリティの直接の子を選択します。

containerプロパティは、container-namecontainer-typeをスラッシュ(/)で区切って一つの宣言にまとめた省略記法です。コンテナの名前は私たちの変数の1つに設定され、タイプは常に inline-size (つまり、横書きモードでの幅)になります。

container-typeプロパティはグリッド コンテナにのみ適用でき、グリッド アイテムには適用できません。つまり、grid-template-rows: subgrid 値と組み合わせることができません。そのため、これらのインスタンスを除外するために、より複雑なセレクタを記述する必要がありました。

デモ

以下のデモをご覧ください。

Penの Grid system playground [forked] by utilitybend を参照してください。

このデモでは、私たちが作成したすべてのCSSを含む別のペンからスタイルを引っ張ってきています。そのため、HTMLの親コンテナの.fluid-flexクラス名をレイアウトユーティリティの別のものに置き換えると、それに応じてレイアウトが更新され、両者を比較することができます。

これらのクラスは以下の通りです:

  • .repeating-grid
  • .repeating-flex
  • .fluid-grid
  • .fluid-flex

もちろん、オプションの .subgrid-rows クラスと .repeating-grid および .fluid-grid ユーティリティを組み合わせることで、グリッドアイテムをグリッドコンテナにすることもできます。

結論: 一度書いたら再利用

これはかなりの旅でしたね。情報量が多いように見えるかもしれませんが、私たちは一度書くだけでよく、モダンなCSSのアプローチを使用して特定のタイプのレイアウトが必要な場所であれば、実質的にどこでも使用できるものを作りました。私は、これらのユーティリティがあなたの仕事の多くを助けるだけでなく、単にレイアウト構成のために使用している可能性のあるCSSフレームワークへの依存を減らすことができると強く信じています。

これは、私がこれまで見てきた多くのテクニックを組み合わせたものです。そのうちの1つは、CSS Day 2023でStephanie Ecklesが行ったプレゼンテーションです。これまで私たちが使っていたものを、現代的なCSSで解決してくれるのは、とてもうれしいことです。ステファニーのデモは最初からすっきりしていて、ウェブ開発の他の多くの分野がますます複雑になっている中で、とても新鮮でした。

CSS Day 2023で多くを学んだ後、私は自分でSubgridを使って遊び、実験から得たさまざまなアイデアを公開しました。これだけで、モダンなCSSレイアウトアプローチがいかに拡張性に富んでいるかがわかり、おそらく長い間頼りにできるユーティリティのセットを作る気になりました。

決して、これらのユーティリティが完璧で、どこでも使うべきであるとか、<framework-du-jour>よりも優れているとか、あなたや他の誰かを説得しようとしているわけではありません。ひとつ確かなことは、この記事で取り上げたアイデアを試してみることで、CSSがレイアウト作業をこれまでよりもずっと便利で強固なものにできることを、しっかりと実感できるということです。

もしよろしければ、この記事から何かを生み出し、コメントで共有してください!