Изучаем 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 выше в режиме редактирования, вы заметите, что в некоторых контрольных точках в гриде появлятся дырки.

grid-holes

Это из-за того, что по умолчанию авторазмещение использует разреженный (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? Еще столько всего предстоит открыть! Я знаю, это может показаться слишком необъятным. Продвигайтесь понемногу. Верстайте. Экспериментируйте. Нельзя мгновенно понять сразу всё, это нормально.

Дальнейшее чтение

P.S. Это тоже может быть интересно:

1 Комментарий
  1. Денис

    Максим, спасибо за интересную и познавательную статью.

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.