</> Praktum

Главная / CSS / CSS Grid

Урок 4 · CSS

CSS Grid: раскладываем блоки по сетке

Редакция PraktumОбновлено 17 минут на урок с практикойУровень: новичок

CSS Grid — модуль CSS для двумерной раскладки: контейнер описывает колонки и строки, а его прямые дети сами занимают ячейки. Grid включают через display: grid, колонки задают в grid-template-columns, промежутки — в gap. Им собирают каталоги карточек, галереи и макет страницы.

Коротко
  • Сетку описывают один раз — на родителе, дети встают в ячейки сами.
  • 1fr — одна доля свободного места, repeat(3, 1fr) — три равные колонки.
  • repeat(auto-fit, minmax(220px, 1fr)) даёт адаптивную сетку без медиазапросов.
  • Линии нумеруются с 1, grid-column: 1 / -1 растягивает блок на всю ширину.
  • Grid — когда важны и строки, и колонки; для одного ряда проще Flexbox.

Зачем этот урок сейчас

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

Первая сетка

.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}
<div class="cards">
  <article class="card">HTML</article>
  <article class="card">CSS</article>
  <article class="card">JavaScript</article>
  <article class="card">Git</article>
</div>

Получились три равные колонки с промежутком 20px. Четвёртая карточка сама ушла на вторую строку: строки создаются автоматически, их не нужно описывать заранее. Ширину карточкам задавать не надо — её задаёт колонка.

Термины Grid

Из чего состоит сетка
ТерминЧто это
КонтейнерЭлемент с display: grid
ЭлементыЕго прямые дети; внуки в сетку не попадают
ЛинииГраницы колонок и строк, нумеруются с 1
ТрекОдна колонка или одна строка между двумя линиями
ЯчейкаПересечение одной колонки и одной строки
ОбластьПрямоугольник из нескольких ячеек, можно назвать по имени

Единица fr и функция repeat

fr делит свободное место на доли. grid-template-columns: 2fr 1fr — левая колонка вдвое шире правой. Единицы можно смешивать: 240px 1fr — фиксированная боковая колонка и резиновая основная.

.layout { display: grid; grid-template-columns: 240px 1fr; gap: 32px; }
.gallery { display: grid; grid-template-columns: repeat(4, 1fr); }

repeat(4, 1fr) — короткая запись для 1fr 1fr 1fr 1fr. Внутри repeat можно повторять и узор: repeat(2, 1fr 2fr) даст четыре колонки.

Адаптивная сетка без медиазапросов

.catalog {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
}

Читается так: «сделай столько колонок, сколько поместится, каждая не уже 220px, а лишнее место раздели поровну». На широком экране будет четыре колонки, на телефоне — одна. Медиазапрос не нужен, пока меняется только число колонок; он понадобится, если на мобильном меняется сама структура макета.

auto-fit и auto-fill отличаются, когда карточек мало. auto-fill оставляет пустые колонки-заглушки, и две карточки займут лишь часть ряда. auto-fit схлопывает пустые колонки, и две карточки растянутся на всю ширину. Для каталога обычно нужен auto-fit.

Линии: как растянуть элемент

Элемент можно разместить по номерам линий или сказать, сколько треков он занимает.

.feature   { grid-column: 1 / -1; }   /* от первой линии до последней */
.wide      { grid-column: span 2; }   /* две колонки с текущего места */
.sidebar   { grid-column: 3 / 4; grid-row: 1 / 3; }

Конец не включается: grid-column: 2 / 4 занимает колонки 2 и 3. Отрицательные номера считаются с конца, поэтому -1 — всегда последняя линия, даже если число колонок меняется на разных экранах.

Именованные области grid-template-areas

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

.page {
  display: grid;
  grid-template-columns: 220px 1fr;
  grid-template-areas:
    "header header"
    "nav    main"
    "footer footer";
  gap: 24px;
}
.page > header { grid-area: header; }
.page > nav    { grid-area: nav; }
.page > main   { grid-area: main; }
.page > footer { grid-area: footer; }

@media (max-width: 700px) {
  .page {
    grid-template-columns: 1fr;
    grid-template-areas: "header" "main" "nav" "footer";
  }
}

На узком экране достаточно переписать карту областей — HTML не меняется. Области должны быть прямоугольными: буква «Г» из одного имени не сработает.

Выравнивание в Grid

