lenec ru

← все посты

Container queries vs media queries: где какие выбирать в 2026

19K

Когда container queries только-только зарелизились в Chromium и Safari, в твиттере и блогах прокатилась волна «media queries умерли». Год спустя стало понятно — нет, не умерли. Container queries не пришли на смену, они закрыли дыру в инструментарии, которой раньше не было решения. У них разные сценарии применения, и путать их — значит делать жизнь себе и команде сложнее.

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

Чем они отличаются на самом деле

Media queries реагируют на свойства viewport или устройства целиком. Container queries — на размеры конкретного контейнера в DOM.

Простой пример. У тебя есть карточка товара. На странице каталога она шириной 250px (4 в ряд). На странице товара она шириной 600px (одна большая). На промо-странице — 100% ширины (ещё больше).

С media queries ты бы написал что-то вроде:

.product-card {
  /* компактный вариант */
}
@media (min-width: 768px) {
  .product-card {
    /* увеличенный */
  }
}

Но проблема в том, что viewport-ширина 768px не имеет отношения к ширине самой карточки. На широком экране в каталоге карточки всё ещё узкие. На узком экране на странице товара карточка широкая. Media query промахивается.

Container query пишется так:

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

.product-card {
  /* компактный */
}

@container card (min-width: 400px) {
  .product-card {
    /* увеличенный */
  }
}

Теперь карточка адаптируется к ширине своего контейнера, а не к viewport. На промо-странице, где она широкая, она автоматически берёт «увеличенный» макет, даже если viewport узкий.

Где я выбираю media queries

Несмотря на всю мощь container queries, в большинстве случаев в проекте всё ещё нужны media queries. Они отвечают за:

Глобальный layout страницы

Двухколоночный или одноколоночный макет, навигация в шапке или гамбургер, sidebar открыт или скрыт. Это про viewport, не про конкретный компонент.

.app-layout {
  display: grid;
  grid-template-columns: 1fr;
}

@media (min-width: 1024px) {
  .app-layout {
    grid-template-columns: 240px 1fr;
  }
}

Системные предпочтения пользователя

Тёмная тема, reduced motion, prefers-contrast — это всё media features, которые container queries в принципе не покрывают.

@media (prefers-color-scheme: dark) { ... }
@media (prefers-reduced-motion: reduce) { ... }
@media (prefers-contrast: more) { ... }

Возможности устройства

hover, pointer, orientation — это про устройство в целом, тут только media queries.

@media (hover: none) {
  /* стили для тач-устройств */
}

@media (pointer: coarse) {
  /* увеличить touch-таргеты */
}

Печать

Никуда не делась. @media print — там же, где и был.

Где я выбираю container queries

Container queries вытесняют media queries в задачах, связанных с переиспользуемыми компонентами.

Компоненты в дизайн-системе

Любой компонент, который может использоваться в разных контекстах с разными размерами — кандидат на container queries. Карточки, плашки уведомлений, медиа-объекты, hero-блоки.

.media-object {
  container-type: inline-size;
  container-name: media;
}

