Адаптация семантических компонентов

Используйте запросы стилей (@container style()), чтобы активировать контекстные вариации без жесткого кодирования того, что создает каждый контекст или реагирует на него. Это уменьшает связь с системой проектирования и улучшает расширяемость и инкапсуляцию компонентов.

Например, правило section.featured { --surface: featured } может задавать контекст, а целевая страница может по умолчанию использовать его через main { --surface: featured }. Компоненты реагируют на --surface: featured: на карточке появляется значок промоакции, кнопка по умолчанию становится заполненной и т. д.

Запросы стилей позволяют обеим сторонам оставаться независимыми: рекомендуемые поверхности никогда не называют элементы, которые к ним адаптируются, а компоненты никогда не проверяют, что создало контекст. Каждую сторону можно расширять независимо от другой, добавляя новые компоненты и рекомендуемые поверхности. Селекторы потомков (например, .featured .button) требуют обратного: селектор должен кодировать обе стороны отношений и должен меняться всякий раз, когда меняется любая из сторон.

Когда использовать это руководство

В этом руководстве рассматриваются изменения поведения: компонент решает, что отображать, как расположить себя или какой вариант представить в ответ на свой контекст – скрытие или отображение значка, переключение направления макета или замена варианта кнопки.

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

Выбор подходящего инструмента

Используйте запрос стиля, когда обе стороны контекстной связи могут изменяться независимо друг от друга – новые элементы могут начать создавать контекст, а новые компоненты – реагировать на него. Косвенность окупается расширяемостью и инкапсуляцией, а запросы стилей пересекают границы теневой модели DOM, чего не могут сделать селекторы-потомки. Если такая гибкость не требуется, лучше использовать более простой инструмент:

  • Селекторы потомков (.toolbar .button) – фиксированная одноразовая связь, которую нельзя использовать повторно или расширить. Применяется YAGNI — не добавляйте косвенность, которую вы не будете использовать.
  • Запросы размера (@container (min-width: …)) – изменения макета и плотности, обусловленные доступным пространством, а не семантическим состоянием.
  • Запросы стилей (@container style(…)): семантическое состояние – что компонент должен отображать, упорядочивать или представлять в данном контексте.

Директивы по реализации

  1. Определите флаг семантического контекста, используя специальное свойство CSS в контейнере.
  2. НЕ задавайте свойство container-type для запросов стилей. Они запрашивают унаследованные дополнительные параметры у ближайшего предка и не требуют явного контекста контейнера.
  3. ОБЯЗАТЕЛЬНО. Используйте @container style(--property: value), чтобы применять условные стили к дочерним элементам. Запросы стилей не могут соответствовать элементу, для которого задано свойство, а только его потомкам.
  4. РЕКОМЕНДУЕТСЯ использовать запросы стилей вместо селекторов потомков, когда связь должна оставаться расширяемой или пересекать границы компонентов/теневой модели DOM (см. раздел Выбор подходящего инструмента выше). Однако селекторы потомков можно использовать для фиксированных одноразовых связей, где косвенность не дает преимуществ.

Пример реализации

/* 1. Set the context on a layout container */
.featured {
  --surface: featured; /* Semantic context flag for children */
}

/* 2. Component reacts to inherited flag */
.button {
  /* Default: Outlined informational style */
  background: transparent;
  border: 1px solid currentColor;
}

.badge {
  /* Default: Hidden — only revealed in featured surfaces */
  display: none;
}

@container style(--surface: featured) {
  .button {
    /* Automatic switch to Filled promotional style */
    background: var(--brand-accent);
    color: white;
    border: none;
  }

  .badge {
    /* Reveal decorative elements only in featured context */
    display: block;
  }
}

Резервные стратегии

Пока запросы стилей контейнеров не станут широко доступны, используйте их как прогрессивное улучшение: задайте вариант по умолчанию на основе селектора, который будет работать везде, и разрешите запросу стиля переопределять его, когда это поддерживается. Используйте :where() в селекторе контекста, чтобы запрос стиля (та же специфичность, позже в порядке источника) выигрывал автоматически.

/* Default styles — work everywhere */
.button {
  background: transparent;
  border: 1px solid currentColor;
}

.badge {
  display: none;
}

/* Selector-based contextual styles — broad browser support */
:where(.featured) .button {
  background: var(--brand-accent);
  color: white;
  border: none;
}

:where(.featured) .badge {
  display: block;
}

/* Progressive enhancement: style queries override when supported */
@container style(--surface: featured) {
  .button {
    background: var(--brand-accent);
    color: white;
    border: none;
  }

  .badge {
    display: block;
  }
}

Резервный селектор связывает контекстный стиль с именем класса, что привязывает компонент к его размещению в DOM. Когда запросы стилей будут широко поддерживаться, правило :where() можно будет удалить.