Webrium
Подписаться

Flexbox или Grid: как выбрать раскладку

Как выбрать между Flexbox и Grid для разных элементов интерфейса и эффективно совмещать их в одном макете

Сравнение одномерной и двумерной раскладки: Flexbox и Grid как разделённые части одного поляFlexbox (одна ось)Grid (две оси)

Одномерность и двумерность: почему это главное отличие Flexbox и Grid

Flexbox и Grid решают одну задачу — раскладку элементов на странице — но делают это принципиально по-разному. Flexbox работает вдоль одной оси: элементы выстраиваются в строку или колонку, и все правила применяются только к этой линии. Grid же создаёт сетку с явными строками и колонками, позволяя управлять расположением сразу по двум направлениям.

Это отличие определяет, когда какой инструмент использовать. Если элементы должны заполнять пространство вдоль одной линии (например, горизонтальное меню или список кнопок), Flexbox подойдёт лучше. Если нужно расположить элементы по сетке (например, карточки товаров или форма с несколькими полями), Grid будет удобнее.

Но почему это так важно? Одномерная модель проще: она решает задачу распределения пространства вдоль одной оси, не заботясь о том, что происходит в перпендикулярном направлении. Двумерная модель сложнее: она требует явного задания правил для обеих осей, но зато позволяет контролировать расположение элементов с точностью до ячейки.

Как выбрать модель для типовых элементов интерфейса

Шапка сайта: Flexbox для строки

Шапка обычно состоит из логотипа, навигации и дополнительных элементов (например, кнопки входа). Все они выстраиваются в строку, поэтому Flexbox — естественный выбор. Если шапка должна адаптироваться к разным экранам, можно использовать flex-wrap: wrap, чтобы элементы переносились на новую строку при нехватке места.

Но что делать, если логотип должен занимать фиксированную ширину, а навигация — растягиваться на оставшееся пространство? Flexbox позволяет задать это с помощью flex-grow. Например:

.header {
  display: flex;
  align-items: center;
}

.logo {
  flex: 0 0 100px; /* фиксированная ширина */
}

.nav {
  flex: 1; /* растягивается на оставшееся пространство */
}

Карточки: Grid для сетки

Карточки — классическая задача для Grid. Если карточки должны располагаться в несколько колонок и выравниваться по высоте внутри одного ряда, Grid справится с этим проще. Например, repeat(auto-fit, minmax(min(300px, 100%), 1fr)) создаст колонки шириной от 300 пикселей, которые не будут выходить за границы контейнера и автоматически заполнят доступное пространство.

Но что, если карточки содержат разное количество текста? Одинаковую высоту карточек в одном ряду дают оба инструмента. Однако если нужно выровнять высоту карточек во всех рядах, Flexbox не справится — каждая строка распределяет пространство независимо. Здесь поможет Grid с grid-auto-rows: 1fr. Чтобы выровнять, например, кнопки внизу карточек, можно использовать subgrid или внутренний Flexbox с margin-top: auto.

Форма: Grid для полей и меток

Форма с несколькими полями — двумерная задача. Grid позволяет легко выравнивать метки и поля ввода по строкам и колонкам. Например, можно задать две колонки: одна для меток, другая для полей ввода, и выравнивать их по вертикали.

Но что, если поля ввода должны растягиваться на всю ширину? Grid позволяет задать это с помощью grid-column: span 2, чтобы поле занимало обе колонки.

Боковая панель: Flexbox для колонки

Боковая панель — это вертикальный список элементов, поэтому Flexbox с направлением column подойдёт лучше. Если панель должна быть фиксированной ширины, а содержимое — растягиваться по высоте, Flexbox позволит сделать это без лишних усилий.

Но что, если элементы панели должны заполнять доступное пространство с разными пропорциями? Распределить высоту между элементами колонки можно через разные значения flex-grow, например flex: 1 у одного и flex: 2 у другого. Это позволяет задать пропорции, сохраняя общую структуру.

Как совмещать Flexbox и Grid в одном компоненте

Иногда в одном компоненте нужно использовать обе модели. Например, в карточке с заголовком, описанием и кнопкой можно использовать Grid для выравнивания карточек между собой, а внутри каждой карточки — Flexbox для выравнивания содержимого по вертикали.

Граница между моделями проходит там, где заканчивается одномерная задача и начинается двумерная. Если элементы нужно расположить по сетке, используйте Grid. Если они должны выстраиваться в линию, Flexbox будет удобнее.

Но как определить эту границу на практике? Представьте, что вам нужно создать компонент с несколькими карточками, каждая из которых содержит заголовок, описание и кнопку. Карточки должны располагаться в несколько колонок и выравниваться по колонкам — это задача для Grid. Внутри каждой карточки заголовок, описание и кнопка должны выстраиваться по вертикали — это задача для Flexbox.

Адаптивность без медиазапросов: автозаполнение и минимальные размеры

Современный CSS позволяет создавать адаптивные интерфейсы без медиазапросов. Например, в Grid можно использовать auto-fit для автоматического заполнения колонок, а minmax() — для задания минимальных и максимальных размеров.

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 16px;
}

Этот код создаст колонки шириной от 300 пикселей, которые будут автоматически заполнять доступное пространство. Если места не хватает, колонки будут переноситься на новую строку. Но как это работает?

auto-fit создаёт столько же треков, сколько помещается в контейнер, но схлопывает пустые треки в нулевую ширину; оставшееся пространство достаётся заполненным трекам только если их максимум гибкий (1fr). minmax(min(300px, 100%), 1fr) задаёт минимальную ширину колонки в 300 пикселей или ширину контейнера (в зависимости от того, что меньше), и максимальную — в одну долю оставшегося пространства.

