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

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

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 110.
  • Safari: 16.

Source

ただし、包含仕様にはサイズクエリだけでなく、親のスタイル値のクエリも含まれています。Chromium 111 以降では、カスタム プロパティ値にスタイル包含を適用し、カスタム プロパティの値について親要素にクエリを実行できるようになります。

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: 151.
  • Safari: 18.

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

サイズとスタイルのクエリを扱う CSS Containment Module Level 3 仕様では、font-weight: 800 などのプロパティと値のペアを含め、親から任意のスタイルをクエリできます。ただし、この機能のロールアウトでは、スタイルクエリは現在 CSS カスタム プロパティ値でのみ機能します。これは、スタイルを組み合わせてデザインからデータを分離するのに非常に役立ちます。CSS カスタム プロパティでスタイルクエリを使用する方法を見てみましょう。

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

次の HTML があるとします。

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

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

直接の親をクエリする

スタイルクエリの図。

スタイルクエリとは異なり、.card が直近の親のスタイルをクエリできるようにするために、.card-containercontainer-type プロパティまたは 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-listcontainer-name を指定し、スタイル クエリでそれを参照することで、.card-list のスタイルに基づいて .card にスタイルを適用できます。

/* 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 にチップを適用できますが、コードブロックに冗長性があることに気づいたかもしれません。現在、@container style(--detail)--detail の存在のみをクエリする方法はありません。この方法を使用すると、スタイルをより適切に共有し、繰り返しを減らすことができます。この機能は現在、ワーキング グループで議論されています

天気カード

前の例では、複数の可能な値を持つ単一のカスタム プロパティを使用してスタイルを適用しました。しかし、複数のカスタム プロパティを使用してクエリを実行することもできます。次の天気カードの例をご覧ください。

天気カードのデモ。

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

@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);
})

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