.media-object__layout {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

@container media (min-width: 480px) {
  .media-object__layout {
    flex-direction: row;
    gap: 24px;
  }
}

Что важно. container-type: inline-size ставится на родителя, не на сам компонент. Внутренние стили реагируют на размеры этого родителя. Если родителю задать ширину через grid или flex — компонент адаптируется.

Виджеты в сайдбарах и слотах

На дашборде в одной ячейке грида может стоять виджет «График продаж». В широкой ячейке у него развёрнутый layout с легендой справа. В узкой — компактный, легенда снизу. Без container queries пришлось бы передавать в компонент проп variant и держать состояние в JS. Сейчас — пара строк CSS.

Email-templates и whitelabel

Когда не контролируешь размер контейнера снаружи (письмо открывается в разных клиентах, виджет встраивается на чужой сайт), media queries не помогут. Container queries — единственный надёжный способ адаптации.

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

На начало 2026 — везде, кроме старых версий. Chromium с 105 (август 2022), Safari с 16 (сентябрь 2022), Firefox с 110 (февраль 2023). Если поддерживаешь только последние 2 года версий — container queries безопасны для прода.

Если нужны старые браузеры — фолбэк через @supports:

.product-card {
  /* компактный — базовое состояние */
}

@supports (container-type: inline-size) {
  .product-card-wrapper {
    container-type: inline-size;
  }
  @container (min-width: 400px) {
    .product-card { /* увеличенный */ }
  }
}

@supports not (container-type: inline-size) {
  /* фолбэк через media queries для старых браузеров */
  @media (min-width: 768px) {
    .product-card { /* увеличенный */ }
  }
}

Я в продакшне такой фолбэк ставлю редко: проектов, где ещё гоняется Chrome 99 или Safari 15, у меня нет.

Container queries: подводные камни

Контейнер не может сам быть размером 100% от себя

Если на элементе стоит container-type: inline-size, его ширина измеряется от размера, заданного снаружи. Если внутри элемента нет контента, влияющего на размер, контейнер схлопнется.

/* плохо: ширина равна 0 */
.wrapper {
  container-type: inline-size;
  display: inline-block;
}

/* нормально: ширина задана родителем (grid/flex) */
.wrapper {
  container-type: inline-size;
  /* родительский grid с шириной 1fr задаст ширину */
}

Размер не учитывает паддинги ребёнка

Container queries реагируют на ширину контейнера, а не на доступное пространство для контента. Если на контейнере паддинг 32px, queries всё равно сравнивают с внешней шириной.

height-based queries осторожно

container-type: size позволяет реагировать на оба измерения, но требует, чтобы у контейнера была явная высота. В большинстве случаев inline-size (только ширина) — правильный выбор. Высота компонента зависит от его контента, и реагировать на неё через CSS — путь к recursion и багам.

Performance

Container queries чуть тяжелее для браузера, чем media queries: при ресайзе нужно пересчитать стили для каждого контейнера, а не одной точки viewport. На современных машинах разница незаметна. Но если у тебя сотни контейнеров на странице — лучше профилировать. У меня в дизайн-системе на 200+ компонентов мы container queries используем активно, и performance regressions ни разу не ловили.

Container query units

Помимо @container, появились новые единицы: cqi (1% от inline-size контейнера), cqb (1% от block-size), cqw/cqh, cqmin/cqmax.

.card-title {
  font-size: clamp(16px, 4cqi, 24px);
}

Здесь 4cqi — 4% от ширины ближайшего контейнера. Шрифт сам адаптируется к размеру компонента, без queries и breakpoint'ов. У меня они закрыли половину задач, где раньше нужен был clamp(...) с viewport-единицами.

Где границу проводить я

Простое правило, которое я даю команде:

  • Если стиль зависит от того, как пользователь видит экран целиком — media queries. Layout страницы, тема, hover/pointer.
  • Если стиль зависит от того, как компонент расположен в макете — container queries. Внутренние перестроения карточек, виджетов, плашек.
  • Если хочется плавную адаптацию без breakpoints — container query units (cqi) в clamp.

Этого хватает на 95% задач. Оставшиеся 5% — экзотика типа @container style(...) или :has()+container queries, до них я ещё не доходила в реальных проектах.

Перенос старого кода

Не надо переписывать весь существующий CSS на container queries. Это бесполезная работа. Берём правило: новый компонент дизайн-системы, в котором breakpoints зависят от его контейнера, — пишем сразу на container queries. Старый компонент трогаем только если рефакторим его по другой причине.

За год работы по такому правилу мы постепенно мигрировали процентов 30 компонентов. Большая часть просто не требовала: глобальный layout как был на media queries, так и остался.

Что унести с собой

Container queries — не замена media queries, а дополнение. Они закрывают сценарий «один и тот же компонент в разных по размеру слотах», который раньше решался JavaScript-измерениями или хардкодом вариантов.

В 2026 они работают везде, где надо. Используй их для компонентов в дизайн-системе и переиспользуемых виджетов. Media queries оставь для глобального layout, темы и системных предпочтений. Так и тот, и другой инструмент работают по назначению, и команда не путает, что где применять.

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

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

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