Гид по технологиям

CSS Container Queries: адаптивные компоненты

7 min read CSS Обновлено 10 Apr 2026
CSS Container Queries — адаптивные компоненты
CSS Container Queries — адаптивные компоненты

Мужчина работает на 15-дюймовом MacBook Pro

Краткое введение

Долгое время основным способом построения адаптивных интерфейсов были медиазапросы (media queries). Они отлично решают проблему адаптации под разные размеры экрана, но имеют ограничение: условие применимо только к размеру окна просмотра (viewport), а не к размеру отдельного элемента или его ближайшего контейнера.

Контейнерные запросы решают эту проблему — теперь можно писать правила, которые срабатывают, когда сам контейнер становится уже или выше заданного порога. Это особенно полезно при создании компонентной архитектуры (React, Vue, Web Components), где один и тот же компонент может попадать в разные контейнеры с разной шириной.

Код из примеров доступен в репозитории на GitHub и распространяется по MIT-лицензии.

Зачем использовать контейнерные запросы?

Рассмотрим практический пример. У нас есть страница с двумя колонками: основной контент и сайдбар. На широком экране карточки в обеих колонках выглядят рядом с картинкой; на узком экране карточка превращается в вертикальный блок (изображение сверху, контент снизу).

Скриншот страницы в полноэкранном виде

При использовании медиазапросов мы ориентируемся на ширину окна:

@media(max-width: 800px) {  
  .card {  
    flex-direction: column;  
  }  
  .card-header {   
    width: 100%;  
  }  
}

Если у вас простой макет — это работает. Но когда элементы вложены и один компонент оказывается внутри узкого сайдбара, медиазапрос по ширине окна не позволяет легко учесть локальные ограничения: сайдбар может быть достаточно узким даже на широком экране, и карточки в нём будут «съеживаться» независимо от окна.

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

.sidebar .card {  
  /* Make the sidebar card smaller */  
}  
  
@media(max-width: 800px) {  
  /* Style the page when the screen is narrower than 800px */  
}  

Чем больше комбинаций контейнеров и компонентов — тем громоздче CSS. Контейнерные запросы избавляют от этой проблемы: компонент сам определяет своё поведение относительно контейнера, в котором он находится.

Как создать контейнерный запрос

  1. Сначала отметьте элемент как контейнер при помощи свойства container-type. Например, сделаем контейнером main и .sidebar:
main, .sidebar {  
  container-type: inline-size  
}  
  1. Теперь внутри можно писать @container — он срабатывает, когда сам контейнер достигает указанных размеров:
@container(max-width: 500px) {  
  .card {  
     flex-direction: column;  
  }  
  .card-header {  
    width: 100%;  
  }  
}  

В отличие от @media, @container проверяет размеры именно контейнера, а не viewport. На практике это означает, что одна и та же .card может вести себя по-разному в main и в sidebar в зависимости от локальной ширины контейнера.

Скриншот страницы с контейнерными запросами

На изображении видно: в сайдбаре карточка стала вертикальной, потому что ширина сайдбара меньше 500px; в основном контенте карточка осталась горизонтальной.

Важно: после добавления container-type сразу визуальных изменений не будет — вы просто подключаете возможность реагировать на размеры контейнера.

Именование контейнеров и выбор специфичного контейнера

По умолчанию @container ищет ближайший объявленный контейнер вверх по дереву DOM. Если вы объявили несколько контейнеров (body, main, aside), то ближайший к целевому элементу победит.

Если нужно ориентироваться не на ближайший контейнер, а на какой-то конкретный — задайте имя контейнера через container-name и укажите его в @container:

body {  
  container-type: inline-size;  
  container-name: body;  
}  

@container body (max-width: 1000px){  
  /* CSS rules that target the body container */  
}  

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

Контейнерные единицы (container units)

Контейнерные единицы похожи на viewport-единицы (vw/vh), но привязаны к размеру контейнера: cqw (container query width) и cqh (container query height). Примеры использования:

