CSS Grid вглубь: areas, minmax, auto-fill

Поднимаемся над «просто колонками»: именованные области, гибкие треки и адаптивные сетки без медиазапросов.

CSS Grid — это двумерная система раскладки: вы одновременно управляете строками и столбцами, описывая сетку треков, а элементы расставляете по линиям или по именованным областям.

Зачем это знать на практике

Базового display: grid и пары колонок хватает для простых случаев. Но реальные макеты дышат: меню сворачивается, контента то много, то мало, ширина экрана непредсказуема. Если вы умеете описать сетку через minmax(), auto-fill и fr, то целая адаптивная галерея карточек умещается в одну строку CSS — без единого медиазапроса. А grid-template-areas превращает раскладку страницы в наглядную ASCII-карту, которую видно прямо в коде. Это экономит десятки строк и делает вёрстку устойчивой к изменениям контента.

Единица fr и распределение свободного места

Единица fr (fraction) — сердце Grid. Она означает «доля свободного пространства, оставшегося после вычитания фиксированных размеров и зазоров». Запись grid-template-columns: 1fr 2fr делит свободное место в пропорции 1 к 2: вторая колонка вдвое шире первой.

.layout {
  display: grid;
  /* боковая панель фиксирована, контент забирает остаток */
  grid-template-columns: 240px 1fr;
  gap: 24px;
}

Важно: fr считается после gap, поэтому зазоры никогда не «вылезают» за контейнер. И ещё тонкость — 1fr по умолчанию имеет минимальный размер auto, то есть трек не сожмётся уже своего содержимого. Это частый источник переполнения, к которому мы вернёмся в разделе про ошибки.

repeat() и повторяющиеся треки

Писать 1fr 1fr 1fr 1fr утомительно. Функция repeat() повторяет шаблон треков:

.grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);   /* четыре равные колонки */
  gap: 16px;
}

/* можно повторять не одно значение, а группу */
.alt {
  grid-template-columns: repeat(3, 200px 1fr);  /* 6 колонок: узкая+широкая ×3 */
}

minmax(): гибкость с границами

Функция minmax(min, max) задаёт треку диапазон. Классика — колонка, которая не уже 200px, но с радостью растягивается: minmax(200px, 1fr). Минимум защищает от схлопывания, максимум 1fr позволяет занять свободное место.

.sidebar-content {
  display: grid;
  /* сайдбар от 180 до 280px, контент — остаток */
  grid-template-columns: minmax(180px, 280px) 1fr;
  gap: 24px;
}

Особое значение min-content внутри minmax спасает узкие колонки от переполнения: minmax(min-content, 1fr) разрешает треку сжаться до самого длинного неразрывного слова, но не уже.

auto-fill против auto-fit: галерея в одну строку

Самый эффектный приём Grid — адаптивная сетка карточек без медиазапросов. Ключевые слова auto-fill и auto-fit внутри repeat() сами вычисляют, сколько колонок поместится:

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 20px;
}

Браузер берёт ширину контейнера и набивает в неё столько колонок шириной минимум 220px, сколько влезет; при сужении окна число колонок уменьшается само. Разница между ключевыми словами проявляется, когда карточек меньше, чем влезает колонок:

ЗначениеПоведение при нехватке элементов
auto-fillсоздаёт пустые «фантомные» треки — карточки прижаты влево, справа пустота
auto-fitсхлопывает пустые треки в ноль — существующие карточки растягиваются на всю ширину

Правило простое: хотите, чтобы немногие карточки растянулись на весь ряд — берите auto-fit; хотите сохранить «исходную» ширину и выравнивание по сетке — auto-fill.

grid-template-areas: раскладка как ASCII-карта

Именованные области — самый читаемый способ собрать макет страницы. Вы рисуете сетку строками-строками, а элементам присваиваете имя области:

.page {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  min-height: 100vh;
  gap: 16px;
}
.page > header  { grid-area: header; }
.page > nav     { grid-area: sidebar; }
.page > main    { grid-area: main; }
.page > footer  { grid-area: footer; }

Точка . в кавычках обозначает пустую ячейку. Все области должны образовывать прямоугольник — нельзя задать L-образную область, браузер просто проигнорирует такую раскладку. Зато перестроить макет под мобильный — это переписать одну карту в медиазапросе, не трогая HTML.

