Веб-разработка
May 22

Удобные сетки на гридах grid — Тренажёр HTML Academy

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

В этой части мы познакомимся с этими чудесными «решётками», рассмотрим их функционал и построим уже современную сетку на гридах.

Знакомьтесь — гриды!

Главное отличие гридов от тех же флексбоксов — это их «двухмерность». На гридах мы работаем сразу и с колонками и со строками, в то время как флексы все-таки базово однострочные.

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

Итак, чтобы задать элементу свойства грид-контейнера указываем значение свойства display: grid;. Сразу, в общем-то, ничего не изменится, по крайней мере визуально — элементы останутся в стандартном потоке друг за другом.

Однако, теперь наш грид-контейнер понимает свойства построения сеток. Причем, в случае грида, что дословно переводится как «сетка» или «решётка», мы будем разбивать контейнер именно на столбцы и строки.

Разделение на столбцы и ряды

Формально, как только мы задаём блоку свойство display: grid; уже создается сетка, но с одним столбцом. Элементы внутри этого столбца переносятся в соответствии со стандартной блочной моделью.

Чтобы разделить контейнер на несколько столбцов нужно использовать свойство grid-template-columns. Он принимает размерные значения, например, пиксели px, проценты % или даже auto:

/* три стобца по 100px шириной */
grid-template-columns: 100px 100px 100px;

Каждое указанное значение — это ширина столбца, а количеством этих значений мы указываем количество столбцов.

Элементы в грид-контейнере, которые раньше занимали один столбец, теперь начнут заполнять по очереди все три столбца. При этом, если элементов больше чем столбцов, они начнут сами переноситься на новую строку, автоматически генерируя ряды:

Размеры условные, отступы для наглядности!

Высота рядов при этом будет растянута так, чтобы равномерно заполнить заданную высоту контейнера. Чтобы явно задать ряды и их размеры, а не надеется на автоматическое заполнение, нужно использовать аналогичное свойство grid-template-rows.

Работает оно абсолютно также как и grid-template-columns, но для создания рядов. Таким образом, используя эти два свойства одновременно, можно создать сетку с определённым размером столбцов и строк.

Можно комбинировать фиксированные значения с нефиксированными, например, подставляя значение auto, создавать относительно гибкую раскладку:

.grid-box {
  display: grid;
  grid-template-columns: 100px auto 100px;
  grid-template-rows: auto 100px;
}
Получится нечто подобное, взято из тренажёра.

Отдельного внимания заслуживает значение доли свободного пространства fr созданная специально для разметки грид-контейнера. Оно, судя по всему, и работает только в гридах, но это не точно.

grid-template-columns: 1fr 2fr;

В этом примере у нас два столбца со значениями fr — первый столбец занимает одну долю, а второй две доли. Что это значит? Свойство поделило контейнер на три равные доли и «раздало» эти доли столбцам в указанных пропорциях.

Чем-то похоже на flex-grow, но намного более понятное и без километровых расчётов. Хотя, конечно, немного менее гибкое.

Координаты грид-элементов

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

К счастью, всем этим можно управлять! Можно задать определённому элементу где и сколько ячеек он будет занимать. Когда грид-контейнер делится на столбцы и ряды у них, очевидно, появляются границы, между которыми они и располагаются.

Эти границы имеют нумерацию, начиная от границы контейнера — слева направо для вертикальных границ и сверху вниз для горизонтальных:

Это и есть наши «координаты», которые мы можем использовать в значениях свойств. Чтобы задать явное расположение элемента на сетке, нам надо буквально указать от каких и до каких границ он будет расположен.

Свойства отвечающие за это и принимающие за значение номер границы:

  • grid-column-start — указывает на начальную вертикальную границу или с какого столбца начнётся область, которую займет элемент;
  • grid-column-end — указывает на конечную вертикальную границу или до какого столбца включительно будет занимать место элемент;
  • grid-row-start — аналогично, указывает на начальную горизонтальную границу или с какого ряда начнется элемент;
  • grid-row-end — указывает на конечную горизонтальную границу;

