Контейнерные запросы
Медиазапрос смотрит на ширину окна. Но одна и та же карточка может стоять и в широкой колонке, и в узкой боковой панели — окно при этом одно. Контейнерные запросы проверяют ширину блока-родителя, и карточка сама решает, как ей выглядеть. Они работают во всех современных браузерах с 2023 года.
Контейнер
Сначала родителя объявляют контейнером — блоком, размеры которого можно проверять. Для этого служит свойство container-type: inline-size. Inline-size — это размер вдоль строки, то есть для обычного текста ширина:
.wrapper {
container-type: inline-size;
}Сам по себе контейнер ничего не меняет внешне. Он только разрешает потомкам задавать вопросы о его ширине.
Правило @container
Правило @container похоже на @media, но условие проверяет ширину ближайшего контейнера. Ниже одна и та же карточка стоит в двух контейнерах разной ширины:
<style>
.wrapper { container-type: inline-size; margin-bottom: 8px; border: 1px dashed gray; }
.narrow { width: 220px; }
.wide { width: 440px; }
.card { display: grid; gap: 8px; padding: 8px; background: lavender; }
.pic { height: 60px; background: mediumpurple; }
@container (min-width: 300px) {
.card { grid-template-columns: 100px 1fr; }
}
</style>
<div class="wrapper narrow">
<div class="card"><div class="pic"></div><p>Картинка над текстом</p></div>
</div>
<div class="wrapper wide">
<div class="card"><div class="pic"></div><p>Картинка слева от текста</p></div>
</div>Код карточки одинаковый, а вид разный: в узком контейнере условие не выполнилось, в широком — выполнилось.
Потяните за угол
Чтобы увидеть переключение вживую, контейнеру можно разрешить менять ширину мышью — свойство resize: horizontal. Потяните за правый нижний угол рамки:
<style>
.wrapper { container-type: inline-size; resize: horizontal; overflow: hidden; width: 240px; max-width: 100%; border: 1px dashed gray; }
.card { padding: 12px; background: honeydew; }
@container (min-width: 360px) {
.card { background: gold; font-size: 20px; }
}
</style>
<div class="wrapper">
<div class="card">Растяните рамку шире 360 пикселей</div>
</div>Окно браузера не меняется, а карточка перестраивается.
Имена контейнеров
Если контейнеры вложены друг в друга, @container смотрит на ближайший. Чтобы обратиться к конкретному, ему дают имя через container-name. Короткая запись container задаёт имя и тип сразу:
.sidebar {
container: sidebar / inline-size;
}
@container sidebar (max-width: 250px) {
.menu { font-size: 14px; }
}Единицы контейнера
Внутри контейнера работают особые единицы. cqi — один процент от ширины контейнера, как vw для окна:
<style>
.wrapper { container-type: inline-size; width: 60%; border: 1px dashed gray; }
.title { font-size: 10cqi; margin: 0; }
</style>
<div class="wrapper">
<p class="title">10cqi</p>
</div>Шрифт равен десятой части ширины рамки и растёт вместе с ней.
Частая ошибка
Элемент не может проверять собственную ширину: условие @container смотрит на предка. Если объявить контейнером саму карточку, её стили внутри запроса не применятся:
<style>
.card { container-type: inline-size; padding: 12px; background: mistyrose; }
@container (min-width: 100px) {
.card { background: gold; }
}
</style>
<div class="card">Я контейнер, но не могу менять себя</div>Карточка осталась розовой. Объявляйте контейнером обёртку, а стили запроса пишите для того, что внутри.