Свойства выравнивания
СвойствоГде пишутЧто выравнивает
justify-itemsконтейнерсодержимое ячеек по горизонтали
align-itemsконтейнерсодержимое ячеек по вертикали
place-itemsконтейнеробе оси сразу: place-items: center
justify-contentконтейнервсю сетку, если колонки уже контейнера
align-contentконтейнервсе строки, если у контейнера есть лишняя высота
justify-self, align-selfэлементодин элемент внутри своей ячейки

По умолчанию элементы растягиваются на всю ячейку (stretch), поэтому карточки в одном ряду получаются одной высоты — то, чего так трудно добиться во flex.

Автоматические строки

Строки, которые не описаны в grid-template-rows, браузер создаёт сам — это неявная сетка. Их высоту задаёт grid-auto-rows: например, grid-auto-rows: minmax(160px, auto) — не ниже 160px, но растёт по содержимому. grid-auto-flow: dense разрешает браузеру заполнять дыры, которые оставили широкие элементы, но тогда визуальный порядок может разойтись с порядком в HTML.

Почему колонка 1fr вылезает

1fr на самом деле означает minmax(auto, 1fr): колонка не может стать уже своего содержимого. Длинный URL, широкая таблица или блок pre раздвигают колонку, и сетка вылезает за экран. Лечится явным минимумом:

.layout { grid-template-columns: 240px minmax(0, 1fr); }

Grid или Flexbox

Grid

Двумерная раскладка: важны и строки, и колонки. Каталог, галерея, расписание, макет страницы.

Flexbox

Один ряд или одна колонка, элементы распределяют свободное место. Меню, шапка, кнопки внутри карточки.

Они не конкуренты: страница на grid, а шапка и кнопки внутри карточек — на flex.

Сетка в DevTools

В Chrome и Firefox рядом с grid-контейнером в панели Elements есть значок grid. Клик по нему рисует поверх страницы линии с номерами и имена областей. Так сразу видно, почему элемент встал не туда: обычно он занимает не те линии или не является прямым ребёнком контейнера.

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

  • Пишут grid-свойства детям, хотя display: grid нужен родителю.
  • Ждут, что внуки встанут в сетку: в неё попадают только прямые дети.
  • Задают фиксированные колонки 300px 300px 300px и ловят горизонтальную прокрутку на телефоне.
  • Путают justify-items (содержимое ячеек) и justify-content (вся сетка).
  • Считают линии с нуля: grid-column: 1 / 3 — это две колонки, а не три.

Доступность

Grid позволяет поставить блок куда угодно, но клавиатура и скринридер идут по порядку HTML. Если визуальный порядок сильно расходится с HTML (через order, номера линий или dense), человек с клавиатурой будет прыгать по странице. Сначала выстрой порядок в HTML по смыслу, сеткой только раскладывай.

Поддержка браузерами

CSS Grid поддерживается всеми актуальными браузерами с 2017 года, включая мобильные. gap для grid работает везде. subgrid — вложенная сетка, наследующая линии родителя, — появился позже и доступен в свежих версиях браузеров; его проверяй на Can I use.

Практика

Собери каталог: карточки не уже 220px, промежуток 16px, число колонок подстраивается под экран без медиазапроса. Первую карточку «Рекомендуем» растяни на всю ширину сетки. Затем добавь в одну карточку длинный URL без пробелов и убедись, что сетка не вылезает за экран.

Показать решение
.catalog {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
}
.catalog .featured {
  grid-column: 1 / -1;
}
.catalog .card {
  min-width: 0;             /* разрешаем сжиматься */
  overflow-wrap: anywhere;  /* переносим длинный URL */
}

Открыть песочницу

Частые вопросы

Чем auto-fit отличается от auto-fill?

Когда карточек меньше, чем помещается колонок, auto-fill оставляет пустые колонки, а auto-fit их схлопывает, и карточки растягиваются на всю ширину. Для каталога обычно выбирают auto-fit.

Что значит 1fr?

Одна доля свободного места в контейнере. 1fr 2fr делит место в пропорции один к двум. Важно: 1fr не сжимается меньше содержимого, для этого пишут minmax(0, 1fr).

Почему элемент не встаёт в сетку?

Чаще всего он не прямой ребёнок grid-контейнера, а вложен в обёртку. Убери лишнюю обёртку, задай ей тоже display: grid или используй display: contents.

Нужны ли медиазапросы с Grid?

Для изменения числа колонок — нет, хватает repeat(auto-fit, minmax(…)). Медиазапрос нужен, когда на узком экране меняется сама структура: например, боковая колонка уходит под основной текст.

Как отцентровать блок через Grid?

Задай родителю display: grid; place-items: center;. Ребёнок встанет по центру по обеим осям.

Источники

Практикум по теме