На примере этой же сетки 4 на 4, чтобы расположить на ней элемент как на картинке ниже:

Нужно указать в свойствах этого элемента:

.grid-element {
  grid-column-start: 3;
  grid-column-end: 5;
  grid-row-start: 3;
  grid-row-end: 4;
}

Или можно немного сократить запись, используя комбинированные свойства:

.grid-element {
  grid-column: 3/5; /* start/end */
  grid-row: 3/4;
}

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

Например, для того же элемента выше, эквивалентной записью будет:

.grid-element {
  grid-column: 3/-1; /* 1-я с конца = 5-я */
  grid-row: 3/-2; /* 2-я с конца = 4-я */
}

Что будет если несколько элементов в грид-контейнере будут пересекаться в некоторых ячейках? Никакой ошибки не будет — они будут наслаиваться друг на друга в порядке как они написаны в разметке, учитывая вложенность.

Тот элемент, что задан ниже по коду будет визуально «над» всеми одноуровневыми элементами, что заданы выше по коду. Работает как слои в любом графическом редакторе.

Этими слоями можно управлять с помощью свойства z-index, который буквально задаёт номер этого слоя. Помогает расположить элементы в нужном порядке, вывести на передний план или наоборот. Подробнее мы о нем поговорим в одной из следующих статей, совсем скоро.

Отлично! Мы научились создавать грид и размещать там элементы. Но этот метод не единственный и не самый быстрый — нужно каждому элементу прописывать начало и конец по двум осям. Попробуем по другому!

Именованные области

Немного отойдем от координат и вообще забудем про цифры. Можем ли мы как-то сказать элементу какие конкретно ячейки он должен занимать, а не в каких рамках? Можем!

В этом нам помогут два взаимосвязанных свойства — grid-template-areas для грид-контейнера и grid-area для элементов.

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

Ячейки у которых совпадает имя будут формировать область, которую заполнит элемент со значением этого имени в свойстве grid-area. Плохо получается объяснять словами, хотя вещь супер-простая. Давайте на примере!

Создадим грид размером 4 на 4 с помощью этого свойства:

.grid-named {
  display: grid;
  grid-template-areas: "water water lava lava"
                       "water water lava lava"
                       "grass grass lava lava"
                       "rocks rocks rocks rocks";
}

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

Теперь у нас есть своего рода шаблон — 4 строки в кавычках и есть ряды, в каждой строке по 4 имени — ячейки. Где имена ячеек совпадают формируются области под элемент. Например имя «water» формирует область из четырёх ячеек.

Теперь нам нужно связать наш «шаблон» с элементами внутри контейнера. Делается это через свойство grid-area которое указывается элементам:

.grid-element-water {
  grid-area: water;
}
.grid-element-lava {
  grid-area: lava;
}
.grid-element-grass {
  grid-area: grass;
}
.grid-element-rocks {
  grid-area: rocks;
}

В итоге вот что у нас получается:

Элементы заняли те ячейки которые совпадали по имени из шаблона, заданного в свойстве grid-template-areas! Таким образом сильно сокращается код — в свойстве контейнера мы разметили области, а в свойствах элементов просто связали их с этими областями.

Важный нюанс — области должны быть неразрывны и прямоугольны. Не получится задать область, например, в виде буквы «Г» или одновременно в первом и третьем столбце.

Также при формировании шаблона должны совпадать количество имён, то бишь, ячеек в строке. То есть во всех рядах должно быть одинаковое количество столбцов.

И последний нюанс — без явного указания размеров столбцов через свойство grid-template-columns и строк через свойство grid-template-rows, грид-контейнер будет поделён поровну, то есть каждая ячейка займет 1fr и по горизонтали и по вертикали.

Продвинутые методы построения грида

