Реактивность токенов дизайна

Общие сведения

Часто автору необходимо внести контекстные изменения в дизайн компонента. Раньше для этого приходилось использовать селекторы. Это часто означало, что, хотя многие из их токенов дизайна могли существовать как пользовательские свойства, токены дизайна более высокого порядка могли быть закодированы только как шаблон селектора (т.е. с использованием имени класса или соглашения об атрибутах) или как реквизиты/контекст в фреймворке JavaScript.

Запросы стилей контейнера позволяют авторам стилизовать элементы на основе вычисленного значения пользовательского свойства элемента-предка. Это означает, что авторы могут писать значимые значения токенов дизайна в своих таблицах стилей, а не полагаться на разметку или 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 не поддерживает запросы стилей контейнеров, НЕ РЕКОМЕНДУЕТСЯ использовать их для основных функций, которые должны быть доступны во всех браузерах, поскольку для них непросто создать резервный вариант, который не лишает их преимуществ или имеет свои собственные ограничения. Например, если плотность пользовательского интерфейса не считается основной функцией, которая должна быть доступна во всех интерфейсах, то для реализации этой функции без резервного варианта можно использовать запросы стиля контейнера.

Использование селекторов

Для основных функций следует использовать альтернативный подход с селекторами. В этом примере используется атрибут 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

Если вам нужно проверить поддержку запросов стиля контейнера, чтобы условно отобразить некоторые элементы интерфейса, которые на них полагаются, вы можете использовать запрос стиля:

: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
}