CSS Grid в деталях: fr, minmax, areas, паттерны
CSS Grid — первая в истории CSS система раскладки, спроектированная как двумерная с первого принципа. Flexbox ведёт элементы вдоль одной оси и переносит; Grid сразу оперирует колонками И строками, как таблица, но без её ограничений. Здесь ты собираешь макеты страниц, карточные сетки и формы — там, где flex-вложенность превращалась в лапшу.
В продакшене grid — инструмент макетного уровня: оболочка страницы, сетка карточек, разметка формы. Его киллер-фича grid-template-areas делает макет читаемым как ASCII-картинку, а auto-fill/minmax() позволяют строить отзывчивые сетки вообще без медиазапросов. Но у grid своя терминология и свои грабли: fr не то, чем кажется, auto-fill молча создаёт пустые треки, а 1fr с minmax() ведёт себя неожиданно при переполнении.
Терминология: контейнер, линии, треки, ячейки, области
Заголовок раздела «Терминология: контейнер, линии, треки, ячейки, области»grid-контейнер:
линии (lines) — вертикальные 1..N, горизонтальные 1..M треки (tracks) — пространство МЕЖДУ двумя соседними линиями ячейки (cells) — пересечение трека строки и трека колонки области (areas) — прямоугольник из одной или нескольких ячеек
линии колонок 1 2 3 4 1 ┌────────┬────────┬────────┐ │ │ │ │ │ area A │ area A │ area B │ ← трек строки 1 │ │ │ │ 2 ├────────┼────────┤ │ │ │ │ │ │ area C │ area D │ area B │ ← трек строки 2 │ │ │ │ 3 └────────┴────────┴────────┘ ▲ ▲ ▲ └────────┴────────┴─ треки колонок (3 штуки)Айтем размещается по линиям: grid-column: 1 / 3 — от линии 1 до линии 3 (два трека). Все счёты — по линиям, не по ячейкам: в сетке из 3 колонок линий 4.
Создание сетки: columns, rows, fr
Заголовок раздела «Создание сетки: columns, rows, fr».page { display: grid; grid-template-columns: 260px 1fr 300px; /* 3 колонки */ grid-template-rows: auto 1fr auto; /* 3 строки: header, main, footer */ min-height: 100dvh;}fr (fraction) — доля свободного места ПОСЛЕ вычитания жёстких треков и gap’ов:
контейнер 1000px, gap 20px × 2 = 40px, колонки: 260px 1fr 300px
свободное = 1000 − 40 − 260 − 300 = 400px1fr = 400px (вся свободная доля на один трек)
итог: 260 | 20 | 400 | 20 | 300
если бы было: 1fr 2fr 1fr → свободных 400 делятся на 4 доли → 100 / 200 / 100Ключевой подвох: fr — это minmax(auto, Nfr), то есть трек не может быть уже содержимого! Длинный неразрывный текст в 1fr-колонке раздует её и сломает сетку. Лечится тем же приёмом, что и во flex: min-width: 0 на айтеме, либо явным minmax(0, 1fr). Формально это записано в спецификации CSS Grid Layout Module Level 2 — раздел про fr-единицы и track sizing algorithm.
/* «настоящий» гибкий трек: старт с нуля, не реагирует на контент */grid-template-columns: minmax(0, 1fr);repeat(), minmax(), auto-fill против auto-fit
Заголовок раздела «repeat(), minmax(), auto-fill против auto-fit»repeat(n, значение) — сокращение: repeat(3, 1fr) = 1fr 1fr 1fr. Внутри можно комбинировать: repeat(2, 120px 1fr).
minmax(min, max) — трек живёт между границами. Звезда адаптивных сеток:
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 1rem;}Контейнер сам решает, сколько колонок поместится: при 1200px — 5, при 700px — 3, при 360px — 1. Медиазапросы не нужны. Теперь разница двух близнецов, которая выносит мозг:
auto-fill: пустые треки СОХРАНЯЮТСЯ auto-fit: пустые треки СХЛОПЫВАЮТСЯ
контейнер 1000px, minmax(240px, 1fr), 2 карточки:
┌────┐┌────┐┌────┐┌────┐ (пустые!) ┌──────────┐┌──────────┐│card││card││ ││ │ │ card ││ card │└────┘└────┘└────┘└────┘ └──────────┘└──────────┘ 4 колонки: карточки узкие, 2 колонки: карточки справа пустое место растянулись на всю ширину
fill — количество колонок фиксировано fit — количество = числу айтемов(полезно с явным justify-контролем)Правило большого пальца: в 95% случаев для карточных сеток нужен auto-fit. auto-fill оправдан, когда ты сознательно держишь сетку «в натяг» (например, галерея с фиксированным числом колонок для определённого breakpoint’а). Разница близнецов с иллюстрациями — в MDN: Автоматическое размещение в Grid.
Другие функции треков
Заголовок раздела «Другие функции треков»grid-template-columns: fit-content(300px) 1fr; /* трек = по контенту, но не шире 300 */grid-template-columns: min(400px, 60%) 1fr; /* жёсткий трек: минимум из двух */grid-auto-rows: minmax(120px, auto); /* все НЕявные строки такие */auto-rows/auto-columns определяют размер треков, созданных неявно (айтемов больше, чем явных строк).
Размещение айтемов: по линиям и по областям
Заголовок раздела «Размещение айтемов: по линиям и по областям».header { grid-column: 1 / -1; } /* от первой линии до ПОСЛЕДНЕЙ (-1) */.sidebar { grid-row: 2 / 4; } /* строки 2..4 */Шорткат grid-area: row-start / col-start / row-end / col-end. Значение span 2 — «захвати два трека».
grid-template-areas: макет как ASCII-картинка
Заголовок раздела «grid-template-areas: макет как ASCII-картинка».page { display: grid; grid-template-columns: 260px 1fr; grid-template-rows: auto 1fr auto; grid-template-areas: "header header" "sidebar main" "footer footer"; min-height: 100dvh;}
.header { grid-area: header }.sidebar { grid-area: sidebar }.main { grid-area: main }.footer { grid-area: footer }Визуально то же, что и areas-запись:
┌─────────────────────────┬─────────────────────────┐│ header │ header │├─────────────────────────┼─────────────────────────┤│ sidebar │ main │├─────────────────────────┼─────────────────────────┤│ footer │ footer │└─────────────────────────┘└─────────────────────────┘
одно и то же слово в соседних ячейках = одна область.точка "." = пустая ячейка: "sidebar ." ← main занимает колонку 2-3Перестройка макета под другой breakpoint — переписать одну строку-матрицу:
@media (max-width: 800px) { .page { grid-template-columns: 1fr; grid-template-areas: "header" "main" "sidebar" "footer"; }}Медиазапросы подробно — в главе про адаптив; здесь важно: areas — самый читаемый способ описать «holy grail» макет. Визуальная шпаргалка по всем grid-свойствам (включая areas) — CSS Tricks: A Complete Guide to Grid.
Выравнивание: justify и align семейства
Заголовок раздела «Выравнивание: justify и align семейства»Как и во flex, два направления. justify-* — по оси колонок, align-* — по оси строк:
| Свойство | Действие | Аналог во flex |
|---|---|---|
justify-items |
выравнивание айтемов В ЯЧЕЙКАХ по горизонтали | align-items в row |
align-items |
выравнивание в ячейках по вертикали | то же, семантика совпадает |
justify-content |
распределение всей сетки, если она уже контейнера | то же |
align-content |
то же по вертикали для строк | align-content |
justify-self / align-self |
переопределение для одного айтема | align-self |
По умолчанию айтемы растягиваются (stretch) на всю ячейку — поэтому grid-карточки естественно одинаковой высоты в строке, без вложенных флексов.
Плотная упаковка и subgrid
Заголовок раздела «Плотная упаковка и subgrid»grid-auto-flow: dense — при явном размещении «дырки» заполняются следующими подходящими айтемами (плотная упаковка, как в masonry). Внимание: порядок визуальный съезжает — те же риски доступности, что и order во flex.
subgrid — айтем-грид наследует линии РОДИТЕЛЬСКОЙ сетки:
.cards { display: grid; grid-template-columns: repeat(3, 1fr);}.card { display: grid; grid-template-rows: subgrid; /* строки карточки = строки .cards */ grid-row: span 3; /* каждая карточка занимает 3 строки сетки */}Эффект: заголовки всех карточек стоят на одной горизонтали, кнопки — на другой, хотя высота контента у всех разная. Раньше для этого городили вложенные гриды с фиксированными размерами. Поддержка: Firefox с 71, Chrome/Edge с 117, Safari 16+ — уже можно.
Masonry (кирпичная кладка Pinterest) — пока только в Firefox за флагом и спецификацией в дискуссии (grid-template-rows: masonry / grid-template-columns: masonry). В продакшене — JS-решения или column-count. Подробности — в разделе про современный CSS.
Паттерны
Заголовок раздела «Паттерны»Holy Grail макет страницы
Заголовок раздела «Holy Grail макет страницы»Header и footer на всю ширину, сайдбар слева, main справа, равная высота колонок по контенту:
<body class="page"> <header class="header">Шапка</header> <aside class="sidebar">Сайдбар</aside> <main class="main">Контент</main> <footer class="footer">Футер</footer></body>.page { display: grid; grid-template-columns: 260px minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto; grid-template-areas: "header header" "sidebar main" "footer footer"; min-height: 100dvh;}На flex это была связка из двух вложенных флексов + хаки для равной высоты. На grid — одна декларация, колонки равной высоты бесплатно.
Карточная сетка без медиазапросов
Заголовок раздела «Карточная сетка без медиазапросов».cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); gap: 1.5rem;}min(100%, 240px) внутри minmax закрывает граблю мобильных: без него при ширине вьюпорта < 240px+padding’и сетка переполнялась бы. Эта одна строка заменяет четыре медиазапроса «2 колонки → 3 → 4 → 6».
Форма: label + input на одной линии
Заголовок раздела «Форма: label + input на одной линии».form { display: grid; grid-template-columns: max-content minmax(0, 1fr); /* label по контенту, input — остаток */ gap: 0.75rem 1rem; align-items: center;}<form class="form"> <label for="email">Email</label> <input id="email" type="email"> <label for="pass">Пароль</label> <input id="pass" type="password"></form>Каждая пара label+input сама садится в свою строку сетки (автоплейсмент по строкам), выравнивание по центру по вертикали — align-items: center одним свойством. Раньше — float’ы и clearfix’ы.
Типичные ошибки и грабли
Заголовок раздела «Типичные ошибки и грабли»auto-fillвместоauto-fitв карточной сетке — самая частая: карточки не растягиваются, справа пустая колонка. Вспомни схему: fill хранит пустые треки.1frведёт себя какmin-contentпри переполнении — длинный контент ломает сетку. Лечитсяminmax(0, 1fr)илиmin-width: 0на айтеме.- Путаница линий/ячеек при размещении — «почему
grid-column: 1 / 2заняло ДВЕ колонки?» Нет: линия 1 до линии 2 — это ОДИН трек. Счёт всегда по линиям. - Пустые области без точек — в
grid-template-areasкаждая строка матрицы должна иметь одинаковое число ячеек; «дырку» помечают.:"header .", а не пропуском. - Ожидание, что
gapучаствует вfr-расчёте — gap’ы вычитаются из свободного места до деления на доли (см. схему расчёта). Это правильно, но объясняет, почему «не сходится» при ручном подсчёте без gap’ов. - Masonry через grid — «почти работает» — только Firefox/за флагом. Проверяй caniuse перед использованием, в остальных браузерах будет обычная построчная раскладка.
- Нумерация с
-1при НЕявных треках —grid-column: 1 / -1тянет айтем до конца ЯВНОЙ сетки; неявные треки за-1не считаются.
Вопросы на собеседовании
Заголовок раздела «Вопросы на собеседовании»- Чем grid принципиально отличается от flexbox? Grid двумерный по замыслу: одновременно колонки и строки, размещение по линиям/областям. Flex одномерен: одна ось, вторая — вспомогательное выравнивание. Макет страницы и карточные сетки — grid; компонентные ряды — flex.
- Что такое
frи как он считается? Доля свободного места после вычитания жёстких треков и gap’ов:итог = свободное × (fr / Σfr). По сути этоminmax(auto, Nfr)— нижняя граница по контенту, поэтому для «чистого» деления нуженminmax(0, 1fr). - Разница
auto-fillиauto-fit? Оба создают столько треков, сколько влезает. Fill оставляет пустые треки (сетка «держит» количество колонок), fit схлопывает пустые в ноль (колонки перераспределяются между реальными айтемами). Для карточек почти всегда fit. - Как работает
grid-template-areas? Декларативная матрица: строки в кавычках, ячейки — имена областей, повтор имени в соседних ячейках склеивает их в одну область, точка — пустая ячейка. Айтем получает область черезgrid-area: имя. Позволяет перестраивать макет одной медиазапросной правкой. - Что такое subgrid и какую задачу решает?
Вложенный грид наследует линии родителя (
grid-template-rows: subgrid), поэтому элементы внутри разных карточек выравниваются по общей сетке (заголовки на одной линии при разной высоте контента). Поддержка: все современные браузеры с конца 2023. - Как сделать адаптивную карточную сетку без медиазапросов?
repeat(auto-fit, minmax(min(100%, 240px), 1fr))сgap. Сетка сама добавляет/убирает колонки по ширине контейнера;min(100%, …)защищает от переполнения на узких экранах. - Что произойдёт, если айтемов больше, чем явных ячеек сетки?
Создаются неявные треки с размером из
grid-auto-rows/grid-auto-columns(по умолчаниюauto). Позиционирование новых айтемов идёт поgrid-auto-flow(rowпо умолчанию). - Почему
min-width: 0нужен и в flex, и в grid? И там, и там дефолтный минимальный размер трека/айтема равенmin-content(черезmin-width: autoу flex-айтема иautoвfr), поэтому неразрывный контент раздувает раскладку. Ноль снимает защиту.
Практика
Заголовок раздела «Практика»- Holy Grail. Собери макет header/sidebar/main/footer через
grid-template-areas, затем через одну медиаправку переключи на одноколоночный порядок «header, main, sidebar, footer». Критерий: перестройка — только изменение одной строки-матрицы, без правок классов айтемов. - Сетка без медиазапросов. Карточная сетка на
auto-fit+minmax(min(100%, 240px), 1fr). Проверь на 320px, 768px, 1440px: ни один breakpoint не написан руками. Критерий: карточки не переполняются при узком вьюпорте. - Карточки в одну линию через subgrid. Три карточки с разным текстом: заголовки на одной горизонтали, кнопки — на другой. Реши дважды: через subgrid и через flex с
margin-top: auto. Критерий: оба решения работают, можешь назвать плюсы каждого. - Расчёт fr. Контейнер 1200px, gap 24px × 3, колонки
200px 1fr 2fr 1fr. Посчитай ширины на бумаге по формуле, сверь с DevTools. Критерий: расхождение не более 1px. - Автоплейсмент. Сетка 4 колонки, 8 айтемов без явного размещения. Добавь
grid-auto-flow: denseи размести 3-й айтем черезgrid-column: span 2. Наблюдай за скачком порядка. Критерий: объясни, почему 5-й айтем «переехал» и чем это опасно для доступности.
Компаньон этой главы — игра Grid Garden: 28 уровней от grid-column-start до repeat() и minmax().
Что почитать
Заголовок раздела «Что почитать»- MDN: CSS Grid Layout — полный раздел: концепции, треки, области, выравнивание.
- MDN: auto-fill против auto-fit — автоплейсмент и разница близнецов с примерами.
- CSS Tricks: A Complete Guide to Grid — визуальная шпаргалка всех свойств.
- Every Layout: The Grid Layout — composable-философия сеток.
- web.dev: CSS subgrid — разбор subgrid с реальными кейсами карточек.
- Grid Garden — тренажёр grid игрой (пройди полностью).