Метод построения грида с помощью именованных зон grid-area очень крутой, но если размерность колонок и рядов нужна отличная от 1fr, то придется указывать свойства grid-template-columns и grid-template-rows.

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

Первое такое значение это функция повтора repeat() — да, это именно функция в значении CSS свойства! В скобках она принимает два аргумента — первое, количество колонок или рядов, которое нужно создать и второе — их размер.

.grid-container {
  display: grid;
  grid-template-columns: repeat(4, 100px); /* 4 колонки по 100px */
}

Даже в таком виде полезность этой функции очевидна, но это не всё! Как мы помним, свойства грид-шаблона могут принимать несколько значений. Функция repeat() лишь одно из них, хотя и делает сразу несколько вещей.

Таким образом, мы можем создать не просто повторяющиеся столбцы, а например, выделить один столбец или строку отдельным значением и дописать остальное повтором:

.grid-container {
  display: grid;
  grid-template-columns: 200px repeat(3, 100px);
  grid-template-rows: 80px repeat(4, 200px) 100px;
  /* Получится грид 4 на 6 */
}

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

Можно ли как-то задать более гибкие свойства гриду? Например, сделать так, чтобы элементы переносились с одной строки на другую в зависимости от размера окна. Ответ — можно! Делается это с помощью специального аргумента для функции repeat() — auto-fit, которое подставляется на место количества колонок:

.grid-container {
  display: grid;
  grid-template-columns: repeat(auto-fit, 100px);
}

Этот аргумент автоматически генерирует столько столбцов шириной 100px, сколько нужно, чтобы уместить максимально возможное количество элементов.

Если у нас, например, контейнер шириной 300px и 4 элемента, то будет создано 3 столбца по 100px, а 4-й элемент перенесётся на новый ряд. Если ширина контейнера увеличится до 400px или больше, то аргумент автоматически создаст еще один столбец, куда поместится 4-й элемент.

Шикарно! Но мы пойдем еще дальше! Сейчас ширина столбцов фиксированная и в промежутках ширины не кратных 100px, например, 380px — у нас окажется пустое, свободное место. Как нам это исправить?

Можно задать создаваемым колонкам динамическую ширину, но не просто в процентах % или auto, а более умное значение-функцию minmax(). Оно принимает два размерных значения — минимальный и максимальный размер:

.grid-container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
}

В данном случае мы указали 100px как минимальный размер колонки, которая может уместиться на строке, а за максимальный размер взяли 1fr — колонка будет растягиваться до тех пор, пока не появится достаточно свободного места под еще одну. Таким образом, у нас не появится пустого пространства:

Заполнение ячеек и поток грида

У нас получился супер гибкий грид-контейнер, что уже чудесно. Теперь вернёмся к самим элементам и их позициям. Выше мы разбирали свойства grid-column и grid-row — они принимают номера границ и с их помощью мы можем точно расположить элемент на сетке.

Но в случае нашей гибкой раскладки количество этих границ меняется в зависимости от ширины контейнера. Колонок может быть 3, а может быть 8 или 12 и, соответственно, количество границ меняется.

Что делать если нужно, чтобы элемент постоянно занимал несколько ячеек? В этом случае нам поможет наследие таблиц! Свойства grid-column и grid-row принимают специальное значение span, которое указывает сколько ячеек может занимать элемент:

.grid-element {
  grid-column: span 2;
  grid-row: span 2;
  /* займет 2 столбца и 2 строки - в сумме 4 яч. */
}

Таким образом, даже без указания координат можем создавать элементы занимающие несколько ячеек:

Кстати, работают всё те же правила «выталкивания» соседнего контента, что и в таблицах — в картинке выше ячейка 4 подвинула ячейку 5.

С этим значением неизбежно возникает проблема — если этот элемент окажется в последней ячейке на строке, то он перенесётся на новую, так как в одну он просто не поместится:

