Клавиша / esc
Иллюстрация: kazakov-al

Руководство по выражениям от контейнера

Время чтения: меньше 5 мин

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

        
          
          .input__icon {  display: none;}@media (min-width: 580px) {  .input__icon {    display: block;    width: 32px;    height: 32px;  }}
          .input__icon {
  display: none;
}

@media (min-width: 580px) {
  .input__icon {
    display: block;
    width: 32px;
    height: 32px;
  }
}

        
        
          
        
      

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

Здесь и приходят на помощь выражения от контейнера. Они позволяют автоматически менять внешний вид компонента в зависимости от стилей родителя. Чтобы начать использовать выражения от контейнер надо сначала объявить родитель компонента контейнером с помощью свойства container-type:

        
          
          .form__item {  container-type: inline-size;}
          .form__item {
  container-type: inline-size;
}

        
        
          
        
      

У container-type есть три значения:

  • normal — значение по умолчанию. Создаёт контейнер, который не позволяет запрашивать размеры, но разрешает запрос стилей.
  • inline-size — создаёт контейнер, который позволяет запрашивать размер по строчному направлению оси.
  • size — создаёт контейнер, который позволяет запрашивать размер по любой оси: и строчной, и блочной.

При объявлении контейнера с помощью container-type под капотом автоматически создаётся контекст, при котором дочерний компонент перестаёт влиять на элементы за пределами своего контейнера.

Рекомендуется давать контейнерам имена с помощью свойства container-name:

        
          
          .form__item {  container-type: inline-size;  container-name: form-item;}
          .form__item {
  container-type: inline-size;
  container-name: form-item;
}

        
        
          
        
      

Так вы сможете обращаться к конкретному контейнеру, если их будет несколько. Для объявления контейнера с именем можно использовать и сокращённую запись:

        
          
          .form__item {  container: form-item / inline-size;}
          .form__item {
  container: form-item / inline-size;
}

        
        
          
        
      

Когда контейнер объявлен, появляется возможность обращаться к его стилям и использовать новые единицы измерений зависящие от размеров контейнера:

  • cqw — 1% от ширины контейнера.
  • cqh — 1% от высоты контейнера.
  • cqi — 1% от inline-size контейнера.
  • cqb — 1% от block-size контейнера.
  • cqmin — меньшее из cqi и cqb.
  • cqmax — большее из cqi и cqb.

Синтаксис запроса к контейнеру во многом похож на медиавыражения, но вместо директивы @media используется @container:

        
          
          @container (inline-size >= 300px) {  .input__icon {    /* Изменение стилей компонента */  }}
          @container (inline-size >= 300px) {
  .input__icon {
    /* Изменение стилей компонента */
  }
}

        
        
          
        
      

Можно запрашивать не только размеры контейнера, а любые его вычисленные стили. Для этого используется функция style().

        
          
          @container style([свойство]: [значение свойства]) {  /* Новые стили */}
          @container style([свойство]: [значение свойства]) {
  /* Новые стили */
}

        
        
          
        
      

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

        
          
          @container (inline-size >= 300px) and style(--bg-color: #fff) {  /* Новые стили */}
          @container (inline-size >= 300px) and style(--bg-color: #fff) {
  /* Новые стили */
}

        
        
          
        
      

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

        
          
          .form__item {  container-type: inline-size;  container-name: form-item;}.input {  container-type: inline-size;  container-name: input;}.input__icon {  /* стили иконки поля ввода */}@container form-item (inline-size >= 300px) {  .input__icon {    /* изменение стилей */  }}
          .form__item {
  container-type: inline-size;
  container-name: form-item;
}

.input {
  container-type: inline-size;
  container-name: input;
}

.input__icon {
  /* стили иконки поля ввода */
}

@container form-item (inline-size >= 300px) {
  .input__icon {
    /* изменение стилей */
  }
}

        
        
          
        
      

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