Проблема
Часто нам нужно комбинировать критерии фильтрации разных типов, например селекторы и запросы медиа, запросы медиа и контейнеров и т. д.
Например, можно объединить (prefers-color-scheme: dark) с селектором, который переопределяет цветовую схему (например, :root:has(> head > meta[name="color-scheme"][content="dark"]) или :root.dark), чтобы применять определенные стили в светлом или темном режиме независимо от того, как была задана цветовая схема.
Наивный подход приводит к дублированию кода:
/* Light mode styles */
.card.featured {
font-weight: 400;
}
#logo-dark {
display: none;
}
@media (prefers-color-scheme: dark) {
/* Dark mode styles */
:root:not(.light) {
.card.featured {
font-weight: 300;
}
#logo-dark {
display: inline;
}
#logo {
display: none;
}
}
}
/* Dark mode styles, again */
:root.dark {
.card.featured {
font-weight: 300;
}
#logo-dark {
display: inline;
}
#logo {
display: none;
}
}
Что делать вместо этого
Сохраните результат комбинированного фильтра в дополнительном параметре, а затем используйте запросы стилей контейнера, чтобы задать правила на основе этого параметра.
ВАЖНО! Из-за особенностей работы запросов контейнера этот метод работает, только если мы можем задать дополнительный параметр для предка любых правил, которые должны соответствовать.
1. Отделение логики выбора от применяемых правил
:root {
--color-scheme: light;
@media (prefers-color-scheme: dark) {
--color-scheme: dark;
}
&:has(> head > meta[name="color-scheme"][content="dark"]) {
--color-scheme: dark;
}
&:has(> head > meta[name="color-scheme"][content="light"]) {
--color-scheme: light;
}
}
2. Ключ дополнительного параметра для примененных правил
@container not style(--color-scheme: dark) {
.card.featured {
font-weight: 400;
}
#logo-dark {
display: none;
}
}
@container style(--color-scheme: dark) {
.card.featured {
font-weight: 300;
}
#logo {
display: none;
}
}
3. НЕОБЯЗАТЕЛЬНО. Вспомогательные классы для переключения видимости
Очень распространенный случай – переключение видимости в зависимости от того, соответствуют ли критерии.
Утилитарные классы должны иметь приоритет над произвольными стилями компонентов (поэтому используется !important), а их display неизвестен, поэтому они не могут использовать переопределения. Ограничьте их с помощью not style(...):
@container not style(--color-scheme: dark) {
.if-dark {
display: none !important;
}
}
@container not style(--color-scheme: light) {
.if-light {
display: none !important;
}
}
Резервные объявления
Существует два типа резервных объявлений, в зависимости от того, насколько критично ветвление и что считается приемлемым резервным объявлением.
- Для критического ветвления, когда все состояния должны работать правильно, даже если запросы стилей не поддерживаются, вы просто не можете использовать запросы стилей. Вместо этого используйте описанный выше простой подход, дублируя правила для каждого штата. Служебные классы можно использовать, если они определены таким же образом.
- Для некритичного ветвления, когда допустимо состояние по умолчанию, вы можете определить базовые стили вне правила
@containerи использовать запросы стилей только для переопределения. Это позволяет сохранить большую часть преимуществ такого подхода, а его основным недостатком является связывание значений, которое вводится переопределениями.
#logo-dark {
display: none;
}
@container style(--color-scheme: dark) {
#logo-dark {
display: inline; /* override */
}
#logo {
display: none;
}
}