MaxSite.org
Всё о создании веб-сайтов

Верстка с помощью CSS Grid Layout

CSS, HTML, LESS, SASS

Спецификация по Grid Layout опубликована уже достаточно давно, но вебмастера всё ещё редко используются grid-вёрстку. Тут две основные причины. Первая — распространенность IE11 (по статистике около 2%), которая, к тому же поддерживает только старую версию спецификации. Вторая — реальная потребность в grid не очень высока: существующего flex хватает для 90% стоящих задач.

C другой cтороны, все современные браузеры уже полноценно поддерживают grid, поэтому можно потихонечку разбираться в его основах и решать некоторые задачи.

Grid — это не flex

Flex — тоже сетка, но с предопределенным поведением, когда ячейки всегда располагаются вдоль основной оси. Grid — более гибкий инструмент, который оперирует ячейками уже в более привычном для нас понимании — строками и столбцами.

Grid очень похож на Flex. Более того, grid позволяет оперировать новыми сущностями, которых не хватает во flex — области ячеек, линии между строками/столбцами, зазоры и т.д. Поэтому, если стоит задача сделать сложную сетку, то grid — хороший выбор.

Основы построения grid-сетки

Как и во flex, для задания grid используется только контейнер, в котором все непосредственные потомки будут являться ячейками.

<style>
.grid1 { display: grid; } 
</style>
 
<div class="grid1">
    <div class="pad10 bg-gray100">A</div>
    <div class="pad10 bg-gray200">B</div>
    <div class="pad10 bg-gray300">C</div>
    <div class="pad10 bg-gray400">D</div>
</div>
A
B
C
D

В данном случае у нас сетка из 4-х ячеек. Поскольку мы ещё не задали «формат» сетки, то браузер их отобразил как обычные блоки.

В примерах я буду использовать классы UniCSS.

Для задания «формата» сетки используются свойства контейнера grid-template-columns и grid-template-rows.

<style>
.grid2 { 
    display: grid; 
    grid-template-columns: 100px 200px 300px;
} 
</style>
 
<div class="grid2">
    <div class="pad10 bg-gray100">A</div>
    <div class="pad10 bg-gray200">B</div>
    <div class="pad10 bg-gray300">C</div>
    <div class="pad10 bg-gray400">D</div>
</div>
A
B
C
D

В данном примере я указал три колонки с разной шириной. Теперь добавим размеры строк.

<style>
.grid3 { 
    display: grid; 
    grid-template-columns: 100px 200px 300px;
    grid-template-rows: 50px 100px;
} 
</style>
 
<div class="grid3">
    <div class="pad10 bg-gray100">A</div>
    <div class="pad10 bg-gray200">B</div>
    <div class="pad10 bg-gray300">C</div>
    <div class="pad10 bg-gray400">D</div>
</div>
A
B
C
D

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

Единица измерения fr

В качестве единиц измерения, можно использовать привычные пиксели или проценты, но лучше использовать «фракцию» (fraction) — долю. По поведению она похожа на flex-свойство grow («жадность»).

<style>
.grid4 { 
    display: grid; 
    grid-template-columns: 1fr 2fr 3fr;
    grid-template-rows: 50px 100px;
} 
</style>
 
<div class="grid4">
    <div class="pad10 bg-gray100">A</div>
    <div class="pad10 bg-gray200">B</div>
    <div class="pad10 bg-gray300">C</div>
    <div class="pad10 bg-gray400">D</div>
</div>
A
B
C
D

Расчёт примерно такой: 1fr 2fr 3fr = 6fr, следовательно 3fr = 50% всей ширины сетки, 2fr = 1/3 и 1fr = 1/6 части.

Если все колонки/строки одинаковые, то можно использовать функцию repeat(колво, размер):

<style>
.grid5 { 
    display: grid; 
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: 50px 100px;
} 
</style>
 
<div class="grid5">
    <div class="pad10 bg-gray100">A</div>
    <div class="pad10 bg-gray200">B</div>
    <div class="pad10 bg-gray300">C</div>
    <div class="pad10 bg-gray400">D</div>
</div>
A
B
C
D

Кроме этого, часто нужно указывать размеры ячеек в каком-то диапазоне. Например колонка может быть от 100px до 200px. Для таких задач используется функция minmax(минимум, максимум).

<style>
.grid6 { 
    display: grid; 
    grid-template-columns: minmax(100px, 200px) 2fr 3fr;
    grid-template-rows: 50px 100px;
} 
</style>
 
<div class="grid6">
    <div class="pad10 bg-gray100">A</div>
    <div class="pad10 bg-gray200">B</div>
    <div class="pad10 bg-gray300">C</div>
    <div class="pad10 bg-gray400">D</div>
</div>
A
B
C
D

Зазоры между ячейками

Зазоры (gap) — это что-то вроде padding/margin, только без привязки к самим ячейкам. Свойство grid-gap задаёт зазоры между строками и столбцами.

<style>
.grid7 { 
    display: grid;
    grid-template-columns: minmax(100px, 200px) 2fr 3fr;
    grid-template-rows: 50px 100px;
    grid-gap: 10px 30px;
} 
</style>
 
<div class="grid7">
    <div class="pad10 bg-gray100">A</div>
    <div class="pad10 bg-gray200">B</div>
    <div class="pad10 bg-gray300">C</div>
    <div class="pad10 bg-gray400">D</div>
</div>
A
B
C
D

Задание области

Ячейки можно задавать в виде «области» (area). Сделать это можно с помощью grid-template-areas и grid-area.

