スタイルクエリ スタートガイド

親のインライン サイズとコンテナのクエリ単位値をクエリする機能は、すべての最新のブラウザ エンジンで最近安定してサポートされるようになりました。

対応ブラウザ

  • 105
  • 105
  • 110
  • 16

ソース

ただし、包含仕様には、サイズクエリ以上のものが含まれています。親のスタイル値をクエリすることもできます。Chromium 111 以降では、カスタム プロパティ値にスタイル封じ込めを適用し、カスタム プロパティの値を親要素に照会できます。

対応ブラウザ

  • 111
  • 111
  • x
  • x

ソース

これにより、CSS でスタイルを論理的に制御できるようになり、アプリケーションのロジックやデータレイヤをスタイルからより適切に分離できるようになります。

CSS Containment モジュール レベル 3 の仕様では、サイズとスタイルのクエリをカバーしており、プロパティと値のペア(font-weight: 800 など)を含む任意のスタイルを親からクエリできます。ただし、この機能のリリースに伴い、現在のところ、スタイルクエリは CSS カスタム プロパティ値でのみ機能します。これは、スタイルを組み合わせたり、データとデザインを分離する場合に非常に便利です。CSS カスタム プロパティでスタイルクエリを使用する方法を見ていきましょう。

スタイルクエリを使ってみる

次のような HTML があるとします。

<ul class="card-list">
  <li class="card-container">
    <div class="card">
      ...
    </div>
  </li>
</ul>

スタイルクエリを使用するには、まずコンテナ要素を設定する必要があります。そのためには、直接親にクエリするか、間接的な親にクエリするかによって、少し異なるアプローチが必要になります。

直接の親のクエリ

スタイルクエリの図。

スタイルクエリとは異なり、.card が直接の親のスタイルをクエリできるようにするために、container-type プロパティまたは container プロパティを使用して包含を .card-container に適用する必要はありません。ただし、スタイル(この場合はカスタム プロパティ値)をコンテナ(この場合は .card-container)または DOM でスタイル設定する要素を含む要素に適用する必要があります。無限ループが発生する可能性があるため、そのクエリを使用してスタイル設定している直接的な要素に、クエリ対象のスタイルを適用することはできません。

親に直接クエリを実行するには、次のように記述します。

/* styling .card based on the value of --theme on .card-container */
@container style(--theme: warm) {
  .card {
    background-color: wheat;
    border-color: brown; 
    ...
  }
}

スタイルクエリでは、クエリが style() でラップされていることにお気づきでしょうか。これは、スタイルとサイズの値を明確化するためです。たとえば、コンテナの幅を @container (min-width: 200px) { … } とするクエリを作成できます。親コンテナの幅が 200 ピクセル以上であれば、スタイルが適用されます。ただし、min-width を CSS プロパティで指定することも可能です。その場合、スタイルクエリを使って min-width の CSS 値を取得できます。そのため、style() ラッパーを使用して違いを明確にします: @container style(min-width: 200px) { … }

間接的な親のスタイル設定

直接の親ではない要素のスタイルをクエリする場合は、その要素に container-name を指定する必要があります。たとえば、.card-list のスタイルに基づいて .card にスタイルを適用するには、.card-listcontainer-name を指定してスタイルクエリで参照します。

/* styling .card based on the value of --moreGlobalVar on .card-list */
@container cards style(--moreGlobalVar: value) {
  .card {
    ...
  }
}

一般的にベスト プラクティスとして、クエリの対象が明確にわかる名前をコンテナに付けて、コンテナにより簡単にアクセスできるようにすることをおすすめします。これが役立つ例としては、.card 内の要素を直接スタイル設定する場合が挙げられます。.card-container に名前付きコンテナがない場合、直接クエリできません。

しかし、これらはすべて、実際にやってみるともっと理にかなっています。いくつか例を見てみましょう。

クエリのスタイルを設定する

複数の商品カード(「新品」または「在庫僅少」のタグが付いたもの、赤い背景の「在庫僅少」カード)を示すデモ画像。

スタイルクエリは、複数のバリエーションを持つ再利用可能なコンポーネントがある場合や、すべてのスタイルを制御できないが特定のケースでは変更を適用する必要がある場合に特に便利です。この例は、同じカード コンポーネントを共有する一連の商品カードを示しています。商品カードによっては、--detail という名前のカスタム プロパティによってトリガーされる「新規」や「在庫不足」など、追加の詳細情報や備考が含まれる場合があります。また、商品が [在庫僅少] の場合は、濃い赤の枠線の背景が表示されます。このタイプの情報はサーバー レンダリングされる可能性があり、次のようにインライン スタイルを使用してカードに適用できます。

 <div class="product-list">
  <div class="product-card-container" style="--detail: new">
    <div class="product-card">
      <div class="media">
        <img .../>
      <div class="comment-block"></div>
    </div>
  </div>
  <div class="meta">
    ...
  </div>
  </div>
  <div class="product-card-container" style="--detail: low-stock">
    ...
  </div>
  <div class="product-card-container">
    ...
  </div>
  ...