Из-за чего образуется пустота и вся наша гибкость насмарку. Однако, у этой проблемы есть решение — свойство grid-auto-flow! Оно позволяет управлять потоком грид-контейнера и с его помощью можно этот поток изменять.

Очень сильно напоминает аналогичное свойство у флексбоксов, меняющее направление главной оси, только действует немного наоборот. Оно имеет несколько значений:

  • row — значение по умолчанию, все элементы идут в ряд друг за другом и переносятся на новую строку при переполнении;
  • column — поворачивает поток, элементы начинают заполнять столбец, а не ряд и при переполнении генерировать новый столбец;
  • dance — значение немного ломающее поток, позволяет элементам размещаться так, что бы заполнить все пустые места, при этом игнорируя их порядок указанный в вёрстке:

Нюанс значения dance в том, что порядок полностью нарушается и, например, если 2-я карточка займёт область 2×2, то вытеснит всё остальное, из-за чего порядок будет еще более хаотичный.

Поэтому следует его использовать только если порядок не имеет значения и главное заполняемость пустых мест.

Отступы и выравнивание элементов

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

Первым разберём свойство gap отвечающее за интервалы между элементами в грид-контейнере. При этом, отличие от margin заданного отдельно элементам в том, что интервалы не появляются между границей контейнера и элементами, а только именно между ними.

То есть одним свойством gap мы решаем большую проблему внешних отступов и их потенциального выпадения. Очень удобно. Еще удобнее то, что можно отдельно указать интервал между столбцами column-gap и интервал между рядами row-gap. При этом как и, например, в margin есть короткая запись:

.grid-container {
  row-gap: 20px; 
  column-gap: 50px;
  /* эквивалент */
  gap: 20px 50px;
}

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

В этом нам помогут свойства выравнивания:

  • justify-items — задаётся контейнеру, отвечает за выравнивание всех элементов грид-контейнера по горизонтали;
  • justify-self — задаётся элементу, отвечает за его выравнивание по горизонтали;
  • align-items — задаётся контейнеру, отвечает за выравнивание всех элементов грид-контейнера по вертикали;
  • align-self — задаётся элементу, отвечает за его выравнивание по вертикали.

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

В каком-то смысле у гридов даже больше свойств, например, justify-self работает только в гридах и не работает во флексбоксе. Значения у них все те же самые — stretch по умолчанию, start, end и т. д. Не буду повторять — все они разобраны в предыдущей статье про флексы.

Испытания

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

В этих же испытаниях акцент на гридах как и положено по названию части. Первое испытания на понимание координат — нужно верно расположить контент на грид-раскладке.

Второе испытание на построение сетки. Причем, нам дают свободу выбора как её строить. Я сделал через именованные области, мне показалось, что так будет проще.

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


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

Эту статью я делал параллельно с предыдущей про флексы, поэтому она вышла так быстро. Здесь по сути компиляция двух блоков, о чем мы поговорим следом. Будем немножечко ругать тренажёры HTML Академии за их раздробленность.

А вас я могу только поблагодарить за внимание! Спасибо, что читаете или даже бегло смотрите, мне правда очень приятно.

Ссылки на предыдущие статьи по HTML Academy:

Знакомство с Веб-разработкой
Знакомство с HTML и CSS
Знакомство с JavaScript
Знакомство с PHP
Таблицы и подробно о формах
Наследование, каскады и селекторы
Блочная модель, поток и сетка на float
Гибкие флексбоксы display: flex
Удобные сетки на гридах display: grid <- Вы здесь
Пропуск блока «Погружение»
Позиционирование и двумерные трансформации
Теневое искусство и линейные градиенты
CSS-фильтры и Кекстаграм
Мастерские
Продвинутые Мастерские
...

Остальные статьи можно посмотреть у меня на главной странице блога.

Также мои соц. сетки, которые я продолжаю вести:

Мой Twitter
Мой Telegram
Мой Паблик ВК

Заходите куда удобно вам и подписывайтесь! Еще раз спасибо за внимание!