<style>
.grid8 { 
    display: grid;
    grid-template-columns: 1fr 2fr 2fr 1fr;
    grid-template-rows: auto;
    grid-template-areas: "a a a a"
                         "b b c c"
                         "d d d d";
} 
 
.grid8 .item-a { grid-area: a; }
.grid8 .item-b { grid-area: b; }
.grid8 .item-c { grid-area: c; }
.grid8 .item-d { grid-area: d; }
</style>
 
<div class="grid8">
    <div class="item-a pad10 bg-gray100">A</div>
    <div class="item-b pad10 bg-gray200">B</div>
    <div class="item-c pad10 bg-gray300">C</div>
    <div class="item-d pad10 bg-gray400">D</div>
</div>
A
B
C
D

То есть каждая ячейка может иметь идентификатор (class, id), которому задаётся имя в пределах grid-шаблона. В grid-template-columns указываются колонки, а в grid-template-areas их имя в пределах сетки. Через grid-area мы связываем эти ячейки с областями сетки.

На базе этой схемы можно формировать произвольные области ячеек.

<style>
.grid9 { 
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
    grid-template-rows: 50px 100px 50px;
    grid-template-areas: "a a c c" 
                         "b b c c" 
                         "b b d d";
} 
.grid9 .item-a { grid-area: a; }
.grid9 .item-b { grid-area: b; }
.grid9 .item-c { grid-area: c; }
.grid9 .item-d { grid-area: d; }
</style>
 
<div class="grid9">
    <div class="item-a pad10 bg-gray100">A</div>
    <div class="item-b pad10 bg-gray200">B</div>
    <div class="item-c pad10 bg-gray300">C</div>
    <div class="item-d pad10 bg-gray400">D</div>
    <div class="pad10 bg-green100">E</div>
    <div class="pad10 bg-green200">F</div>
    <div class="pad10 bg-green300">G</div>
    <div class="pad10 bg-green400">H</div>
</div>
A
B
C
D
E
F
G
H
Следует учитывать, что области должны быть прямоугольными.

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

Индивидуальное поведение ячеек

Также можно задать индивидуальное поведение ячеек с помощью свойств

grid-column-start: 1; 
grid-column-end: 4; 
grid-row-start: 1; 
grid-row-end: 2;

По сути это тоже самое, что и с помощью grid-template-areas, только отдельными свойствами.

<style>
.grid10 { 
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
}
 
.grid10 .item-a { 
    grid-column-start: 1; 
    grid-column-end: 4; 
}
 
.grid10 .item-b { 
    grid-column-start: 1; 
    grid-column-end: 3; 
}
</style>
 
<div class="grid10">
    <div class="item-a pad10 bg-gray100">A</div>
    <div class="item-b pad10 bg-gray200">B</div>
    <div class="item-c pad10 bg-gray300">C</div>
    <div class="item-d pad10 bg-gray400">D</div>
</div>
A
B
C
D

Здесь нужно учитывать, что расположение ячейки указывается между грид-треками, а не между грид-линиями. Поскольку у нас 4 колонки, то треков будет 5.

1
2
3
4
5

Именно поэтому в ячейке A последняя секция оказалась пустой, а вторая ячейка B заняла две секции.

При таком задании положения, может возникнуть ситуация, когда ячейки окажутся в одной позиции. Для grid-сетки это нормальное поведение и чтобы определить какая ячейка должна быть сверху используется привычное z-index. Посмотрите на пример, где ячейки A и B накладываются друг на друга, но слой A поднимаем выше.

<style>
.grid11 { 
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr;
}
 
.grid11 .item-a { 
    grid-column-start: 1; 
    grid-column-end: 4;
    grid-row-start: 1; 
    grid-row-end: 2;
    z-index: 1;
}
 
.grid11 .item-b { 
    grid-column-start: 1; 
    grid-column-end: 5; 
    grid-row-start: 1; 
    grid-row-end: 2;
}
</style>
 
<div class="grid11">
    <div class="item-a pad10 bg-yellow400">A</div>
    <div class="item-b pad10 bg-blue300 t-right">B</div>
    <div class="item-c pad10 bg-gray300">C</div>
    <div class="item-d pad10 bg-gray400">D</div>
</div>
A
B
C
D

Такой приём удобно использовать, когда нужно задать поведение индивидуальной ячейки. В html-коде просто задаётся класс ячейки, а в css уже его положение. Если же нужно задать поведение всей сетки, то лучше всё-таки сделать это в контейнере с помощью grid-template-areas.

Короткие записи свойств

Разработчики CSS Grid Layout постарались на славу. Многие grid-свойства можно задать короткой записью. Я не привожу примеры, поскольку их слишком много (например свойство grid) и это пугает. Разобраться, так с ходу, довольно сложно, поэтому я думаю, что лучше запомнить азы, а дальше уже двигаться в сторону усложнений.

Применение Grid

Совершенно очевидно, что grid следует применять там, где подразумевается сложное расположение элементов/блоков. С точки зрения адаптивности, grid — не самый лучший выбор, поскольку он оперирует сразу двумя коордионатами, что делает его использование более сложным чем flex.

Второй момент — grid очень сильно завязан на верстку конкретной сетки. Это не позволяет вынести его верстку на уровень HTML в виде css-классов (atomic design).

Поэтому правило довольно простое: там где можно использовать flex, grid использовать не стоит. :-)

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

Комментарий будет опубликован после проверки. Вы соглашаетесь с правилами сайта.

(обязательно)