.card {  
  font-size: calc(1rem + 0.5cqw);  
}

Эти единицы позволяют делать масштабирование шрифтов и размеров на основе локального контекста, а не окна браузера.

Пример: адаптивная карточка с запасом по доступности

Ниже пример карточки, которая меняет порядок элементов и размеры шрифта в зависимости от ширины контейнера.

/* Объявляем контейнер для секции */
.section {  
  container-type: inline-size;  
}

/* По умолчанию карточка горизонтальная */
.card {  
  display: flex;  
  gap: 1rem;  
  align-items: flex-start;  
}

.card-header {  
  width: 40%;  
}

@container(max-width: 480px) {  
  .card {  
    flex-direction: column;  
  }
  .card-header {  
    width: 100%;  
  }
  .card {  
    font-size: 1.05rem; /* чуть крупнее для узкого контейнера */
  }
}

Этот подход даёт предсказуемое поведение компонента независимо от того, где он размещён.

Когда контейнерные запросы не подходят

Важно понимать ограничения и случаи, когда container queries не решают задачу:

  • Если вам нужно реагировать на размеры самого окна браузера (например, управление поведением глобальной сетки), то медиазапросы остаются необходимыми.
  • Если приложение динамически меняет размер контейнера через JavaScript и вы рассчитываете на синхронное поведение старых браузеров — учтите, что поддержка устаревших движков ограничена.
  • Очень глубокая вложенность и сложные правила именования контейнеров могут привести к путанице; разумнее держать архитектуру контейнеров простой.

Альтернативы и дополняющие техники

  • Media queries — по-прежнему актуальны для глобальных breakpoint-стратегий.
  • ResizeObserver + JavaScript — гибко, но сложнее и может ухудшать производительность при неправильном использовании.
  • CSS-переменные + медиазапросы — удобны для централизованного управления стилями и миграции на контейнерные запросы поэтапно.

Пример простого наблюдателя, если нужно триггерить JS-логики по изменению размера контейнера:

const ro = new ResizeObserver(entries => {
  for (let entry of entries) {
    // entry.contentRect.width => текущая ширина контейнера
    // Логика обновления классов или переменных CSS
  }
});
ro.observe(document.querySelector('.section'));

Используйте ResizeObserver только если CSS не может решить задачу: JS-обход работает медленнее и требует тестирования производительности.

Ментальные модели и эвристики при проектировании

  • Думайте о «локальном окне»: при работе с компонентом представляйте, что у него есть собственное окно просмотра — его контейнер.
  • Разделяйте правила: глобальные точки останова — через @media, локальные — через @container.
  • Называйте контейнеры осмысленно (например, app-shell, card-frame), если пользуетесь container-name.
  • Поддерживайте обратную совместимость: сначала добавляйте container-type, потом постепенно переводите поведение из @media в @container.

Миграция существующих компонентов — мини-методология

  1. Инвентаризация: найдите компоненты, которые меняют поведение в зависимости от ширины контекста (например, карточки в сайдбаре).
  2. Объявите соответствующие контейнеры: добавьте container-type к элементам-обёрткам.
  3. Переместите локальные медиазапросы в @container внутри стилей компонента.
  4. Тестирование: проверьте компонент в разных контейнерах и на разных устройствах.
  5. Рефактор: удалите избыточные селекторы, когда поведение стабильно.

Критерии приёмки

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

Роли и чек-листы

Разделю на две роли — разработчик и дизайнер.

Разработчик:

  • Добавил container-type к корневому элементу контейнера.
  • Перенёс локальные медиазапросы в @container, где это имеет смысл.
  • Проверил поддержку браузеров и добавил полифилы/фоллбэки при необходимости.
  • Прогнал тесты производительности и визуальные тесты.

