Эта статья — перевод оригинальной статьи "Getting Started with Style Queries"

Также я веду телеграм канал “Frontend по-флотски”, где рассказываю про интересные вещи из мира разработки интерфейсов.

Вступление

Size container queries и container query units недавно достигли стабильной поддержки во всех современных браузерных движках.

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

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

Спецификация CSS Containment Module Level 3, которая охватывает запросы размера и стиля, позволяет получать любые стили у родителя, включая пары ключей и значений, например, font-weight: 800. Однако в процессе развертывания этой функции получение стилей в настоящее время работает только со значениями CSS custom property. Это всё равно очень полезно для комбинирования стилей и отделения данных от дизайна. Давайте рассмотрим, как использовать style queries с CSS custom properties:

Начало работы с Style Queries

Допустим, у нас есть следующий HTML:

<ul class="card-list">
  <li class="card-container">
    <div class="card">
      ...
    </div class="card">
  </li>
</ul>

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

Запрос прямых родителей

В отличие от Style Queries, вам не нужно использовать контейнеры через свойства container-type или container к классу .card-container, чтобы .card могла получать стили своего непосредственного родителя. Однако нам необходимо применить стили (значения CSS custom property в данном случае) к контейнеру (.card-container в данном случае) или любому элементу, содержащему элемент, который мы стилизуем в DOM. Мы не можем применять стили, которые мы запрашиваем, к непосредственному элементу, который мы стилизуем с помощью этого запроса, поскольку это может привести к бесконечным циклам.

Чтобы напрямую обратиться к родителям, вы можете написать:

@container style(--theme: warm) {
  .card {
    background-color: wheat;
    border-color: brown; 
    ...
  }
}

Возможно, вы заметили, что запрос стиля обертывает запрос с помощью style(). Это сделано для того, чтобы отделить значения размеров от стилей. Например, вы можете написать запрос на ширину контейнера в виде @container (min-width: 200px) { ... }. Это позволит применить стили, если родительский контейнер имеет ширину не менее 200px. Однако min-width также может быть CSS custom property, и вы можете запросить CSS-значение min-width с помощью стилевых запросов. Вот почему вы используете обертку style(), чтобы сделать разницу очевидной: @container style(min-width: 200px) { ... }.

Стилизация косвенных родителей

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

@container cards style(--moreGlobalVar: value) {
  .card {
    ...
  }
}

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

Но все это имеет гораздо больше смысла на практике. Давайте рассмотрим несколько примеров:

Style queries в действии

Style queries особенно полезны, когда у вас есть многократно используемый компонент с несколькими вариантами, или когда у вас нет контроля над всеми стилями, но вам нужно применить изменения в определенных случаях. В данном примере показан набор карточек товаров, которые используют один и тот же компонент карточки. Некоторые карточки товаров имеют дополнительные детали/примечания, такие как "Новинка" или "Низкий запас", вызываемые через custom property с именем --details. Кроме того, если товар находится в состоянии "Низкий запас", он выделяется красной рамкой. Этот тип информации, вероятно, отображается на сервере и может быть применен к карточкам с помощью инлайн стилей, как показано ниже:

 <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 и использовать его для применения стилей:

@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;
  }
}

CodePen результат

Приведенный выше код позволяет нам применить фишку для --detail: low-stock и --detail: new, но вы могли заметить некоторую избыточность в блоке кода. В настоящее время не существует способа запросить только наличие --detail с помощью @container style(--detail), что позволило бы лучше распределить стили и уменьшить количество повторений. Эта возможность в настоящее время обсуждается в рабочей группе.

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

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

Чтобы стилизовать фоновые градиенты и значки для этих карточек, найдите характеристики погоды, такие как "cloudy", "rainy" или "sunny":

@container style(--sunny: true) {
  .weather-card {
    background: linear-gradient(-30deg, yellow, orange);
  }
  
  .weather-card:after {
    content: url(<data-uri-for-demo-brevity>);
    background: gold;
  }
}

Таким образом, вы можете стилизовать каждую карточку на основе ее уникальных характеристик. Но вы также можете создавать стили для комбинаций характеристик (custom property), используя комбинатор 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;
  }
}

CodePen результат

Еще более полезным для запросов к контейнеру стиля была бы возможность использовать значения диапазона. Например, вы можете отправить такие данные, как --chanceOfRain: 50%, а затем использовать диапазон, например, @container style(30% <= --chanceOfRain < 60%) для применения стилей. Таким образом, можно было бы получить гораздо более детальный дизайн и его различные комбинации. Это еще одна возможность, которая активно обсуждается в рабочей группе по container queries.

Отделение данных от дизайна

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

Обновление значений CSS custom property через JavaScript может быть выполнено, либо во время вёрстки компонента (т.е. при создании компонента во фреймворке), либо обновлено в любое время с помощью parentElem.style.setProperty('--myProperty', ).

Вот демонстрация, которая в несколько строк кода обновляет --theme кнопки и применяет стили с помощью style queries и custom property (--theme):

CodePen результат

Стилизация карточки через style queries с помощью JavaScript:

const themePicker = document.querySelector('#theme-picker')
const btnParent = document.querySelector('.btn-section');

themePicker.addEventListener('input', (e) => {
  btnParent.style.setProperty('--theme', e.target.value);
})

Функции, подробно описанные в этой статье, - это только начало. Вы можете ожидать от container query большего, что поможет вам создавать динамичные, отзывчивые интерфейсы. Что касается конкретно style queries, то здесь остается несколько открытых вопросов. Один из них - реализация style queries для стилей за пределами CSS custom properties. Это уже является частью текущего уровня спецификации, но еще не реализовано ни в одном браузере. Ожидается, что boolean context evaluation будет добавлена в текущий уровень спецификации, когда будет решен этот вопрос, в то время как запрос диапазона запланирован на следующий уровень спецификации.

Комментарии (4)


  1. Mingun
    00.00.0000 00:00
    +1

    Что-то не очень понятно, зачем это всё нужно. Чем классы не угодили? Они же и были изначально придуманы ровно с той целью, чтобы по-разному стилизовать одинаковые элементы.


    1. Finesse
      00.00.0000 00:00
      +2

      Применение разных стилей в зависимости от ширины контейнера, а не ширины страницы — очень полезная возможность. Можно эмулировать с помощью классов и JavaScript, но получится не так гибко.

      Показанное в статье применение разных стилей в зависимости от кастомных CSS свойств выглядит как лишнее переусложнение стандарта.


      1. Mingun
        00.00.0000 00:00

        Применение разных стилей в зависимости от ширины контейнера, а не ширины страницы — очень полезная возможность.

        С этим не спорю, нужная вещь. Сомнения вызывает зависимость стилей от значений свойств.


  1. Finesse
    00.00.0000 00:00
    +3

    То ли перевод плохой, то ли сама статья такая. С самого начала ничего не понятно, как будто не хватает погружения в некий контекст.