デザイン トークンの反応性

背景と概要

多くの場合、作成者はコンポーネントのデザインにコンテキストの変更を加える必要があります。従来、作成者はこのような変更を適用するためにセレクタを使用する必要がありました。これは多くの場合、多くのデザイン トークンがカスタム プロパティとして存在できる一方で、高次のデザイン トークンはセレクタ パターン(クラス名や属性規則を使用)として、または JavaScript フレームワークの props / コンテキストとしてしかエンコードできないことを意味していました。

コンテナ スタイルクエリを使用すると、作成者は、祖先要素の計算されたカスタム プロパティ値に基づいて要素のスタイルを設定できます。つまり、作成者は、プレゼンテーションにマークアップや JavaScript を使用するのではなく、スタイルシート内に意味のあるデザイン トークン値を記述できます。

導入方法

コンテナ スタイルクエリを使用してリアクティブなデザイン トークンを実装するのは、非常に簡単です。

  1. 上位のデザイン トークンをコンテナのカスタム プロパティとして設定します。これは、登録済みのカスタム プロパティである必要はありません。
  2. @container style() ルールを使用して、そのカスタム プロパティの値をクエリします。
  3. コンテナ内の子孫要素に適切なスタイルを適用します。

次の点にご注意ください。

  • スタイルクエリでクエリされるコンテナに container-type または container-name を設定する必要はありませんが、container-name を使用すると、より具体的なクエリを実行できます。
  • コンテナ自体は、コンテナ スタイルクエリでスタイル設定できません。

上記の実装手順の基本的な例を次に示します。

<div class="features">
  <div class="card"></div>
  <div class="card"></div>
</div>
<div class="bugs">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>
.features {
  --density: spacious;
}

.bugs {
  --density: compact;
}

@container style(--density: compact) {
  .card {
    padding: 8px;
  }
}

@container style(--density: spacious) {
  .card {
    padding: 24px;
  }
}

代替戦略

コンテナ スタイルクエリが Baseline でサポートされるまでは、すべてのブラウザで利用可能である必要があるコア機能にコンテナ スタイルクエリを使用することはおすすめしません。コンテナ スタイルクエリのメリットを損なわないフォールバックや、独自の制限があるフォールバックを作成することは簡単ではないためです。たとえば、UI 密度のユーザー設定が、すべてのエクスペリエンスで利用可能にする必要があるコア機能とは見なされない場合、コンテナ スタイルクエリを使用して、フォールバックなしで機能を実装できます。

代わりにセレクタを使用する

コア機能については、セレクタを使用する代替アプローチを使用する必要があります。この例では、data-density 属性を使用して、カスタム プロパティではなくマークアップで密度デザイン トークンをエンコードしています。

<div class="features" data-density="spacious">
  <div class="card"></div>
  <div class="card"></div>
</div>
<div class="bugs" data-density="compact">
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>
/* This example uses `:where()` to avoid increasing specificity */
:where([data-density="compact"]) .card {
  padding: var(--card-padding-compact);
}

:where([data-density="spacious"]) .card {
  padding: var(--card-padding-spacious);
}

このフォールバック アプローチの大きな制限は、data-density 属性が設定された要素のネストがサポートされないことです。セレクタの特異性が同じであるため、表示順序を使用してスタイルが決定されます(つまり、[data-density="spacious"] は常に [data-density="compact"] よりも優先されます)。

プログレッシブ エンハンスメントとしてスタイルクエリを使用する

推奨はしませんが、コア機能のプログレッシブ エンハンスメントとしてスタイルクエリを使用する場合は、重複を避けるために、カスタム プロパティをいくつか作成してから、スタイルクエリを追加します。フォールバック アプローチでコンテナ要素を選択する際に、詳細度が高くならないように :where() を使用していることを確認します。

.card {
  --card-padding-compact: 8px;
  --card-padding-spacious: 24px;
}

:where([data-density="compact"]) .card {
  padding: var(--card-padding-compact);
}

:where([data-density="spacious"]) .card {
  padding: var(--card-padding-spacious);
}

/* Use style queries as a progressive enhancement: same specificity, so order of appearance is used */

@container style(--density: compact) {
  .card {
    padding: var(--card-padding-compact);
  }
}

@container style(--density: spacious) {
  .card {
    padding: var(--card-padding-spacious);
  }
}

CSS のみを使用した機能チェック

コンテナ スタイルクエリを機能チェックして、それに依存する UI を条件付きで表示する必要がある場合は、スタイルクエリを使用して行うことができます。

:root {
  --style-queries-supported: check;
}

.density-toggle {
  display: none;
}

@container style(--style-queries-supported) {
  .density-toggle {
    display: revert;
  }
}

JavaScript を使用した機能チェック

JavaScript での機能チェックは、コンテナ スタイルクエリが追加される前から CSSContainerRule インターフェースが存在していたため、少し複雑です。そのため、この目的には信頼できません。代わりに、スタイルクエリで設定されている既知のプロパティの計算値をチェックする必要があります。

この例では、視覚効果がないカスタム プロパティを使用しています。

:root {
  --style-queries-supported: check;
}

@container style(--style-queries-supported: check) {
  body {
    --style-queries-supported: yes;
  }
}

次に、次のように JavaScript で計算された値を確認します。

if (getComputedStyle(document.body).getPropertyValue("--style-queries-supported") === "yes") {
  // Use container style queries
} else {
  // Use fallback strategy
}