CSS Grid: Практические примеры

Иллюстрация, на которой человек собирает стену из разноцветных кирпичиков
Иллюстрация говорящего человека

Ежемесячная рассылка CSSSR

Новости, свежие статьи и многое другое

Отправляя данную форму, я подтверждаю своё согласие на получение рекламных и информационных материалов, а также факт своего ознакомления и согласия с

Будущее наступило: CSS Grid поддерживается всеми современными браузерами, девтулзы для работы с ним доступны и в Firefox, и в Хроме, а с автопрефиксером и осторожностью можно использовать гриды даже в IE 10/11.

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

В этой статье я хочу поделиться накопленным за последний год опытом использования CSS Grid в коммерческих проектах. Ниже я разберу примеры, как применять гриды для вёрстки всей страницы, групп элементов и отдельных компонентов.

Для демонстрации я буду пользоваться девтулзами Хрома:

Настройки девтулзов для CSS Grid в Хроме

Если вы пока мало знакомы с гридами, статья всё равно может оказаться полезной, но за подробным описанием свойств и их возможных значений советую обращаться к гайду от CSS-Tricks.

Колоночная сетка

Начнём с того, как использовать гриды для вёрстки всей страницы. Если вы привыкли верстать стандартной колоночной сеткой (например, из бутстрапа), то гриды с лёгкостью позволяют придерживаться такого подхода.

Для этого достаточно создать грид-контейнер с 12 равными по ширине колонками, а дальше у детей указывать, какие колонки им занять:

.main {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-gap: 1rem;
}

.hero {
  grid-column: 1 / span 12;
}

.title {
  grid-column: 3 / span 4;
}

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

@media (max-width: 720px) {
  .main {
    grid-template-columns: repeat(6, 1fr);
  }
  
  .hero {
    grid-column: 1 / span 6;
  }

  .title {
    grid-column: 2 / span 3;
  }
}

Пример использования такого подхода можно увидеть на нашем лендинге:

Скриншот сайта csssr.com на десктопном разрешении
12-колоночная сетка на десктопном разрешении
Скриншот сайта csssr.com на мобильном разрешении
6-колоночная сетка на мобильном разрешении

С CSS Grid можно не ограничиваться колонками и задать количество строк (grid-template-rows) тоже, чтобы иметь возможность точно позиционировать каждый элемент (grid-row).

Разметка страницы на области

Возможности CSS Grid не заканчиваются на колоночной сетке и включают новый способ размечать страницу. С помощью grid-template-areas можно разделить страницу на области, а части страницы раскидать по этим областям:

body {
  display: grid;
  grid-template-areas:
    'header header'
    'menu   content'
    'footer footer';
  grid-template-columns: 12rem 1fr;
  grid-template-rows: 5rem 1fr 3rem;
}

header {
  grid-area: header;
}

article {
  grid-area: content;
}

aside {
  grid-area: menu;
}

footer {
  grid-area: footer;
}
Разметка страницы по областям на десктопном разрешении

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

Более того, в такую разметку легко добавить адаптив, ничего не меняя в детях:

@media (max-width: 600px) {
  body {
    grid-template-areas:
      'header'
      'menu'
      'content'
      'footer';
    grid-template-columns: 1fr;
    grid-template-rows: 5rem auto 1fr 3rem;
  }
}
Разметка страницы по областям на мобильном разрешении

Вот полный код примера:

Витрина карточек

От лейаута страницы переходим к её содержимому.

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

Верстая такую витрину на флексах, я каждый раз сталкивался с одними и теми же проблемами:

  • Как задать отступы между карточками? Есть свойство gap, но оно пока ещё плохо поддерживается. Поэтому приходится давать всем элементам отступы друг от друга, а у их контейнера отрицательным отступом компенсируем лишние отступы крайних элементов.
  • Как сделать так, чтобы карточки в последнем ряду не растягивались на полную ширину? Флекс будет тянуть элемент на всё доступное место, так что придётся либо хардкодить ширину элементов, либо заполнять последнюю строку невидимыми элементами.
  • Как сделать адаптив? Нужно опытным путём вычислить ширину, на которой наши карточки перестают нормально вмещаться, и на ней поправить значения ширины для карточек, чтобы было меньше столбцов.