Контейнерные запросы позволяют менять стили элемента в зависимости от размеров его контейнера. Например, можно изменить количество колонок в Grid или направление Flexbox, если контейнер стал слишком узким. Подробнее об этом можно прочитать в материале Что современный CSS убрал из вашего JS.

Частые поломки и как их исправить

Переполнение длинным текстом

Если текст внутри элемента не помещается, он может выйти за границы контейнера. Это происходит, когда содержимое шире или выше, чем ожидалось. У flex- или grid-элементов по умолчанию min-width: auto, и длинное содержимое их распирает. Лечится так: min-width: 0 у flex-элемента или minmax(0, 1fr) у трека, плюс overflow-wrap: anywhere или overflow: hidden для самого текста.

В Grid можно задать minmax(0, 1fr) для треков, чтобы они не расширялись под содержимое. Это означает, что трек будет иметь минимальную ширину в 0 пикселей и максимальную — в одну долю оставшегося пространства.

Сжатие картинок

Картинка искажается, когда её ширина и высота заданы независимо друг от друга: явно, растяжением через align-items: stretch или сжатием flex-shrink. Чтобы картинка не сжималась, задайте ей flex-shrink: 0 (или flex: none). Отключить растяжение можно через align-self: flex-start у картинки либо align-items у контейнера, а если нужно сохранить пропорции — добавьте явную высоту вместе с object-fit. Если же нужно, чтобы элемент мог сжиматься меньше ширины содержимого, используйте min-width: 0.

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

Неожиданные отступы

Отступы между элементами могут появляться из-за gap, margin или распределения пространства (justify-content, align-content). Чтобы их убрать, уменьшите значение gap или проверьте стили дочерних элементов. Также причиной может быть выбор между auto-fill и auto-fit: auto-fill создаёт пустые треки, которые мешают карточкам растянуться на всю ширину ряда, а auto-fit схлопывает пустые треки, позволяя заполненным занять всё пространство.

Например, если отступы появляются из-за justify-content: space-between, можно заменить его на justify-content: flex-start и добавить gap. Если отступы берутся из margin у дочерних элементов, можно уменьшить или убрать эти margin.

Тупик: вложенные контейнеры и ручные ширины

Частая ошибка — оборачивать всё во вложенные flex-контейнеры и подгонять ширины вручную. Это приводит к тому, что раскладка держится только на одном экране и рассыпается на другом. Например, если задача была двумерной (карточки в несколько колонок), а вы использовали Flexbox с переносом строк, каждая строка распределяет пространство независимо. Из-за этого колонки в разных строках не совпадают по ширине; если у элементов задан flex-grow больше нуля, элементы последнего неполного ряда дополнительно растягиваются на всю ширину контейнера.

Почему это происходит? Flexbox с flex-wrap не поддерживает общие колоночные треки — каждая строка работает сама по себе. Grid же сохраняет единую ширину колонок для всех элементов, независимо от ряда. Чтобы все ряды имели одинаковую высоту, используйте Grid с grid-auto-rows: 1fr.

Вместо этого используйте Grid для двумерных задач. Например, для карточек подойдёт repeat(auto-fit, minmax(min(300px, 100%), 1fr)), который задаёт общие колонки, а высоту выравнивает только в пределах одного ряда. Если нужно выровнять высоту во всех рядах, добавьте grid-auto-rows: 1fr. Для содержимого внутри карточек используйте subgrid или внутренний Flexbox.

Позиция редакции

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

Однако эта рекомендация неверна, когда:

  • Нужно поддерживать старые браузеры, где Grid работает с багами или не поддерживается вовсе. В таких случаях Flexbox с flex-wrap остаётся единственным вариантом, пусть и с ограничениями по выравниванию колонок.
  • Элементы должны сохранять фиксированную ширину в неполном ряду. При flex-grow: 0 Flexbox не растягивает элементы последнего ряда, в отличие от Grid с auto-fit, который всегда распределяет пространство между заполненными треками.
  • Требуется оставить пустые треки для визуальной согласованности. auto-fill создаёт колонки даже при малом числе элементов, что полезно, если ширина карточек не должна меняться от ряда к ряду. Grid с auto-fit в этой ситуации схлопнет пустые треки, нарушив дизайн.

С чем это связано

Метка над заголовком говорит, зачем туда идти: продолжить тему, перейти к практике или увидеть возражение.

Два стыка: CSS встроен в конвейер рендеринга и совпадает с кадром, JS-наблюдатель стоит снаружи и разошёлся — опаздывает на кадрCSS в конвейереJS опаздывает
Предыстория

Что современный CSS убрал из вашего JS

CSS решает задачи компоновки и реактивности быстрее JS — разберитесь, где скрипты уже не нужны и почему они всегда отстают на кадр.

Цепочка шагов аудита доступности: автоматические проверки разметки лежат внутри границы, а последний шаг — поведение по Tab с реальными барьерами — выходит за евидит автоматбарьер в поведении
Усиление

Доступность как инженерная задача, а не как галочка

Как клавиатурная навигация и тесты на контракты превращают доступность из формальности в реальную инженерию.

Три показателя веса страницы: HTML, CSS, JS в норме, изображения превышают допустимый весИзображения
Практика

Картинки на сайте: форматы, размеры и ленивая загрузка

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

Письмо по вторникам

Один разбор недели и короткий список того, что изменилось. Без дайджестов на сорок ссылок.

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

↑↓ выбрать · Enter открыть · Esc закрыть