Загрузка...

Container queries в CSS

Nesting держит селекторы компонента вместе. Media queries по-прежнему смотрят на viewport. Container queries задают другой вопрос: какова ширина этого родительского блока? Одна карточка товара остаётся компактной в узком rail и раскрывается в широкой колонке — даже если окно браузера не менялось.

Почему viewport недостаточно

В вёрстке один и тот же компонент часто попадает в слоты разной ширины: боковой rail 16rem и гибкая основная область. У обоих одна ширина viewport, поэтому @media (min-width: …) срабатывает одинаково. Нужны стили от контейнера вокруг карточки, а не от всего экрана.

Создать контекст измерения

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

Turn a rail into a size container

css
.rail {
container-type: inline-size;
container-name: rail;
width: 16rem;
padding: 1rem;
background: #f8fafc;
}

.stage {
container-type: inline-size;
container-name: stage;
flex: 1;
min-width: 0;
padding: 1.25rem;
}

Без container-type у @container нечего надёжно измерять. container-name необязателен, но полезен, когда несколько предков — контейнеры: можно прицелиться по имени.

Запрос к контейнеру через @container

После настройки оберните правила в @container с условием по размеру. Потомки реагируют, когда этот блок подходит — а не только когда меняется viewport:

Product card reacts to its parent width

css
.product-card {
display: grid;
gap: 0.75rem;
padding: 1rem;
border: 1px solid #e2e8f0;
border-radius: 0.75rem;
background: #fff;
}

.product-card .media {
aspect-ratio: 16 / 10;
border-radius: 0.5rem;
background: #cbd5e1;
}

.product-card .meta {
font-size: 0.875rem;
color: #64748b;
}

@container (min-width: 28rem) {
.product-card {
grid-template-columns: 10rem 1fr;
align-items: center;
}

.product-card .title {
font-size: 1.25rem;
}
}

В узком .rail карточка остаётся в одной колонке. Тот же HTML в широком .stage включает две колонки — тот же разметка, тот же viewport, другая ширина контейнера.

Именованные контейнеры

Если вложенные контейнеры путают, какой блок измеряется, укажите имя после @container:

Target a named containment context

css
@container stage (min-width: 36rem) {
.product-card {
grid-template-columns: 12rem 1fr auto;
gap: 1.25rem;
}

.product-card .buy {
justify-self: end;
}
}

@container rail (max-width: 18rem) {
.product-card .meta {
display: none;
}
}

@container stage (…) смотрит только на предка с именем stage. Жёсткие правила rail не спорят с просторными правилами stage, когда оба оборачивают связанную разметку.

Единицы container query

При наличии контекста containment длины вроде cqi (1 % inline-размера query-контейнера) и cqw (1 % его ширины) масштабируют тип и отступы с родителем, а не с viewport (vi / vw):

Type that tracks the container

css
.product-card .title {
font-size: clamp(1rem, 0.85rem + 2.5cqi, 1.5rem);
}

.product-card {
padding: clamp(0.75rem, 2.5cqi, 1.5rem);
}

Сочетайте с var() и clamp() из прошлых уроков Modern Toolkit. Container-единицы измеряют контекст для запроса — удобно для переиспользуемых виджетов.

Контейнер и media — кто за что отвечает

📊 Container queries vs media queries

Какой вопрос вы задаёте Предпочтительно Типичный инструмент
Насколько широк viewport браузера? Оболочка страницы, nav, многоколоночная страница @media (min-width: …)
Насколько широк этот родитель вокруг компонента? Карточки, виджеты, embed в неравных слотах @container (min-width: …) после container-type
Нужны оба? Часто да MQ для страницы; CQ для компонента в слоте

Два слота, один компонент

Небольшой эскиз показывает выгоду — два контейнера, один класс карточки:

Sidebar rail and main stage share one card

css
.page {
display: flex;
gap: 1rem;
align-items: start;
}

.rail {
container-type: inline-size;
container-name: rail;
width: 16rem;
}

.stage {
container-type: inline-size;
container-name: stage;
flex: 1;
min-width: 0;
}

/* Same .product-card class in both slots — @container rules above decide layout */

Не нужно плодить .product-card--sidebar и .product-card--main только из-за ширины слота. Слот сообщает размер; карточка слушает.

Сочетать media страницы и контейнеры компонента

Viewport-запросы оставьте для решений страницы (свернуть nav, одна колонка оболочки). Container queries — для компонента в любом слоте, который дала страница:

Viewport for the page, container for the card

css
@media (max-width: 40rem) {
.page {
flex-direction: column;
}

.rail {
width: auto;
}
}

@container (min-width: 28rem) {
.product-card {
grid-template-columns: 10rem 1fr;
}
}

На узком телефоне страница складывается в колонку; на широком desktop rail может оставаться узким, а stage широким — это различие чётко выражают только container queries.

Попробуйте сами

  1. Добавьте container-type: inline-size на обёртку карточки; в DevTools проверьте контекст containment.
  2. Напишите @container (min-width: 24rem) — из стека в две колонки.
  3. Задайте container-name: panel и прицельтесь @container panel (min-width: 30rem).
  4. Заголовок через cqi; меняйте только ширину родителя (не окна).
  5. Оставьте один @media на оболочке страницы; не дублируйте ту же логику в карточке.

Частые ошибки

  • @container без container-type у предка — нечего измерять
  • Заменить все media queries на container queries — оболочка страницы всё ещё для @media
  • Три вложенных контейнера и неясно, какая ширина «побеждает» — назовите и цельтесь явно
  • Слепо копировать breakpoints viewport (48rem) в каждый виджет — настраивайте под слот, не каталог устройств
  • Путать nesting (&, вложенные селекторы) и containment — nesting упорядочивает селекторы; container queries измеряют блоки

Итог

  • Container queries стилизуют от размера родителя, не только viewport
  • container-type (часто inline-size); при необходимости container-name
  • @container (min-width: …) / @container name (…) для перестройки потомков
  • cqi / cqw, когда длины должны следовать за контейнером
  • Дальше в Modern Toolkit: feature queries (@supports) — стили от возможностей браузера

🧠 Проверьте Свои Знания

Готов к Началу

Проверьте Свои Знания

Бросьте себе вызов с помощью этой интерактивной викторины и узнайте, насколько хорошо вы понимаете тему

❓
6
Вопросы
🎯
70%
Для Прохождения
♾️
∞
Время
🔄
∞
Попытки

📝 Инструкции

  • Внимательно прочитайте каждый вопрос
  • Выберите лучший ответ на каждый вопрос
  • Вы можете пересдавать тест столько раз, сколько захотите
  • Ваш прогресс будет показан вверху