Дизайнер:

  • Обозначил желаемые точки изменения макета в терминах локального контейнера.
  • Согласовал, какие элементы должны адаптироваться независимо от окна.
  • Предоставил макеты для разных ширин контейнера (например: >800px, 500–800px, <500px).

Совместимость и советы по миграции

На момент написания: современные версии Chromium, Firefox и Safari поддерживают контейнерные запросы. Однако старые браузеры могут не поддерживать их. Рекомендации:

  • Для критичных интерфейсов используйте progressive enhancement: добавьте контейнерные запросы как улучшение, а базовый стиль делайте доступным и без них.
  • Тестируйте в реальных браузерах и включайте fallback-правила (например, медиазапросы) для старых окружений.
  • Если нужна совместимость с IE11/Edge Legacy — придётся использовать JavaScript-обходы.

Примеры плохих пар и сценарии отказа

  • Не стоит объявлять слишком много вложенных контейнеров с разными именами без явной нужды — это усложняет отладку.
  • Если компонент зависит от глобального расположения (например, full-bleed баннеры), лучше оставить медиазапросы.
  • В высокочастотных анимациях или при частом изменении размеров контейнера проверяйте производительность: избыток пересчётов может снизить FPS.

Небольшая галерея исключительных случаев

  • Компонент внутри iFrame — контейнерные запросы работают относительно контейнера в фрейме, но учтите контекст iframe.
  • Компонент в layout-системе с CSS Grid: container queries готовы к взаимодействию, но порядок и placement grid-ячейки могут усложнить логику изменений.

1-строчная глоссарий

  • container-type: CSS-свойство, объявляющее элемент как контейнер.
  • @container: директива, похожая на @media, но ориентированная на контейнер.
  • cqw / cqh: контейнерные единицы ширины и высоты.

Решение «что использовать?» — простое дерево принятия решений

flowchart TD
  A[Нужно адаптировать элемент?] --> B{Зависит от всего окна?}
  B -->|Да| C[Используйте @media]
  B -->|Нет| D{Можно ли объявить контейнер?}
  D -->|Да| E[Добавьте container-type и @container]
  D -->|Нет| F[Используйте ResizeObserver или медиазапросы как fallback]

Риски и способы их снижения

  • Риск: отсутствие поддержки в целевых браузерах. Смягчение: progressive enhancement, fallback-медиазапросы, тестирование.
  • Риск: логическая запутанность при множественных именованных контейнерах. Смягчение: документировать архитектуру контейнеров и выбрать единый подход к именованию.
  • Риск: ухудшение производительности. Смягчение: избегать частых JS-пересчётов, оптимизировать CSS и тестировать реальную нагрузку.

Заключение

Контейнерные запросы изменяют парадигму адаптивного дизайна: вместо единого глобального окна вы получаете «локальные окна» для каждого компонента. Это делает компоненты более устойчивыми, переносимыми и предсказуемыми.

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

Короткая памятка — что делать сейчас:

  • Добавьте container-type к контейнерам, где компоненты чувствуют себя по-разному.
  • Перенесите локальные точки изменения в @container.
  • Тестируйте компоненты в разных контекстах и браузерах.

Ресурсы и дальнейшее чтение

  • Официальная документация по спецификации CSS Container Queries (поиск по MDN/Web Platform).
  • Примеры и репозиторий с исходным кодом в GitHub (MIT).

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

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

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Папка автозагрузки Windows — найти и управлять
Windows

Папка автозагрузки Windows — найти и управлять

Отключить исчезновение окон в macOS Sonoma
macOS

Отключить исчезновение окон в macOS Sonoma

Отключить клавишу Globe на Mac
macOS

Отключить клавишу Globe на Mac

Отключить миниатюры скриншотов на Mac
macOS

Отключить миниатюры скриншотов на Mac

RAW в JPEG на Mac — через Preview
Фото

RAW в JPEG на Mac — через Preview

Удалить фон с фото на Mac — быстро и без программ
macOS

Удалить фон с фото на Mac — быстро и без программ