- 1. Основы
- 2. Flexbox
- 3. Сетка и подсетка
- 4. Запросы контейнера
- 5. Нативные оверлеи, позиционирование фиксированных объявлений и контексты наложения
- 6. Отслеживание переполнения и стабильность макета
- 7. Механика области просмотра и распространение треков
- 8. Сетка (masonry)
1. Основы
По возможности используйте движок макета браузера для повышения производительности. Используйте внутренние размеры, логические свойства и aspect-ratio, прежде чем прибегать к жестко заданным размерам или сложным запросам медиа.
1.1. Какой режим макета использовать?
Пройдите по дереву решений сверху вниз и остановитесь на первом совпадении. Обратите внимание, что макеты могут быть вложены друг в друга, и каждое решение основано на варианте использования этого контейнера.
- Это простая строка ИЛИ столбец элементов? Используйте flexbox – одномерный макет, в котором контент распределяется по одной оси.
- Должен ли вложенный элемент выравниваться по дорожкам сетки своего родительского элемента? Используйте subgrid – двумерную сетку, в которой на первом месте стоят отношения между элементами. Она наследует родительские дорожки, поэтому дочерние элементы могут выравниваться по соседним элементам.
- Имеет ли страница или компонент сложную структуру со строками И столбцами? Используйте grid – двухмерный макет, в котором вы определяете структуру, а контент заполняет ее.
- Представляет ли контент собой длинный текст, который нужно разбить на сбалансированные столбцы? Используйте несколько столбцов – одномерный поток, как в газете.
- Нужно ли плотно упаковать предметы разной высоты? Используйте grid с
grid-auto-flow: denseуже сегодня. Переходите на встроенную сетку masonry (также известную как grid lanes), только когда она станет доступна в вашей целевой версии Baseline (см. раздел 8). - Нужно ли, чтобы элемент плавал над страницей и оставался пространственно привязанным к триггеру, даже за пределами границ DOM или контекстов наложения? Используйте позиционирование привязки –
anchor-nameдля триггера,position-anchorдля оверлея (см. §5).
1.2. Принципы работы
- Используйте логические свойства (
inline-size,block-size,margin-inline,padding-block,inset-inline-start) для размеров макета и интервалов. Подробнее об этом рассказывается в статье CSS: современная архитектура. - Применяйте ментальную модель "сначала контент" или "сначала макет": flexbox, когда элементы диктуют поток, и grid, когда вы сначала определяете скелет.
- Используйте сокращения
place-*(place-content,place-items,place-self), чтобы выровнять элементы по обеим осям в одном объявлении. - Используйте внутренние размеры (
min-content,max-content,fit-content()) и гибкие дорожки (fr,minmax()) вместо фиксированныхwidth/height. Это позволит уменьшить количество медиазапросов и сделать макеты более устойчивыми. - НЕ используйте
overflow: hidden, чтобы скрыть проблемы с макетом. Устраняйте их в источнике. Например, некоторые заменяемые элементы, такие как<canvas>,<img>,<svg>,<video>,<iframe>, по умолчанию используютdisplay: inline, что может привести к появлению пробела в некоторых макетах. Вместо того чтобы обрезать пробел с помощьюoverflow, удалите его, применив значение display-outside (например,display: block,grid,flexи т.д.). - Используйте
aspect-ratio, чтобы зарезервировать место для медиаконтента и предотвратить смещение макета до загрузки объектов.
.sidebar { inline-size: max-content; } /* Size to longest unbreakable token. */
.main-content { inline-size: fit-content; } /* Grow to available space, no further. */
.media { aspect-ratio: 16 / 9; inline-size: 100%; block-size: auto; }
body.centered { display: grid; place-content: center; min-block-size: 100dvb; }
Информацию о
calc-size()и внутреннем размере с учетом ограничений можно найти в разделе Расчет с использованием внутренних размеров.
2 Flexbox
Одномерный макет – элементы располагаются вдоль одной главной оси и выравниваются по поперечной оси. Используйте его для панелей навигации, панелей инструментов, строк элементов и любого распределения по одной строке или столбцу.
Что рекомендуется делать
- Задайте контекст с помощью
display: flexи установите главную ось с помощьюflex-direction(по умолчаниюrow). - Используйте
flex-wrap: wrap, когда возможно переполнение.nowrapбезoverflow: auto/hiddenбудет выходить за пределы узких областей просмотра. - Добавьте
flex-wrap: balanceпослеflex-wrap: wrapв строках с переносом компактных элементов (например, облаков тегов, фильтров, групп кнопок или ссылок навигации), чтобы равномерно распределить элементы по строкам и избежать появления одного элемента в последней строке. В браузерах без поддержки будет использоватьсяwrap. - Используйте сокращенное свойство
flex<grow> <shrink> <basis>(например,flex: 1 1 250px) для элементов вместо того, чтобы задаватьflex-grow/flex-shrink/flex-basisпо отдельности. - Используйте
gap(илиrow-gap/column-gap) для интервалов между элементами вместо дочерних полей. - Добавьте префикс
safeк выравниванию по позиции (например,align-items: safe center), чтобы контент, на который можно установить фокус, не обрезался, когда контейнер уже, чем контент. - Переместите один элемент в дальний конец главной оси с помощью
margin-inline-start: auto(илиmargin-block-start: auto) – это стандартный способ. - Переопределить выравнивание по поперечной оси для каждого элемента с помощью
align-self. - Используйте
align-items, чтобы выровнять все элементы по центру поперечной оси. Используйтеmargin: autoдля одного элемента, чтобы выровнять его по центру обеих осей независимо. Используйтеalign-content, только если контейнер переносит элементы и имеет дополнительное пространство между строками. - Задайте
min-inline-size: 0(илиmin-width: 0) для элементов flex, содержащих длинный неразрывный контент (URL, код, длинные строки). По умолчанию элементы flex не сжимаются до размера меньше размера их контента, что приводит к переполнению.
Не делайте следующее:
- Не используйте
justify-selfдля гибких элементов – это свойство работает только для макетов с сеткой, блоками и абсолютным позиционированием. Используйте автоматические поля. - Не используйте
orderилиflex-direction: *-reverse, чтобы изменить порядок интерактивного контента. Они меняют только визуальный порядок. Порядок DOM по-прежнему определяет последовательный фокус, поэтому поток вкладок клавиатуры не будет соответствовать тому, что видит пользователь. - Не путайте
space-around(половинные отступы по краям) сspace-evenly(одинаковые отступы до, между и после). - Не забывайте о смене осей: если
flex-direction: column, тоjustify-contentвыравнивает по блочной оси, аalign-items– по строчной оси (противоположно значению по умолчанию). - Не задавайте размеры контейнера и его дочерних элементов так, чтобы они заполняли друг друга. Это распространенная причина переполнения и неожиданных результатов. Укажите точный размер одной стороны.
- Не задавайте для одного элемента и
flex-basis, иwidth/inline-size. В контексте flex приоритет имеетflex-basis, аwidthигнорируется. Используйтеflex-basis(или сокращениеflex) в качестве единого источника достоверных данных для определения размеров гибких элементов.
.card-grid { display: flex; flex-flow: row wrap; gap: 1rem; }
.card-item { flex: 1 1 250px; } /* grow, shrink, basis */
.card-item-action { margin-inline-start: auto; } /* Push to main-axis end. */
.toolbar { display: flex; align-items: safe center; }
.tag-list { display: flex; flex-wrap: wrap; flex-wrap: balance; gap: 0.5rem; }
3. Сетка и подсетка
Двухмерный макет – строки и столбцы задаются явно или определяются движком. Subgrid позволяет вложенной сетке наследовать дорожки родительской сетки, чтобы внуки выравнивались по братьям и сестрам.
Как выбрать функции сетки
- Вы точно знаете, сколько столбцов вам нужно?
- Да – использовать треки с контентом для взрослых (
grid-template-columns: 200px 1fr,repeat(3, 1fr)и т. д.) - Нужны ли разные размеры для разных столбцов (боковая панель + основная, заголовок, охватывающий все)? → используйте
grid-template-areasдля именованных, читаемых регионов - Все ли столбцы одинаковы или они расположены только по номеру строки? → используйте
repeat(N, ...)или именованные линии - Нет (адаптивный дизайн, количество товаров неизвестно) – используйте
repeat(auto-fit, minmax(min, 1fr)). - Должны ли элементы в последней строке растягиваться, чтобы заполнить оставшееся пространство? →
auto-fit - Должны ли пустые дорожки последней строки сохранять свой минимальный размер (сохраняя фантомные слоты столбцов)? →
auto-fill
- Да – использовать треки с контентом для взрослых (
- Вам нужно разместить объект в определенном месте?
- Да – используйте
grid-column: <start> / <end>илиgrid-area: <name>. - Нет (просто охватывает несколько дорожек, положение в потоке не имеет значения) – используйте
grid-column: span <n>.
- Да – используйте
- Должны ли дочерние элементы наследовать размеры дорожек родительской сетки (выравнивание по неровным краям между дочерними элементами)?
- Да – использовать вложенную сетку на затронутой оси.
- Количество дочерних элементов в ячейке может быть разным? → subgrid только для одной оси; для другой используйте
grid-auto-rows/grid-auto-columns - Количество детей фиксировано? → subgrid on both axes is fine
- Нет – стандартная сетка, дополнительная сетка не требуется.
Что рекомендуется делать
- Установите контекст с помощью
display: grid. - Используйте
grid-template-areasдля сложных макетов на уровне страницы – названия областей говорят сами за себя, а объявление можно выровнять по строкам и столбцам для удобства чтения. - Используйте
repeat(auto-fit, minmax(200px, 1fr))для адаптивных сеток карточек, которые растягивают заполненные дорожки, чтобы заполнить строку, илиauto-fill, чтобы сохранить пустые повторяющиеся дорожки минимального размера. - Используйте
frдля пропорционального распределения дорожек иminmax(min, max)для гибких, но ограниченных дорожек. - Размещайте элементы с помощью
grid-column: span <n>, чтобы задать размер по дорожкам,grid-column: <start> / <end>, чтобы разместить на определенных строках, илиgrid-area: <name>для именованных регионов. - Используйте вложенную сетку (
grid-template-columns: subgridилиgrid-template-rows: subgrid), чтобы решить проблему неровных краев в списках карточек. Внутренние элементы, такие как заголовки, метаданные и призывы к действию, выравниваются по соседним элементам. - Чтобы обеспечить совместимость со старыми браузерами, добавьте объявление subgrid после объявления
grid-template-rows/-columns.
Не делайте следующее:
- Не ожидайте, что размер дорожки
auto-fit/auto-fillбудет определяться содержимым элемента. Он определяется аргументом размераrepeat(). - Не используйте
grid-auto-flow: denseдля интерактивного контента. Он эффективно упаковывает элементы, но визуально меняет их порядок, нарушая порядок перехода по вкладкам клавиатуры в DOM. - Не применяйте субсетку к обеим осям, если количество дочерних элементов может меняться. Дополнительные элементы попадают в последнюю дорожку. Вместо этого используйте
grid-auto-rows/grid-auto-columnsдля неявной оси. - Не путайте
justify-items/align-items(выравнивает контент элемента внутри его полосы) сjustify-content/align-content(выравнивает полосы сетки внутри контейнера). Если вы укажете неверный, он просто не будет работать. - Не используйте
repeat(auto-fit/auto-fill, ...)без определенногоinline-sizeв контейнере. Внутриdisplay: inline-gridили flex-элемента без размера у контейнера нет ширины, которую можно разделить, поэтому количество дорожек будет непредсказуемым.
3.1. Пример кода: сетка и подсетка
Оболочка страницы: <main class="page-layout"> содержит <header>, <aside>, <section class="card-grid"> с <div class="card"> дочерними элементами и <footer>.
/* Align grid-template-areas in rows and columns for readability. */
.page-layout {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-areas:
"header header header"
"sidebar main main"
"footer footer footer";
gap: 1.5rem;
}
header { grid-area: header; }
aside { grid-area: sidebar; }
footer { grid-area: footer; }
.card-grid {
grid-area: main;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
grid-template-rows: auto 1fr; /* title block, body block */
gap: 1rem;
}
.card {
grid-row: span 2;
display: grid;
/* Same-cascade fallback: ignored when subgrid is supported. */
grid-template-rows: auto 1fr;
grid-template-rows: subgrid;
}
4 запроса контейнера
Запрашивать размер (или вычисленный стиль) родительского контейнера, а не области просмотра. Ментальная модель: запросы контейнера = контекст компонента; медиа-запросы = глобальный макет страницы и пользовательские настройки (prefers-color-scheme, prefers-reduced-motion).
Что рекомендуется делать
- Прежде чем запрашивать дочерние элементы, установите контекст контейнера с помощью
container-type: inline-size(запросы только по ширине) илиcontainer-type: size(обе оси) в оболочке. - Если вложенные контексты могут конфликтовать, присвойте контейнерам имена с помощью
container-name(или сокращенного вариантаcontainer:container: card / inline-size). - Включать единицы запросов контейнера в расчет адаптивного типа и интервалов:
cqi/cqb(логические встроенные/блочные),cqw/cqh(физические),cqmin/cqmax. - Если используется
container-type: size, задайте для контейнера определенное значениеblock-size. В противном случае дочерние элементы будут свернуты, поскольку ограничение размера заставляет контейнер игнорировать свой контент.
Не делайте следующее:
- Не используйте
block-sizeв качестве значенияcontainer-type– это недопустимо. Используйтеsizeдля обеих осей. - Не ожидайте, что внутренний размер дочерних элементов повлияет на контейнер после объявления
container-type. Контейнер вычисляется так, как будто у него нет дочерних элементов, когда ограничение активно. - Не полагайтесь на единицы измерения запросов контейнера внутри потомков неподходящего предка; они возвращаются к малому окну просмотра (
svw/svh).
4.1. Пример кода: адаптивная типографика с использованием единиц измерения запросов контейнера
.card-wrapper {
container: card / inline-size; /* shorthand for container-name / container-type */
}
@container card (inline-size > 400px) {
.content {
display: flex;
gap: 2rem;
}
}
.title {
/* Fluid type bound to the container width, not the viewport. */
font-size: clamp(1rem, 4cqi, 2rem);
}
О том, как создавать адаптивные стили на основе компонентов, читайте в статьях Стили, зависящие от размера и Гибкое масштабирование.
5. Нативные оверлеи, позиционирование привязки и контексты наложения
Когда использовать каждый примитив наложения:
- Используйте
popoverдля временных немодальных элементов интерфейса (всплывающих окон, уведомлений, подсказок). Они находятся на верхнем уровне, поэтому управлятьz-indexне нужно. - Используйте
<dialog>с.showModal()для модальных взаимодействий, требующих захвата фокуса и инертного фона. - Не используйте
popoverи.showModal()для одного элемента, так как они являются взаимоисключающими состояниями времени выполнения.
Позиционирование якоря (пространственное расположение оверлеев):
- Используйте
position-area(илиanchor()для вставок) иanchor-size(), чтобы задать положение и размер оверлея относительно триггера. - Используйте
position-try-fallbacks: flip-block(илиflip-inline), чтобы браузер мог изменить положение, когда оверлей выходит за пределы области просмотра. - Не смешивайте физические и логические ключевые слова в одном значении
position-area– выберите одну систему координат. - Используйте
@supports (anchor-name: --x)для определения поддержки функции и укажите резервное значение absolute для свойства position.
Полные сведения о реализации, стратегии полифилов и справочные данные о значении
popoverможно найти в статьях Декларативное управление диалоговыми окнами и всплывающими элементами и Подсказки с учетом положения. Примеры позиционирования с привязкой для раскрывающихся списков, всплывающих окон и индикаторов вкладок можно найти в статьях Раскрывающиеся списки с учетом области просмотра и Подчеркивание вкладок с позиционированием с привязкой.
6. Отслеживание переполнения и стабильность макета
Управляйте смещением макета, полосами прокрутки и обрезкой.
Что рекомендуется делать
- Используйте
overflow: auto, чтобы полосы прокрутки появлялись только тогда, когда контент действительно выходит за пределы контейнера. - Используйте
overflow: clip, чтобы обрезать контент без создания контейнера прокрутки. Чтобы разрешить выход за пределы контейнера, используйтеoverflow-clip-margin. - Используйте свойство
scrollbar-gutter: stable, чтобы зарезервировать место для полос прокрутки и предотвратить смещение макета при увеличении объема контента. - Используйте
overscroll-behavior: contain(илиnone) для вложенных областей прокрутки (например, боковых панелей, модальных окон и списков), чтобы цепочки прокрутки не переходили на страницу, илиoverscroll-behavior: chainдля промежуточных оболочек прокрутки (например, дорожек, открывающихся свайпом), когда избыточная прокрутка должна распространяться на родительский скроллер без запуска локального отскока резиновой ленты. Для усечения многострочного текста используйте триаду
-webkit-line-clamp+display: -webkit-box+-webkit-box-orient: vertical. Несмотря на префикс, этот шаблон полностью определен и не устарел. Объявите сокращенное свойствоline-clampбез префикса. Браузеры, которые его не поддерживают, просто проигнорируют его. Не делайте следующее:Не используйте
overflow: scroll, если можно использоватьauto.scrollпринудительно добавляет полосы прокрутки, даже если прокручивать нечего.Не используйте
overflow: hidden, если вам нужно только обрезать контент.hiddenсоздает контейнер прокрутки, который можно прокручивать программно.
.scrollable-list {
max-block-size: 400px;
overflow-y: auto;
scrollbar-gutter: stable; /* Reserve scrollbar space. */
overscroll-behavior: contain; /* No scroll chaining into the page. */
}
.snippet {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
line-clamp: 3; /* Ignored where unsupported. */
overflow: clip;
}
Подробные сведения о
overflow: clipиoverflow-clip-marginприведены в статье Управление обрезкой переполнения. О том, как передавать вложенную прокрутку вoverscroll-behavior: chainв интерфейсе с жестом смахивания, читайте в статье Жест смахивания. Чтобы узнать, как настроить цвет, размер и тему полосы прокрутки, ознакомьтесь со статьями Как настроить цвет или толщину полосы прокрутки, Тёмная тема и Как настроить полосу прокрутки для режима высокой контрастности.
7. Механика области просмотра и распределение дорожек
- Используйте
dvh/dvwдля контейнеров макетов на мобильных устройствах, которые должны учитывать сдвиг интерфейса браузера (сворачивание/разворачивание панели URL). - Не используйте
100vwдля макета на всю ширину экрана, так как при этом не учитывается ширина полосы прокрутки и возникает горизонтальное переполнение. Используйте вместо него100%,100dvwили100svw.
О том, как начать работу с адаптивным дизайном (динамические единицы области просмотра, резервирование места для медиаконтента, гибкая типографика), читайте в статье Адаптивный дизайн.
8 дорожек сетки (мозаика)
Спецификация находится в разработке под названием grid lanes (например, display: grid-lanes), а в более ранних экспериментальных прототипах использовалось grid-template-rows: masonry.
Что рекомендуется делать
- Используйте сетку с
grid-auto-flow: denseдля плотной упаковки, учитывая, что порядок DOM может не совпадать с визуальным порядком. - Используйте многоколоночный макет (
columns: 3; column-gap: 1rem) для контента, похожего на кирпичную кладку, когда элементы представляют собой фрагменты документа, а не карточки равного веса. - Используйте
grid-template-rows: masonryтолько для прогрессивного улучшения. Для определения поддержки используйте@supports.
Не делайте следующее:
- Не делайте
grid-template-rows: masonryобязательным требованием, пока целевой показатель Baseline не достигнет нужного уровня.
.gallery { columns: 3 200px; column-gap: 1rem; }
.gallery > * { break-inside: avoid; margin-block-end: 1rem; }
@supports (grid-template-rows: masonry) {
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-template-rows: masonry;
gap: 1rem;
columns: unset;
}
}