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

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

対応ブラウザ

  • 105
  • 105
  • 110
  • 16

ソース

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

対応ブラウザ

  • 111
  • 111
  • x
  • x

ソース

これにより、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 がその直接親のスタイルをクエリできるようにするために、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-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 にチップを適用できますが、コードブロックに冗長性があることに気づいたかもしれません。現時点では、--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)を適用したスタイルから分離するという構造的なメリットがあります。スタイルは、コンポーネント スタイル内に存在する可能なバリアントとして記述され、エンドポイントは、コンポーネントのスタイル設定に使用するデータを送信できます。1 つ目の方法(--detail の値の更新など)で 1 つの値を使用することも、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 スタイルのスタイルクエリの実装です。この機能は、すでに現在の仕様レベルに含まれていますが、どのブラウザにも実装されていません。未解決の問題が解決した時点で、ブール型コンテキスト評価が現在の仕様レベルに追加され、範囲クエリが次の仕様レベルの仕様に追加される予定です。