</div>

この構造化データを基に、--detail に値を渡し、この CSS カスタム プロパティを使用してスタイルを適用できます。

@container style(--detail: new) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'New';
    border: 1px solid currentColor;
    background: white;
    ...
  }
}

@container style(--detail: low-stock) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'Low Stock';
    border: 1px solid currentColor;
    background: white;
    ...
  }
  
  .media-img {
    border: 2px solid brickred;
  }
}

上記のコードを使用すると、--detail: low-stock--detail: new にチップを適用できますが、コードブロックに冗長性があることに気付くかもしれません。現時点では、--detail の有無だけを @container style(--detail) でクエリする方法はなく、スタイルを共有しやすくなり、繰り返しを減らすことができます。この機能は現在、ワーキング グループで検討中です。

天気情報カード

前の例では、1 つのカスタム プロパティと複数の有効な値を使用してスタイルを適用していました。しかし、複数のカスタム プロパティを使用してクエリを実行し、組み合わせて使用することもできます。天気情報カードの例を次に示します。

天気情報カードのデモ。

これらのカードの背景のグラデーションやアイコンのスタイルを設定するには、「曇り」、「雨」、「晴れ」などの天候の特性を探します。

@container style(--sunny: true) {
  .weather-card {
    background: linear-gradient(-30deg, yellow, orange);
  }
  
  .weather-card:after {
    content: url(<data-uri-for-demo-brevity>);
    background: gold;
  }
}

このようにして、各カードの固有の特性に基づいてスタイルを設定できます。ただし、メディアクエリと同じ方法で and コンビネータを使用して、特徴(カスタム プロパティ)の組み合わせのスタイルを設定することもできます。たとえば、曇りと晴れの両方の日は次のようになります。

@container style(--sunny: true) and style(--cloudy: true) {
    .weather-card {
      background: linear-gradient(24deg, pink, violet);
    }
  
  .weather-card:after {
      content: url(<data-uri-for-demo-brevity>);
      background: violet;
  }
}

設計からデータを分離する

どちらのデモでも、データレイヤー(ページにレンダリングされる DOM)を適用されたスタイルから分離することで、構造的なメリットが得られます。スタイルは、コンポーネント スタイル内に存在する可能性のあるバリアントとして記述しますが、エンドポイントは、コンポーネントのスタイルを設定するためのデータを送信します。最初のケースのように --detail 値を更新する単一の値を使用することも、2 番目のケースのように複数の変数(--rainy--cloudy--sunny のいずれかを設定)を使用することもできます。最大のメリットは、これらの値を組み合わせることができることです。--sunny--cloudy の両方をチェックすると、部分的に曇っている可能性があります。

JavaScript によるカスタム プロパティ値の更新は、DOM モデルのセットアップ時(フレームワーク内のコンポーネントの構築時)でも、いつでも <parentElem>.style.setProperty('--myProperty’, <value>) を使用して更新できます。I

次のデモでは、数行のコードでボタンの --theme を更新し、スタイルクエリとそのカスタム プロパティ(--theme)を使用してスタイルを適用するデモを行います。

スタイルクエリを使用してカードのスタイルを設定します。カスタム プロパティ値の更新に使用する JavaScript は次のとおりです。

const themePicker = document.querySelector('#theme-picker')
const btnParent = document.querySelector('.btn-section');

themePicker.addEventListener('input', (e) => {
  btnParent.style.setProperty('--theme', e.target.value);
})

この記事で説明する機能はほんの始まりにすぎません。コンテナクエリでは、動的でレスポンシブなインターフェースの構築に役立つ多くの要素が想定されています。特にスタイルクエリに関しては、未解決の問題がいくつかあります。一つは、カスタム プロパティ以外の CSS スタイルに対するスタイルクエリの実装です。これは現在の仕様レベルにすでに含まれていますが、どのブラウザにもまだ実装されていません。ブール値のコンテキスト評価は、未解決の問題が解決されたときに現在の仕様レベルに追加されることが予想されます。一方、範囲クエリは仕様の次のレベルで計画されています。