Гриды решают все эти проблемы буквально в 3 строки:

main {
  display: grid;
  grid-template-columns: repeat(
    auto-fit,
    minmax(250px, 1fr)
  );
  grid-gap: 1rem;
}
Гифка, где при сужении экрана в сетке становится всё меньше колонок
  • Отступы между элементами явно указаны в grid-gap.
  • Карточки в последнем ряду будут занимать свои колонки и не будут растягиваться.
  • Адаптив без медиа-запросов: у карточки указана минимально допустимая ширина 250px, а дальше сетка уже сама считает, сколько таких карточек поместится в строку.

Вот полный код примера:

Другие варианты витрин на гридах:

Форма

Типичная форма состоит из 2 столбцов: в левом идут названия полей, в правом — инпуты. Проблема с таким расположением иногда заключается в том, что мы не знаем, какой длины будут названия полей, но хотим, чтобы все строки формы были выровнены.

Сделать такое на флексах можно, только захардкодив ширину левой колонки, чтобы в каждой строке она была одинаковой. Можно сделать на table, но это будет использованием не по назначению. И тут на помощь приходит CSS Grid:

form {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-gap: 1rem;
  align-items: baseline;
}
Форма с горизонтальным расположением лейблов и инпутов

Левой колонке мы задаём ширину auto, чтобы она подстраивалась под длину лейблов, а правой колонке выделяем 1fr, то есть всё оставшееся от левой колонки место.

Чтобы сделать форму вертикальной на мобильных, мы можем заменить сетку на 1-колоночную и уменьшить отступ между лейблом и инпутом, образовавшийся из-за grid-gap:

@media (max-width: 500px) {
  form {
    grid-template-columns: 1fr;
  }
  label {
    margin-bottom: -0.75rem;
  }
}
Форма с вертикальным расположением лейблов и инпутов

И на десктопном, и на мобильном разрешениях кнопка сабмита занимает полную ширину контейнера. Это происходит благодаря следующей строчке:

button {
  grid-column: 1 / -1;
}

Эта запись означает, что кнопка будет расположена между самой первой вертикальной линией сетки (1) и самой последней (-1). То есть она будет занимать целую строку, независимо от того, сколько в сетке столбцов.

Вот полный код примера:

Группировка ячеек

В форме может понадобиться объединить инпуты в <fieldset>, но с гридами не так очевидно, как это сделать. Грид (как и флекс) описывает только расположение своих детей. Если в детей вложены другие элементы, то на них грид уже не влияет. В будущем мы сможем решать такие задачи с помощью subgrid, но как быть сейчас?

Я пользуюсь для таких целей display: contents, но стоит учитывать, что у него:

Элемент с display: contents при рендеринге заменяется своими детьми, поэтому если такой элемент лежит внутри грид-контейнера, то его дети будут располагаться на сетке так, будто они сами лежат внутри грид-контейнера.

Так как сам элемент с display: contents не рендерится, то и визуальных свойств у него быть не может. Но зато он может реагировать на события мыши и определять css-переменные. Вот пример, как с помощью такого элемента реализовать ховер на группу ячеек грида:

Карточка с вариациями отображения

Теперь переходим к конечным компонентам, при вёрстке которых нас тоже может выручить грид.

Один из таких компонентов — информационная карточка (например, товара или объявления). Часто у таких карточек есть не один вариант отображения, а сразу несколько. Например, нам нужно реализовать адаптив, красиво утрамбовывая элементы карточки в меньший объём. Или предоставить несколько вариантов отображения для разных мест размещения: на витрине, в рекламе или в попапе предпросмотра.

Гриды позволяют красиво решать такие задачи, даже не меняя HTML-разметку. Для примера разберём вот такую карточку объявления о сдаче в аренду квартиры:

Карточка аренды квартиры, размер L
Карточка размера L
.card.size_l {
  grid-template-areas:
    'photo info    price'
    'photo desc    desc'
    'photo actions date';
  grid-template-columns: 20rem 1fr max-content;
}
Карточка аренды квартиры, размер M
Карточка размера M
.card.size_m {
  grid-template-areas:
    'photo  photo'
    'info   price'
    'info   actions'
    'desc   desc';
  grid-template-columns: 1fr max-content;
}
Карточка аренды квартиры, размер S
Карточка размера S
.card.size_s {
  grid-template-areas:
    'photo'
    'info';
}

