CSS Grid. Практические примеры
Будущее наступило: CSS Grid поддерживается всеми современными браузерами, девтулзы для работы с ним доступны и в Firefox, и в Хроме, а с автопрефиксером и осторожностью можно использовать гриды даже в IE 10/11.
Но, знакомясь с туториалами, я заметил, что большинство из них фокусируются либо на том, как работают конкретные свойства, либо на том, как сверстать постер или журнальный разворот. Да, это интересно, помогает погрузиться в тему и показывает всю мощь нового инструмента, но как использовать полученные знания в работе?
В этой статье я хочу поделиться накопленным за последний год опытом использования CSS Grid в коммерческих проектах. Ниже я разберу примеры, как применять гриды для вёрстки всей страницы, групп элементов и отдельных компонентов.
Для демонстрации я буду пользоваться девтулзами Хрома:
Если вы пока мало знакомы с гридами, статья всё равно может оказаться полезной, но за подробным описанием свойств и их возможных значений советую обращаться к гайду от CSS-Tricks.
Колоночная сетка
Начнём с того, как использовать гриды для вёрстки всей страницы. Если вы привыкли верстать стандартной колоночной сеткой (например, из бутстрапа), то гриды с лёгкостью позволяют придерживаться такого подхода.
Для этого достаточно создать грид-контейнер с 12 равными по ширине колонками, а дальше у детей указывать, какие колонки им занять:
Чтобы адаптировать такую вёрстку под маленькие разрешения, можно заменить сетку на 6-колоночную и обновить позиции элементов:
@media (max-width: 720px) < .main < grid-template-columns: repeat(6, 1fr); >.hero < grid-column: 1 / span 6; >.title < grid-column: 2 / span 3; >>
Пример использования такого подхода можно увидеть на нашем лендинге:
С 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
На флексах для такого простого лейаута пришлось бы делать несколько вложенных обёрток, а информация о размерах блоков размазалась бы по обёрткам и их детям. C CSS Grid всё это лежит в одном месте — в контейнере. При этом описание областей в CSS даёт представление о том, как результат будет выглядеть в браузере.
Более того, в такую разметку легко добавить адаптив, ничего не меняя в детях:
Витрина карточек
От лейаута страницы переходим к её содержимому.
Витрина — пожалуй, самый часто встречаемый контейнер для однородных элементов. С помощью неё могут отображаться товары интернет-магазина, новости, фотографии в альбоме, в общем, что угодно прямоугольной формы.
Верстая такую витрину на флексах, я каждый раз сталкивался с одними и теми же проблемами:
- Как задать отступы между карточками? Есть свойство gap, но оно пока ещё плохо поддерживается. Поэтому приходится давать всем элементам отступы друг от друга, а у их контейнера отрицательным отступом компенсируем лишние отступы крайних элементов.
- Как сделать так, чтобы карточки в последнем ряду не растягивались на полную ширину? Флекс будет тянуть элемент на всё доступное место, так что придётся либо хардкодить ширину элементов, либо заполнять последнюю строку невидимыми элементами.
- Как сделать адаптив? Нужно опытным путём вычислить ширину, на которой наши карточки перестают нормально вмещаться, и на ней поправить значения ширины для карточек, чтобы было меньше столбцов.
Гриды решают все эти проблемы буквально в 3 строки:
- Отступы между элементами явно указаны в grid-gap .
- Карточки в последнем ряду будут занимать свои колонки и не будут растягиваться.
- Адаптив без медиа-запросов: у карточки указана минимально допустимая ширина 250px, а дальше сетка уже сама считает, сколько таких карточек поместится в строку.
Другие варианты витрин на гридах:
- CodePen витрины с горизонтальным скроллом при сужении экрана;
- CodePen витрины с карточками разных размеров;
- CodePen с masonry grid;
- статья в CSS-Tricks, где подробно разбирается пример адаптивной сетки с карточками разных размеров.
Форма
Типичная форма состоит из 2 столбцов: в левом идут названия полей, в правом — инпуты. Проблема с таким расположением иногда заключается в том, что мы не знаем, какой длины будут названия полей, но хотим, чтобы все строки формы были выровнены.
Сделать такое на флексах можно, только захардкодив ширину левой колонки, чтобы в каждой строке она была одинаковой. Можно сделать на table, но это будет использованием не по назначению. И тут на помощь приходит CSS Grid:
Левой колонке мы задаём ширину auto , чтобы она подстраивалась под длину лейблов, а правой колонке выделяем 1fr , то есть всё оставшееся от левой колонки место.
Чтобы сделать форму вертикальной на мобильных, мы можем заменить сетку на 1-колоночную и уменьшить отступ между лейблом и инпутом, образовавшийся из-за grid-gap :
@media (max-width: 500px) < form < grid-template-columns: 1fr; >label < margin-bottom: -0.75rem; >>
И на десктопном, и на мобильном разрешениях кнопка сабмита занимает полную ширину контейнера. Это происходит благодаря следующей строчке:
Эта запись означает, что кнопка будет расположена между самой первой вертикальной линией сетки (1) и самой последней (-1). То есть она будет занимать целую строку, независимо от того, сколько в сетке столбцов.
Группировка ячеек
В форме может понадобиться объединить инпуты в , но с гридами не так очевидно, как это сделать. Грид (как и флекс) описывает только расположение своих детей. Если в детей вложены другие элементы, то на них грид уже не влияет. В будущем мы сможем решать такие задачи с помощью subgrid, но как быть сейчас?
Я пользуюсь для таких целей display: contents , но стоит учитывать, что у него:
Элемент с display: contents при рендеринге заменяется своими детьми, поэтому если такой элемент лежит внутри грид-контейнера, то его дети будут располагаться на сетке так, будто они сами лежат внутри грид-контейнера.
Так как сам элемент с display: contents не рендерится, то и визуальных свойств у него быть не может. Но зато он может реагировать на события мыши и определять css-переменные. Вот пример, как с помощью такого элемента реализовать ховер на группу ячеек грида:
Карточка с вариациями отображения
Теперь переходим к конечным компонентам, при вёрстке которых нас тоже может выручить грид.
Один из таких компонентов — информационная карточка (например, товара или объявления). Часто у таких карточек есть не один вариант отображения, а сразу несколько. Например, нам нужно реализовать адаптив, красиво утрамбовывая элементы карточки в меньший объём. Или предоставить несколько вариантов отображения для разных мест размещения: на витрине, в рекламе или в попапе предпросмотра.
Гриды позволяют красиво решать такие задачи, даже не меняя HTML-разметку. Для примера разберём вот такую карточку объявления о сдаче в аренду квартиры:
Все 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-row / grid-column , то наложение элементов на сетке можно сделать частичным:
В данном примере кнопка частично накладывается на фото, но избегает наложения на текст справа от неё. С абсолютным позиционированием достигнуть такого эффекта было бы сложнее, так как кнопка была бы вырвана из потока и наезжала на текст.
Столбчатая диаграмма
Ещё один пример из практики — график столбиками (bar chart). Тут использование грида позволяет каждому сегменту графика пропорционально растягивать всю колонку:
- Если в какой-то группе станет больше столбиков, то место под подпись тоже увеличится.
- И наоборот — если где-то будет длинная подпись, то увеличится место под столбики.
В реальной жизни количество сегментов было динамическое, так что значение для свойства grid-template-columns формировалось в JS. Тут для упрощения возьмём график из 3 сегментов с разным количеством столбиков в каждом. Для каждой вертикальной и горизонтальной линии указываем название, чтобы проще было располагать на этих линиях элементы:
Теперь можно довольно легко добавить вариации положений основных элементов графика: лейблов, оси, единиц измерения. Благодаря именованным линиям, можно явно описывать, на какую линию мы хотим переместить элемент:
.axisOnRight .axis < grid-column: right; >.unitOnBottom .unit
Попробовать переключение в деле можно, покликав на кнопки под графиком:
Ложки дёгтя
Используя CSS Grid, стоит учитывать и некоторые ограничения:
- Анимации размера столбцов/строк предусмотрены в спеке, но поддерживаются только в Firefox. Впрочем, можно дать столбцу размер auto , а анимировать размер его содержимого.
- Subgrid тоже пока поддерживается только в Firefox. Его появление в остальных браузерах откроет возможности для новых подходов и упростит группировку элементов в HTML. Также с помощью subgrid можно будет вообще всю страницу уложить в одну сетку. Так что ждём.
- С осторожностью используйте гриды в случаях, когда в них вложены флексы и размер контента может динамически меняться. Это может вызвать проблемы с производительностью из-за большого количества пересчётов. Держите руку на пульсе счётчика FPS и вместо Nfr используйте minmax(Xpx, Nfr) , чтобы упростить пересчёт.
Заключение
По итогам года с гридами могу сказать, что использование CSS Grid не только упростило мою работу, но и сделало её интереснее. Гриды не пытаются заменить флексбокс или таблицы, а дают верстальщикам современный и мощный инструмент. Когда удаётся использовать гриды для подходящей задачи, решение обычно получается простым и наглядным.
Надеюсь, на этих примерах мне удалось показать пользу гридов, а кого-то даже вдохновить их попробовать.
Ежемесячная рассылка CSSSR
Новости, свежие статьи и многое другое