Изучаем CSS-гриды
Перевод статьи Learning CSS Grids с сайта varun.ca для CSS-live.ru, автор — Варун Вачхар
+-------------------------+ +---+ | | | | | CSS Grid | | | | Layout Module | | | | | | | +-------------------------+ | | +----+ +------------------+ | | | | | | | | | | | | | | | | +------------------+ +---+ | | +------+ +---------------+ | | | | | | +----+ +------+ +---------------+
CSS-гриды пришли! С марта 2017-го их поддерживают все основные браузеры. В свете этой хорошей новости я решил выкроить немного времени, чтобы лучше понять, как их использовать. Изучение далось мне ощутимо тяжелее, чем я рассчитывал — модуль CSS Grid Layout вводит порядка 17 новых понятий. Пытаться охватить их все было как-то слишком, так что я стал дробить материал на части, поясняя их заметками и примерами, которыми я и собираюсь поделиться. Это ни в коей мере не исчерпывающее руководство. Возможно, оно даже не на 100% фактически верно. Но оно должно дать достаточно информации, чтобы сложить в уме схему, как работают CSS-гриды, и начать верстать на них.
Терминология
Прежде чем начать верстать на гридах, нужно выучить несколько новых терминов. Грид-контейнер — элемент, к которому применили display: grid
. Все его непосредственные потомки — грид-элементы.
Определяя грид, мы указываем, сколько рядов и колонок нам нужно. Затем система грид-раскладки генерирует нумерованные линии, которые делят грид по горизонтали и по вертикали. Это грид-линии. Пространство между двумя соседними грид-линиями называется грид-полосой. Это по сути собирательный термин для колонок и рядов.
Грид-линии +---+---+---+---+---+---+ 1 | | | | | | | | | | | | | | | | | | | | | +-------+---+-----------+ 2 | |xxx| | | | | | |xxx|<-- Grid Cell | | |xxx| | | | | +-----------------------+ 3 | | | | | | | | | | | | | | | | | | | | | +---+---+---+---+---+---+ 4 1 2 3 4 5 6 7 Полосы-ряды +---+---+---+---+---+---+ |xxxxxxxxxxxxxxxxxxxxxxx| |xxxxxxxxxxxxxxxxxxxxxxx| |xxxxxxxxxxxxxxxxxxxxxxx| +-----------------------+ | | | | | | | | | | | | | | | | | | | | | +-----------------------+ | | | | | | | | | | | | | | | | | | | | | +---+---+---+---+---+---+ Полосы-колонки +---+---+---+---+---+---+ |xxx| | | | | | |xxx| | | | | | |xxx| | | | | | +xxx--------------------+ |xxx| | | | | | |xxx| | | | | | |xxx| | | | | | +xxx--------------------+ |xxx| | | | | | |xxx| | | | | | |xxx| | | | | | +---+---+---+---+---+---+
Нумерованные линии грида можно использовать при размещении элементов. Грид-полоса — пространство между любыми двумя линиями в гриде (точнее, между любыми двумя соседними линиями — прим. перев.).
Грид-ячейка — наименьшая единица в гриде. Это пересечение ряда и колонки. По смыслу она очень похожа на ячейку таблицы.
Грид-область — пространство, образованное пересечением 4 грид-линий. Другими словами, это набор из одной и более соседних грид-ячеек. Важно: грид-области могут быть только прямоугольными. Нельзя делать Т- или Г-образные грид-области. Мне почему-то первым делом захотелось именно этого.
Грид-область +---+---+---+---+---+---+ | | | | | | | | | | | | | | | | | | | | | +---+---+-----------+---+ | | |xxxxxxxxxxx| | | | |xxxxxxxxxxx| | | | |xxxxxxxxxxx| | +---+---|xxxxxxxxxxx|---+ | | |xxxxxxxxxxx| | | | |xxxxxxxxxxx| | | | |xxxxxxxxxxx| | +---+---+-----------+---+
Грид-элементы могут охватывать одну или более ячеек — по рядам или по колонкам — это и есть грид-область.
Определяем грид
Есть много разных способов задать грид. Я собираюсь сосредоточиться на базовом сценарии: 4 колонки на 5 рядов. grid-template-rows
с grid-template-columns
позволяют задавать число и размер рядов и колонок соответственно. Можно перечислять полосы, как показано в примере ниже.
.my-grid-container { display: grid; grid-template-columns: 20px 20px 20px 20px; grid-template-rows: 20px 20px 20px 20px 20px; } .my-grid-container +---+----+----+----+-------------+ | | | | | | | | | | | | +---+----+----+----+ | | | | | | | | | | | | | +---+----+----+----+ | | | | | | | | | | | | | +---+----+----+----+ | | | | | | | | | | | | | +---+----+----+----+ | | | | | | | | | | | | | +---+----+----+----+ | | | | | | | | | +--------------------------------+
Простейший грид с 4 колонками и 5 рядами. Ряды определены с помощью grid-template-rows
, а колонки — с помощью grid-template-columns
.
Грид готов! Не самый полезный из возможных гридов, но всё-таки грид. В примере выше у каждого ряда высота 20px, а у каждой колонки ширина 20px. Единственное, что тут стоит отметить — то, что grid-template-rows
и grid-template-columns
позволяют определить грид-полосы. Затем браузер генерирует грид-линии автоматически. Эти линии невидимы. Они помогают располагать элементы в гриде, но не влияют на дизайн визуально. Но всё же возможность видеть эти линии крайне полезна при отладке. К счастью, в Firefox есть встроенный грид-инспектор, который визуализирует нам грид (ура!).
В этом примере размеры полос в гриде фиксированы. Это так по-модному называется то, что у рядов и/или колонок фиксированная ширина. Следовательно, грид останется одного и того же размера независимо от размера контейнера.
Гибкие гриды
Можно определить и грид с гибкими размерами полос. Или даже с фиксированными и гибкими полосами одновременно! (См. пример ниже.) Значения в процентах тут относятся к процентам от грид-контейнера.
.my-weird-but-flexible-grid { display: grid; grid-template-columns: 20px 5% 80px 40%; grid-template-rows: 4rem 2vh 12% 80px 20em; } .my-weird-but-flexible-grid +-+--+----+----------------------+ | | | | | | +-+--+----+-----------------+ | +-+--+----+-----------------+ | | | | | | | | | | | | | +-+--+----+-----------------+ | | | | | | | +-+--+----+-----------------+ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +-+--+----+-----------------+ | | | | | | | | | | | +--------------------------------+
Гибкий грид, где мы намешали разных единиц для размеров полос — пиксели, проценты, единицы вьюпорта и em-ы.
Единица fr
Давайте взглянем на еще один пример гибкого грида. Тут мы по-прежнему будем делать сетку 4х5, но нам надо, чтоб ряды и колонки растягивались и заполняли весь контейнер. Для этого мы воспользуемся единицей fr
.
На этом месте у вас, возможно, готов вырваться вопрос: какую-какую единицу?
.my-grid { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-rows: 1fr 1fr 1fr 1fr 1fr; /* Выше можно было написать ещё и так: grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(5, 1fr); */ } .my-grid +------+------+------+------+ | | | | | | | | | | | | | | | +------+------+------+------+ | | | | | | | | | | | | | | | +------+------+------+------+ | | | | | | | | | | | | | | | +------+------+------+------+ | | | | | | | | | | | | | | | +------+------+------+------+ | | | | | | | | | | | | | | | +------+------+------+------+
Гибкий грид с 4 колонками и 5 рядами. Его гибкость обеспечена заданием рядам и колонкам значения 1fr
.
У нас новая единица! Единица fr
представляет собой долю доступного пространства в грид-контейнере. Вы писали когда-либо flex: 1
, flex: 2
, и т.д.? Вот и это работает точно так же. Можно задавать размеры полос как соотношение свободного места, напр. 1fr 2fr
.
На заметку: можно задавать размеры полос в единицах длины, процентах или fr
.
Интервалы
Грид не будет настоящим гридом без интервалов между полосами. Для этого предусмотрены свойства grid-column-gap
и grid-row-gap
, либо их сокращение grid-gap
. Интервалы в гриде создаются только между полосами. Перед первой полосой или после последней полосы интервалов нет.
Важно: даже если между двумя соседними полосами есть интервал, это всё равно одна грид-линия.
.my-grid { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-rows: 1fr 1fr 1fr 1fr 1fr; grid-gap: 1rem; } .my-grid 1 2 3 4 5 +----+-+----+-+----+-+----+ 1 | | | | | | | | | | | | | | | | +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----+ 2 | | | | | | | | | | | | | | | | +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----+ 3 | | | | | | | | | | | | | | | | +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----+ 4 | | | | | | | | | | | | | | | | +----+ +----+ +----+ +----+ +----+ +----+ +----+ +----+ 5 | | | | | | | | | | | | | | | | +----+-+----+-+----+-+----+ 6
Гибкий грид 4х5 с интервалами (зазорами). Интервалы можно делать свойством grid-gap
.
Размещение грид-элементов
Теперь, когда мы определили грид, давайте рассмотрим некоторые варианты того, как можно размещать в нем элементы. Во многом сродни флексбоксам, размещение в гриде не зависит от порядка элементов в разметке. Это позволяет создавать сложные раскладки, которые можно полностью перестраивать с помощью медиавыражений. Подробнее об этом чуть дальше. Пока сосредоточимся на неких основах.
Размещение элементов в гриде довольно похоже на абсолютное позиционирование. Только мы указываем не координаты в единицах длины, а начальную и конечную грид-линии.
.my-grid 1 2 3 4 5 +----+-+----+-+----+-+----+ 1 | | | | | | | | | | | | | | | | | | | | | | | | grid-row-start (2)-------------> o-----------o <-----(5) grid-column-end grid-column-start (3) | | | | |xxxxxxxxxxx| | | | | |xxxxxxxxxxx| | | | | |xxxxxxxxxxx| +----+ +----+ |xxxxxxxxxxx| | | | | |xxxxxxxxxxx| | | | | |xxxxxxxxxxx| | | | | |xxxxxxxxxxx| grid-row-end (4)------------------> o-----------o 4 | | | | | | | | | | | | | | | | | | | | | | | | +----+ +----+ +----+ +----+ 5 | | | | | | | | | | | | | | | | | | | | | | | | +----+-+----+-+----+-+----+ 6
Можно разместить элемент в гриде с помощью grid-column-start
, grid-column-end
и grid-row-start
, grid-row-end
.
Грид-линии автоматически пронумерованы. Начиная с 1
для крайней верхней и крайней левой линий. Эти номера мы используем при объявлении grid-column-start / grid-column-end
и grid-row-start / grid-row-end
. Можно считать и с конца. Для этого начинаем с -1
для крайней правой и крайней нижней линий. Наконец, можно указывать линии с помощью того, сколько полос должен занять элемент. Например, grid-row-end
: span 3
означает, что номер конечной линии — начальная грид-линия + 3 полосы.
Внизу — пример на CodePen, показывающий несколько разных стилей размещения. Обратите внимание, что грид-элементы могут перекрываться. Для управления порядком наложения можно использовать свойство z-index
.
See the Pen CSS Grid Positioning by Varun Vachhar (@winkerVSbecks) on CodePen.
Автоматизируем всё, что можно
Под конец я хочу продемонстрировать еще пару понятий на примерах. Я хочу воспроизвести эту сетку товаров с сайта Aldo. Вот что здесь надо отметить:
- На больших экранах (
> 60em
) это сетка 4х5 - В ней размещены 13 элементов
- Некоторые элементы занимают по 2 колонки и/или 2 ряда
- У всех картинок пропорции
1000 : 1270
- Между ячейками интервал в
1rem
, а по периметру грида отступ тоже в1rem
- Максимальная ширина грида ограничена до
60em
See the Pen Aldo Style Product Grid (CSS Grid) by Varun Vachhar (@winkerVSbecks) on CodePen.
Грид также меняется в определенных контрольных точках. На средних устройствах (от 30em
до 60em
) он сокращается до 3 колонок, а на маленьких устройствах (< 30em
) — до двух. Наконец, нам нужно сохранять пропорцию 1000 : 1270
между размерами рядов и колонок.
Большие экраны min-width: 60em (4 колонки) +-----------+ +-----------+ | шир. 2 | | | | | | | +-----------+ | шир. 2 | +----+ +----+ | выс. 2 | | | | | | | | | | | | | +----+ +----+ +-----------+ +----+ +----+ +----+ +----+ | | | | | | | | | | | | | | | | +----+ +----+ +----+ +----+ +-----------+ +----+ +----+ | | | | | | | | | | | | | шир. 2 | +----+ +----+ | выс. 2 | +----+ +----+ | | | | | | | | | | | | +-----------+ +----+ +----+ |<----max-width: 60em---->| Средние экраны min-width: 30em and max-width: 60em (3 колонки) +------------------+ | шир. 3 | | | +------------------+ +-----------+ +----+ | | | | | | | | | шир. 2 | +----+ | выс. 2 | +----+ | | | | | | | | +-----------+ +----+ +----+ +----+ +----+ | | | | | | | | | | | | +----+ +----+ +----+ +-----------+ +----+ | | | | | | | | | шир. 2 | +----+ | выс. 2 | +----+ | | | | | | | | +-----------+ +----+ +----+ +----+ +----+ | | | | | | | | | | | | +----+ +----+ +----+ Маленькие экраны min-width: 60em (2 колонки) +-----------+ | шир. 2 | | | +-----------+ +-----------+ | | | | | шир. 2 | | выс. 2 | | | | | +-----------+ +----+ +----+ | | | | | | | | +----+ +----+ +----+ +----+ | | | | | | | | +----+ +----+ +----+ +----+ | | | | | | | | +----+ +----+ +-----------+ | | | | | шир. 2 | | выс. 2 | | | | | +-----------+ +----+ +----+ | | | | | | | | +----+ +----+ +----+ +----+ | | | | | | | | +----+ +----+
Грид с товарами в стиле Aldo. 2 колонки на маленьких устройствах, 3 на средних и 4 — на больших.
Определяем размеры рядов и колонок
До сих пор мы определяли грид с помощью чего-то типа grid-template-columns: repeat(4, 1fr)
and grid-template-rows: repeat(5, 1fr)
. Из-за пропорции в 1000 : 1270
мы больше так не можем. Вместо этого нам понадобится немного математики, чтобы найти размеры рядов и колонок. Мы можем воспользоваться для этого CSS-переменными и calc. Рассчитать ширину колонок и высоту рядов можно по этой формуле:
width = (width_of_grid - (gutters + padding)) / number_of_columns height = 1270 * width / 1000
Контрольная точка | Ширина грида | Интервалы + внутр. отступ | Число колонок |
---|---|---|---|
По умолчанию (до 30em) | 100vw | (1 + 2)rem | 2 |
min-width: 30em and max-width: 60em |
100vw | (2 + 2)rem | 3 |
min-width: 60em | 60em | (3 + 2)rem | 4 |
Итак, в случае по умолчанию width = (100vw - 3rem) / 2
. Чудесно! Давайте соберем всё вместе. Начнем с определения значения по умолчанию в :root. Затем в каждой контрольной точке пересчитаем width
и обновим свойство grid-template-columns
.
:root { --width: calc( (100vw - 3rem) / 2 ); --height: calc( 1270 * var(--width) / 1000 ); } .layout { grid-gap: 1rem; grid-template-columns: repeat( 2, var(--width) ); grid-auto-rows: var(--height); } @media screen and (min-width: 30em) and (max-width: 60em) { :root { --width: calc( (100vw - 4rem) / 3 ); } .layout { grid-template-columns: repeat( 3, var(--width) ); } } @media screen and (min-width: 60em) { :root { --width: calc( (60em - 5rem) / 4 ); } .layout { grid-template-columns: repeat( 4, var(--width) ); } }
Автоматические ряды
В предыдущем примере кода вы могли обратить внимание на grid-auto-rows: var(--height)
. Это дает браузеру команду автоматически генерировать ровно столько рядов, сколько нужно, чтобы уместить все элементы. Так что если мы добавим еще элементов, он добавит еще рядов, и наоборот. Значение grid-auto-rows
— то, какой высоты ряды нам нужны.
Пример на CodePen ниже показывает, как будет выглядеть наш грид на данный момент. Откройте его в режиме редактирования, чтобы увидеть, как работает расширение и сужение окна, а также что происходит при добавлении и удалении
See the Pen Aldo Style Product Grid (CSS Grid) STEP 1 for blogpost by Varun Vachhar (@winkerVSbecks) on CodePen.
На заметку. Вы обратили внимание, что нам не пришлось размещать элементы в гриде?! Если не указывать положение, то браузер размещает элементы в гриде автоматически. Это называется авторазмещением.
Элементы на несколько полос
Как я отметил ранее, некоторые элементы охватывают несколько полос. Для этого мы можем использовать прием с grid-<row|column>-end: span <число>
. Картинки у нас охватывают одно и то же число полос при всех размерах экранов. Но вот информация о товаре охватывает 3 колонки на средних экранах и 2 колонки в остальных диапазонах. Поэтому мы применяем к ним wide-2 wide-3-m
. Остальные элементы используют wide-2 tall-2
.
.wide-2 { grid-column-end: span 2; } .tall-2 { grid-row-end: span 2; } @media screen and (min-width: 30em) and (max-width: 60em) { .wide-3-m { grid-column-end: span 3; } }
See the Pen Aldo Style Product Grid (CSS Grid) STEP 2 for blogpost by Varun Vachhar (@winkerVSbecks) on CodePen.
Хотите узнать больше об автоматическом размещении? Вот потрясающие туториал и наглядный пример Рэйчел Эндрю.
Плотная упаковка
У нас есть грид. Мы воспользовались авторазмещением, чтобы расположить в нем элементы, и некоторые элементы занимают по нескольку колонок и рядов. Так что, задача решена? Не совсем. Если вы посмотрите пример на CodePen выше в режиме редактирования, вы заметите, что в некоторых контрольных точках в гриде появлятся дырки.
Это из-за того, что по умолчанию авторазмещение использует разреженный (sparse) алгоритм упаковки, который может оставлять дырки в раскладке. Хорошая новость в том, что мы можем переключиться на плотный (dense) алгоритм упаковки, который заставляет браузер оглядываться назад и заполнять все пустые ячейки в раскладке.
.layout { grid-gap: 1rem; + grid-auto-flow: dense; grid-template-columns: repeat(2, var(--width)); grid-auto-rows: var(--height); }
Вот и всё! Внизу — окончательная версия. И вот еще один пример, использующий авторазмещение и плотную упаковку для отображения музыкальных альбомов.
See the Pen Aldo Style Product Grid (CSS Grid) by Varun Vachhar (@winkerVSbecks) on CodePen.
Заключение
Задумайтесь на миг об открывшихся возможностях. Подумайте о сколь угодно безумных раскладках, которые мы теперь можем построить. Не приходит на ум ни одна? Не беда. Заскочите в лабораторию раскладок Джен Симмонс за кое-какими идеями. CSS Grid Layout наверняка здорово повлияет на веб-дизайн. Множество макетов, от которых вы отказывались, потому что их нельзя было реализовать на CSS, запросто могут оказаться возможны благодаря CSS-гридам.
Что дальше? Вы знали, что грид-линии можно именовать? Вы знали, что можно выравнивать элементы в полосах с помощью свойств align
and justify
? А что можно указывать размеры в виде диапазонов «от и до» с помощью minmax
? Еще столько всего предстоит открыть! Я знаю, это может показаться слишком необъятным. Продвигайтесь понемногу. Верстайте. Экспериментируйте. Нельзя мгновенно понять сразу всё, это нормально.
Дальнейшее чтение
- Гриды на примерах
- Впрыгиваем в CSS Grid 🌼
- Основные понятия грид-раскладки – MDN
- Спецификация CSS Grid
P.S. Это тоже может быть интересно:
Максим, спасибо за интересную и познавательную статью.