CodePen с полным кодом примера

Все 3 состояния используют одну HTML-разметку, но в CSS у них разная конфигурация областей и положения элементов. Обратите внимание, что на размерах M и S некоторые блоки располагаются друг поверх друга. Это тоже сделано на гридах, но уже с помощью наложения.

Наложение элементов в одной области

Приём в том, что мы разных детей складываем в одну и ту же grid-area. При этом они не располагаются, как обычные блоки, друг под другом, а накладываются поверх. Дальше с помощью align-self/justify-self можно поставить каждого ребёнка в свой угол:

Пример расположения нескольких элементов в одной области
.photo {
  grid-area: photo;
}

.size_s .price {
  grid-area: photo;
  align-self: start;
  justify-self: stretch;
}

.size_s .date {
  grid-area: photo;
  align-self: end;
  justify-self: start;
}

.size_s .actions {
  grid-area: photo;
  justify-self: end;
  align-self: end;
}

Частичное наложение элементов

Если располагать элементы не по областям, а назначать им положение в сетке с помощью grid-row/grid-column, то наложение элементов на сетке можно сделать частичным:

В данном примере кнопка частично накладывается на фото, но избегает наложения на текст справа от неё. С абсолютным позиционированием достигнуть такого эффекта было бы сложнее, так как кнопка была бы вырвана из потока и наезжала на текст.

Столбчатая диаграмма

Ещё один пример из практики — график столбиками (bar chart). Тут использование грида позволяет каждому сегменту графика пропорционально растягивать всю колонку:

Столбчатая диаграмма на гридах
  • Если в какой-то группе станет больше столбиков, то место под подпись тоже увеличится.
  • И наоборот — если где-то будет длинная подпись, то увеличится место под столбики.

В реальной жизни количество сегментов было динамическое, так что значение для свойства grid-template-columns формировалось в JS. Тут для упрощения возьмём график из 3 сегментов с разным количеством столбиков в каждом. Для каждой вертикальной и горизонтальной линии указываем название, чтобы проще было располагать на этих линиях элементы:

.bar-chart {
  display: grid;
  grid-template-columns:
    [left] auto
    [bar1] auto
    [bar2] auto
    [bar3] auto
    [right] auto;
  grid-template-rows:
    [top] auto
    [center] 10rem
    [bottom] auto;
}

.axis {
  grid-row: center;
  grid-column: left;
}

.unit {
  grid-row: top;
  grid-column: left;
}

Теперь можно довольно легко добавить вариации положений основных элементов графика: лейблов, оси, единиц измерения. Благодаря именованным линиям, можно явно описывать, на какую линию мы хотим переместить элемент:

.axisOnRight .axis {
  grid-column: right;
}

.unitOnBottom .unit {
  grid-row: bottom;
}

Попробовать переключение в деле можно, покликав на кнопки под графиком:

Ложки дёгтя

Используя CSS Grid, стоит учитывать и некоторые ограничения:

  • Анимации размера столбцов/строк предусмотрены в спеке, но поддерживаются только в Firefox. Впрочем, можно дать столбцу размер auto, а анимировать размер его содержимого.
  • Subgrid тоже пока поддерживается только в Firefox. Его появление в остальных браузерах откроет возможности для новых подходов и упростит группировку элементов в HTML. Также с помощью subgrid можно будет вообще всю страницу уложить в одну сетку. Так что ждём.
  • С осторожностью используйте гриды в случаях, когда в них вложены флексы и размер контента может динамически меняться. Это может вызвать проблемы с производительностью из-за большого количества пересчётов. Держите руку на пульсе счётчика FPS и вместо Nfr используйте minmax(Xpx, Nfr), чтобы упростить пересчёт.

Заключение

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

Надеюсь, на этих примерах мне удалось показать пользу гридов, а кого-то даже вдохновить их попробовать.

Иллюстрация говорящего человека

Ежемесячная рассылка CSSSR

Новости, свежие статьи и многое другое

Отправляя данную форму, я подтверждаю своё согласие на получение рекламных и информационных материалов, а также факт своего ознакомления и согласия с

Читайте также

Комментарии

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