Начало работы с запросами стилей

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

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 110.
  • Сафари: 16.

Source

Однако спецификация встраивания стилей включает в себя не только запросы по размеру; она также позволяет запрашивать значения стилей родительского элемента. Начиная с Chromium 111, вы сможете применять встраивание стилей к значениям пользовательских свойств и запрашивать значение пользовательского свойства у родительского элемента.

Browser Support

  • Chrome: 111.
  • Край: 111.
  • Firefox: 151.
  • Сафари: 18.

Это означает, что у нас есть еще более логичный контроль над стилями в 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>

Для использования стилевых запросов необходимо сначала настроить элемент-контейнер. Это требует несколько иного подхода в зависимости от того, обращаетесь ли вы к прямому или косвенному родительскому элементу.

Запрос к непосредственным родителям

Диаграмма запроса стиля.

В отличие от запросов стилей, вам не нужно применять контейнеризацию с помощью свойства container-type или container к .card-container чтобы .card мог запрашивать стили своего непосредственного родительского элемента. Однако нам необходимо применять стили (в данном случае значения пользовательских свойств) к контейнеру (в данном случае .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 с помощью запросов стиля. Именно поэтому используется обертка style() , чтобы сделать разницу очевидной: @container style(min-width: 200px) { … } .

Стилистические услуги для непрямых родителей

Если вы хотите задать стили для любого элемента, который не является его непосредственным родительским элементом, вам необходимо указать этому элементу container-name . Например, мы можем применить стили к элементу .card на основе стилей элемента .card-list , указав элементу .card-list container-name и сославшись на него в запросе стилей.

/* 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) , что позволило бы лучше использовать общие стили и уменьшить повторение. Эта возможность в настоящее время обсуждается в рабочей группе.

Карточки погоды

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

Демонстрация погодных карточек.

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

@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, который будет отображаться на странице) от применяемых стилей. Стили записываются как возможные варианты, которые находятся внутри стиля компонента, в то время как конечная точка может отправлять данные, которые затем будут использоваться для стилизации компонента. Вы можете использовать одно значение, как в первом случае, обновляя значение --detail , или несколько переменных, как во втором случае (устанавливая либо --rainy , --cloudy , либо --sunny . И самое приятное, что вы также можете комбинировать эти значения, проверяя одновременно --sunny и --cloudy что может отобразить стиль частично облачного освещения.

Обновление значений пользовательских свойств с помощью JavaScript можно выполнить без проблем, либо во время настройки модели DOM (т.е. при создании компонента во фреймворке), либо в любое время, используя <parentElem>.style.setProperty('--myProperty', <value>) .

Вот демонстрационный пример, который всего несколькими строками кода обновляет параметр --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);
})

Функции, подробно описанные в этой статье, — это только начало. Вы можете ожидать от запросов контейнера и других возможностей, которые помогут вам создавать динамичные, адаптивные интерфейсы. Что касается запросов стилей, то здесь еще есть несколько открытых вопросов. Один из них — реализация запросов стилей для CSS-стилей, выходящих за рамки пользовательских свойств. Это уже часть текущего уровня спецификации, но пока не реализовано ни в одном браузере. Ожидается, что логическая оценка контекста будет добавлена ​​в текущий уровень спецификации после решения этой нерешенной проблемы , а запросы диапазона планируются на следующий уровень спецификации.