lenec ru

← все посты

CSS Container Queries: где они уже работают

17K

Контейнерные запросы (CSS Container Queries) — это инструмент, который ждали пятнадцать лет. Раньше адаптивная вёрстка строилась только на ширине viewport, и компонент в боковой панели и в основной области приходилось писать одинаково: либо подстраиваясь под огромный экран, либо под маленький. Контейнерные запросы дают компоненту реагировать на ширину своего родителя.

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

Базовый синтаксис

На контейнере включаем container-type:

.card-host {
  container-type: inline-size;
  container-name: card;
}

inline-size — нас интересует ширина контейнера. Альтернатива — size (и ширина, и высота, но дорого по производительности и редко нужно).

Дальше внутри — медиа-подобный запрос:

.card {
  display: block;
}

@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 120px 1fr;
    gap: 1rem;
  }
}

Если контейнер шире 400 пикселей — отрисуй карточку как двухколоночную; если уже — как обычный блок. Никакой связи с шириной браузера.

Сокращённый синтаксис

Если у тебя один контейнер на компонент — можно не давать ему имя:

.card-host { container-type: inline-size; }

@container (min-width: 400px) {
  .card { display: grid; }
}

Я именую только тогда, когда внутри есть вложенные контейнеры разных уровней.

Где это меняет жизнь

Карточки в гридах

Самый показательный пример. У нас в e-commerce есть карточка товара. На лендинге она в одной колонке (узкая), в каталоге — в трёх (средняя), в детальной — в одной полной (широкая). Раньше приходилось писать три варианта компонента или подстраиваться через :where-селекторы.

С контейнерными запросами один компонент сам решает: при 320px показывать обложку и заголовок, при 480px — добавить блок цены справа, при 720px — развернуть всё в горизонтальный layout с кнопкой «купить».

Сайдбары и виджеты

Виджет «связанные посты» в правой колонке узкий, в подвале — широкий. Один компонент — два разных layout. Я это использовала в портале новостей, и команда оценила: вёрстка стала ощутимо проще, дизайнер перестал слать варианты «для узкого случая».

Компоненты в админках

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

Container query units

В дополнение к запросам появились новые единицы измерения, привязанные к размеру контейнера:

  • cqw — 1% ширины контейнера;
  • cqh — 1% высоты;
  • cqi — 1% inline-размера (логическая ширина);
  • cqb — 1% block-размера;
  • cqmin и cqmax — минимум/максимум inline и block.

Применение: например, заголовок карточки занимает 8% от ширины контейнера, и адаптируется без медиа-запросов:

.card-title {
  font-size: clamp(1rem, 4cqi, 1.5rem);
}

Тут 4cqi — 4% от inline-размера контейнера. На узкой раскладке шрифт будет ближе к 1rem, на широкой — к 1.5rem, плавно. Без брейкпоинтов.

Style queries

В 2026 году в Chromium и Safari работают style queries — запросы по значению CSS-переменной контейнера. Это даёт интересные паттерны темизации:

.theme-host { --mode: dark; }

@container style(--mode: dark) {
  .label { color: white; }
}

В Firefox style queries пока в работе, не во всех версиях. Я бы для прод-проекта пока избегала зависимости от этой фичи.

Поддержка браузеров

Размерные контейнерные запросы (min-width, max-width) — стабильны в Chromium 105+, Safari 16+, Firefox 110+. На январь 2026 это покрывает почти весь актуальный трафик. Я в проектах перестала их полифилить около года назад.

Ширина и единицы cq* поддерживаются параллельно с запросами, нет смысла разделять.

Что осторожно

Контейнерные запросы создают новый «формирующий контекст» для содержимого. Это значит, что элементы с position: fixed внутри контейнера могут вести себя не так, как ты ожидаешь — фактически они становятся fixed-овыми относительно контейнера, а не viewport. Решение — выносить такие элементы выше по дереву.

Ещё одно: container-type: inline-size делает невозможным height: 100% у дочерних элементов в обычном режиме. Если внутри был трюк с заполнением высоты — переписывай через min-height или CSS Grid.

Производительность: для большинства случаев контейнерные запросы не дороже медиа-запросов. Но если ты применяешь container-type: size к огромному количеству элементов (тысячи), браузер начинает нагружаться. На больших сетках — переходи на inline-size, его дешевле обрабатывать.

Реальный пример карточки

Покажу полную карточку, которая адаптируется по контейнеру. Один компонент, три состояния:

.product-card {
  container-type: inline-size;
  container-name: card;
  border-radius: 12px;
  overflow: hidden;
  background: #fff;
}

.product-card__inner {
  display: grid;
  gap: 0.75rem;
  padding: 1rem;
}

.product-card__image { aspect-ratio: 4/3; }
.product-card__price { font-weight: 600; }

@container card (min-width: 360px) {
  .product-card__inner {
    grid-template-columns: 120px 1fr;
    gap: 1rem;
  }
}

@container card (min-width: 600px) {
  .product-card__inner {
    grid-template-columns: 200px 1fr auto;
    align-items: center;
  }
  .product-card__cta { justify-self: end; }
}

Один HTML, три раскладки. Никаких @media, никакого JS. Карточка работает одинаково в любом контексте — главное, чтобы родитель был контейнером.

Имена контейнеров и вложенность

Когда контейнеров несколько, удобно их именовать:

.layout { container: layout / inline-size; }
.sidebar { container: sidebar / inline-size; }
.card { container: card / inline-size; }

@container card (min-width: 400px) {
  .card-title { font-size: 1.25rem; }
}

@container sidebar (min-width: 280px) {
  .nav-list { gap: 0.5rem; }
}

Запрос @container card ищет ближайшего предка с именем card, не путая со sidebar. На вложенных компонентах это спасает от случайных совпадений.

Когда не использовать

  • Очень простые сайты с одной колонкой — там и медиа-запросов хватит.
  • Layout, который зависит от ширины окна (хедер, футер) — здесь именно viewport.
  • Анимации с трансформациями — не клади на контейнер, могут быть странные эффекты с трансформирующимся контекстом.

В сухом остатке

Container queries окончательно изменили подход к адаптивной вёрстке. Я больше не пишу один компонент для трёх контекстов и не «защищаюсь» от переноса. Компонент просто знает, в каком пространстве он живёт. Команды, которые перешли на этот стиль, отмечают: меньше CSS, чище код, быстрее правки. И главное — дизайнеры внезапно начали мыслить компонентами, а не страницами.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.