Именованные линии и выравнивание

Линии сетки можно именовать прямо в шаблоне, в квадратных скобках, и затем ссылаться на имена вместо номеров:

.grid {
  display: grid;
  grid-template-columns: [start] 1fr [content-start] 3fr [content-end] 1fr [end];
}
.hero { grid-column: content-start / content-end; }

Выравнивание в Grid — две оси и четыре свойства. По строке (инлайн-ось) работают justify-items и justify-content, по столбцу (блочная ось) — align-items и align-content. *-items выравнивают содержимое внутри ячеек, а *-content распределяют саму сетку в контейнере, когда треки уже всего места. Отдельной ячейке можно переопределить выравнивание через justify-self / align-self.

.grid {
  display: grid;
  grid-template-columns: repeat(3, 100px);
  justify-content: space-between; /* раскидать колонки по ширине */
  align-items: center;           /* центрировать содержимое ячеек по вертикали */
}

Как это работает под капотом

Алгоритм Grid выполняется в несколько проходов. Сначала браузер строит явную сетку из grid-template-*. Если элементов больше, чем ячеек, добавляются неявные треки, чьи размеры задаёт grid-auto-rows / grid-auto-columns (по умолчанию auto). Затем идёт размер треков: фиксированные значения резервируются первыми, далее распределяется содержимое для auto и min/max-content, и только в конце оставшееся место делится между fr пропорционально их весам. Поэтому fr — это всегда «остаток», а не абсолютная доля ширины: добавьте фиксированную колонку, и доли fr пересчитаются. Понимание порядка проходов объясняет, почему fr-колонка иногда не сжимается: её базовый минимум — auto, то есть размер контента, и до дележа остатка она уже «застолбила» это место.

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

Переполнение из-за минимального auto у fr и 1fr. Длинное слово, картинка или pre внутри 1fr-трека раздувают его шире контейнера, и появляется горизонтальная прокрутка. Лечится заменой на minmax(0, 1fr) — нулевой минимум разрешает треку сжаться.

Путают auto-fill и auto-fit. Берут auto-fill и удивляются, почему две карточки не растянулись на весь ряд. Для растяжения нужен auto-fit.

Незамкнутый прямоугольник в areas. Если именованная область образует не прямоугольник (например, «лесенку»), вся grid-template-areas игнорируется без ошибки в консоли — макет молча ломается.

Путают *-items и *-content. Ставят justify-content: center, ожидая центрирования текста в ячейках, а это центрирует всю сетку. За содержимое ячеек отвечают justify-items / align-items.

Итоги

  • fr делит оставшееся место после фиксированных треков и gap; у него минимум auto.
  • repeat() сокращает повторы, minmax() задаёт треку диапазон «min–max».
  • repeat(auto-fill, minmax(220px, 1fr)) — адаптивная галерея без медиазапросов; auto-fit растягивает немногие элементы, auto-fill оставляет пустые треки.
  • grid-template-areas описывает макет читаемой картой; области обязаны быть прямоугольными.
  • *-items выравнивают содержимое ячеек, *-content — всю сетку; против переполнения помогает minmax(0, 1fr).
Проверьте себя
1. Чем auto-fit отличается от auto-fill в repeat(auto-fit, minmax(220px, 1fr)), когда карточек меньше, чем влезает колонок?
Aauto-fit схлопывает пустые треки в ноль, и существующие карточки растягиваются на всю ширину
Bauto-fit создаёт пустые фантомные треки, прижимая карточки влево
CНикакой разницы нет, оба ведут себя одинаково
Dauto-fit запрещает перенос карточек на новую строку
2. Что означает единица fr в grid-template-columns?
AФиксированную ширину во фреймах
BДолю свободного места, оставшегося после фиксированных треков и gap
CПроцент от ширины окна браузера
DРазмер шрифта родителя, как em
3. Почему 1fr-колонка с длинным неразрывным словом может вызвать горизонтальную прокрутку и как это исправить?
AУ 1fr минимальный размер равен auto (по контенту); заменить на minmax(0, 1fr)
Bfr не поддерживается в Grid; использовать только проценты
CНужно убрать gap, он добавляет ширину
DЭто баг браузера, лечится только медиазапросом