Как CSS Grid меняет представление о структурировании контента

Автор оригинала: Kevin Powell
  • Перевод
Каждый, кто хотя бы немного занимался созданием веб-сайтов, знает, что теги <div> — являются важным строительным блоком для контроля над макетом.

HTML5 представил новые семантические элементы, чтобы помочь в этом. И хотя они являются фантастическим дополнением к языку, они немного похожи на украшение к нашему супу из <div> элементов.



С приходом CSS Grid, нам больше не нужно полагаться на элементы <div> для создания структуры страницы или даже более сложного компонента. Структура буквально определяется родительским элементом, а не тем, как расположено содержимое внутри него.

Это значит, что мы можем получить хороший простой макет, который структурирует содержимое, не обращая внимание на то, как оно изначально организовано с помощью элементов <div>.



CSS Grid может быть сложным, но и Flexbox тоже


Я слышал о жалобах множества людей, что Grid слишком сложны и что Flexbox отлично справляется с теми же задачами. Я бы сказал, что им просто комфортно использовать Flexbox и поэтому нет желания дополнительно изучать CSS Grid.

Технология CSS Grid действительно вводит множество новых свойств и значений, так что да, есть кривая обучения. Но и Flexbox также довольно сложен.

Можете ли вы рассказать о преимуществах использования свойства flex-basis вместо width? Или как Flexbox рассчитывает ширину flex-элементов, если мы её явно не задали?

Например, если вы покажете приведённый ниже пример тому, кто никогда не использовал Flexbox, как вы объясните тот факт, что в нём заданы одинаковая разметка и одинаковые стили для обоих наборов столбцов? Что ещё хуже, второй столбец в обоих случаях имеет ширину 50%. Очевидно, что ширина со значением 50% на самом деле не устанавливает размер элемента в 50%.


«Что ж, всё начинается с того, что элементы сжимаются, если в контейнере недостаточно места, поэтому, даже если мы установили элементу ширину = 50%, места ему не хватает, поэтому он начинает сжиматься, чтобы втиснуться в контейнер, так как другой <div> требует больше места. 50% — это скорее идеальный размер для блока, а не размер фактический.»

«Таким образом, в примере выше содержимое первого блока настолько большое, что создаёт проблему, потому что, как flex-элемент, по умолчанию он стремится сжаться, чтобы вместить содержимое. В этом примере данный элемент имеет много содержимого, так что…

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

Grid сложен тем, что влечёт за собой много новых свойств и значений, но они дают нам гораздо больше контроля, чем flexbox.

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

Ограничения Flexbox


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

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



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



Желтый и оранжевый блоки в этой ситуации нужны, чтобы при добавлении свойства display: flex для элемента .card (красный блок), создались две колонки. Таким образом, чтобы структурировать всё содержимое, мы получаем разметку, которая выглядит примерно так:

<div class="card">  
    <div class="profile-sidebar">
        <!-- Изображение профиля и список иконок соцсетей --> 
    </div>
    <div class="profile-body">
        <!-- Имя, должность, описание -->
    </div>
</div>

Как только вы поймёте, как работает Flexbox, понять такую структуру станет просто.

Когда мы добавим свойство display: flex для элемента .card, получим две колонки, после чего нужно будет стилизовать каждую из них отдельно.

Вот рабочий пример со всеми стилями:


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

Упрощение всего с помощью CSS Grid


Поскольку CSS Grid двухмерный, он позволяет нам одновременно создавать строки и столбцы, а это значит, что grid-контейнер (которому мы задали свойство display: grid) имеет полный контроль над макетом внутри себя.

Раньше для этого требовались дополнительные элементы, как в приведенном выше примере с Flexbox. Используя Grid, мы можем полностью избавиться от них.

<div class="card"> <img src="https://i.pravatar.cc/125?image=3" alt="" class="profile-img">
  <ul class="social-list"> ... </ul>
  <h2 class="profile-name">Ramsey Harper</h2>
  <p class="profile-position">Graphic Designer</p>
  <p class="profile-info">Lorem ipsum ...</p>
</div>

С точки зрения разметки, разве не имеет ли это больше смысла?

Есть элемент с классом .card, в который мы помещаем его непосредственное содержимое. Нам не нужно переживать о структуре и компоновке элементов, мы просто помещаем контент, который нам нужен, и идём дальше.

Структурирование макета


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

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



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

Чтобы настроить сетку, мы можем сделать что-то вроде этого:

.card {  
    display: grid;
    grid-template-columns: 1fr 3fr;
}

Единица измерения fr уникальная для Grid, и обозначает долю доступного пространства. Такое использование очень похоже на установку двух колонок во Flexbox и определение им ширин 25% и 75% соответственно.

Размещение элементов на сетке


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

Можно было использовать grid-row и grid-column для каждого элемента, чтобы разместить их именно там, где мы хотим, но чем больше я использую Grid, тем больше я влюбляюсь в свойство grid-template-areas и размещаю элементы с помощью определения областей.

Такой подход занимает немного больше времени, но оно стоит того, особенно когда мы создаём адаптивный дизайн (скоро мы дойдём до этого).

Сперва мы должны определить grid-template-areas для элемента .card, после чего мы можем назначить все дочерние элементы в эти области:

.card {
  ...
  display: grid;
  grid-template-columns: 1fr 3fr;
  grid-column-gap: 2em;
  grid-template-areas:
      "image name"
      "image position"
      "social description";
}


.profile-name     { grid-area: name; }
.profile-position { grid-area: position; }
.profile-info     { grid-area: description; }
.profile-img      { grid-area: image; }
.social-list      { grid-area: social; }

В примере ниже можно увидеть это всё в действии:


Это так просто


Одна из причин, по которой я люблю использовать grid-template-areas, заключается в том, что если кто-то еще посмотрит на код, он сразу поймёт, что происходит…

Если кто-то показывает вам код, в котором заданы grid-row и grid-column с использованием чисел и span, высчитать и понять, где и как они будут расположены элементы, достаточно просто. Для простых макетов или для использования span в некоторых местах, их использование считаю уместным, но гораздо приятнее, просто посмотрев только на код родительского элемента, сразу понять, как будет выглядеть весь макет.

При использовании Grid легче узнать фактический размер элемента


В самом первом нашем примере, где мы установили ширину одного из flex-элементов = 50%, на самом деле она не была равна 50%. Если вы понимаете, почему так произошло, отлично, но порой это всё равно может раздражать. Это достаточно просто обойти, но когда используется Grid, это становится гораздо меньшей проблемой.

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

И конечно же, мы получаем в распоряжение функцию minmax() и единицы измерения fr, которые немного мутят воду, так как позволяют более гибко определять размеры (как те, что мы используем в примере выше), но даже тогда мы всё ещё имеем полный контроль над их гибкостью, и всё это контролируется родителем, без необходимости задавать часть свойств для родителя, а часть — для дочерних элементов.

Ограниченные изменения


Если рассмотреть пример с Flexbox, мы не можем сделать его похожим на макет ниже без изменения HTML-разметки:



Мы ограничили сами себя тем, как группировали элементы вместе с помощью <div>. Нам пришлось использовать эти <div>, чтобы заставить макет работать, но теперь это стало для нас преградой.

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

.card {
  /* Старая разметка 
  grid-template-areas:
      "image name"
      "image position"
      "social description"; */
   
  /* Новая разметка */
  grid-template-areas:
      "image name"
      "image position"
      "image social"
      ". description";
}

Играя с grid-template-areas подобным образом, быстро и легко можно сместить иконки социальных сетей туда, где мы хотим их видеть (точка в последней части указывает на пустую ячейку).

Это облегчает жизнь при работе с медиа-запросами


Как я упоминал несколько раз, это тот случай, когда оправдано использование grid-template-areas. Мы можем полностью контролировать наш макет только с помощью свойств родителя:

.card {
  /* настройки для небольших экранов */
  display: grid;
  grid-template-columns: 1fr 3fr;
  grid-column-gap: 2em;
  grid-template-areas: 
      "name" 
      "image" 
      "social" 
      "position" 
      "description";
}
.profile-name     {  grid-area: name;}
.profile-position {  grid-area: position; }
.profile-info     {  grid-area: description; }
.profile-img      {  grid-area: image; }
.social-list      {  grid-area: social; } 


/* Перестраивание макета для больших экранов */

@media (min-width: 600px) {
  .card {
    text-align: left;
    grid-template-columns: 1fr 3fr;
    grid-template-areas: 
        "image name" 
        "image position" 
        "social description";
  }
  .profile-name::after {
    margin-left: 0;
  }
}

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


Flexbox всё еще имеет свою область применения


Я всё чаще и чаще использую Grid, но думаю, что Flexbox всё еще имеет свою область применения. Если у меня есть логотип и навигация, расположенные рядом друг с другом, хорошо бы иметь простой способ сделать что-то подобное и знать, что они окажутся там, где я хочу чтобы они были:

.header {
  display: flex;
  justify-content: space-between;
}

То же касается и <ul>, который мы используем для навигации, чтобы просто получить элементы, следующие друг за другом, или, как вы могли заметить в примере с карточкой, который мы рассматривали, он идеально подходит для .social-list.

Для простых компонентов, где нам не нужен сложный макет, это действительно хорошо работает. Но всё больше и больше я замечаю за собой движение в сторону Grid, иногда из-за явной потребности в них, иногда потому что хочу использовать функцию minmax() или единицы измерения fr.

Но в конце концов, я думаю, что самое лучшее в Grid — это возможность существенно упростить нашу разметку.

Мы всё ещё вынуждены использовать <div>, но благодаря Grid, мы всё меньше можем их использовать.

В завершение


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

При использовании Flexbox, наибольшее, что мы можем изменить, это направление flex-direction. Используя Grid, мы можем быстро и просто полностью изменить дизайн компонента.

И Flexbox и Grid имеют гораздо больше возможностей. Каждый имеет своё предназначение, но если вы чувствуете, что не знаете, какой подход выбрать, или застряли при попытке понять адаптивный дизайн в общем, недавно на Scrimba я анонсировал курс, который углубляется в вопросы адаптивного дизайна, который называется The Responsive Web Design Bootcamp.

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 19

  • НЛО прилетело и опубликовало эту надпись здесь
      +2
      Мы, вероятно, сильно не правы, т.к. очень далеки от вёрстки.
      Но у нас есть смутное чувство, что история повторяется. Сначала были таблицы, все от них плевались. Потом стали дивы, и половина статей была на тему как на них реализовать то, что делалось на таблицах без проблем, но таблицы назывались греховными. Потом появились флексбоксы, в них содержалось много табличного по идеологии и вот — дошли до гридов. И гриды как-то по нашим ощущениям являются по сути более ¿улучшенной? версией таблиц.
        +2
        гриды как-то по нашим ощущениям являются по сути более ¿улучшенной? версией таблиц

        Если и так, разве это плохо?
        Кажется, эволюция и развитие — вполне ожидаемое и полезное явление.
        Наверное, не всегда нужно изобретать заново «колесо».
        Можно учесть недостатки существующих подходов и попытаться исправить их в новых решениях.
          +4
          У них разный смысл.

          Таблица обладает семантикой и предназначена для вывода табличных данных. Гриды и флексы не обладают семантикой и предназначены исключительно для раскладки.

          А если начать вдаваться в детали, то получится что гриды и таблицы похожи только на первый взгляд.
            –1
            Про разную семантику — это всё как бы верно, но если откровенно, то рядовому веб-разработчику на это плевать (и я его понимаю). Намного важнее то, что гриды позволяют очень гибко управлять размерами и поведением своих ячеек, а таблицы в этом смысле тупые и малопредсказуемые.
          • НЛО прилетело и опубликовало эту надпись здесь
              0
              Это собираются исправить во второй версии гридов с помощью сабгридов.

              Боюсь, там «флексбокс вс. грид» окончательно станет холиварной темой, особенно если по производительности будет плюс-минус однинаково.
              • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  Но в общем случае у них разные области применения. Сделать сетку на флексах можно сделать только с помощью лишних обёрток и, возможно, хаков в запущенных случаях.

                  Ну на самом деле это всё не так сложно, и эта задача вообще не сложнее создания чего-то типа модального окна в html (т.е. такой вещи, у которой нет адекватного built-in функционала, но который тривиально эмулируется кодом и некоторой дополнительной версткой).
                  То есть с одной стороны да, сетку на флексе сделать — нужны будут дополнительные блоки и дополнительный код, с другой стороны это будут очень даже очевидные дополнительные блоки и очевидный дополнительный код.
                0

                table-layout: fixed, кстати, отвратительно работает в ситуациях, когда первая строка таблицы имеет объединённые ячейки при ненулевой толщине границы.

                  0

                  Можете уточнить, в чем проблема при таких условиях?

                    0

                    Насколько я помню, там получается проблема с границами, которые то учитываются, то нет. А в colgroup нельзя использовать calc, из-за чего таблица вечно вылезает на пару (десятков) пикселей за пределы контейнера.

                +1

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

                • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  Это было бы по сути так, пока ты вдруг н понимаешь для чего все-таки существуе тgrid-template-areas.

                  Я при беглом осмотре grid-ов вообще не понял в чем преимущество перед таблицами, а когда понял что надписи имен профайлов в grid-template-areas это и есть блоки, и как ты их напишешь, просто напишешь их имена в тексте, так они и выстроятся на странице — это реально магия, вот только тогда я понял для чего сделали Grid.

                  Это реально cool, напиши 3 слова в строку — получи 3 столбца в итоге, напиши 2 слова в строке и 2 во второй строке — получи 4 ячейки 2x2.

                  При чём, размеры в fr, сами подгоняют соотношения в любой ширине (для меня это кстати было большим плюсом, ибо я уже 20 лет пишу неизменно размер столбцов для ListView в обычных языках именно в соотношениях относительно друг к дургу, а не в каких то других единицах).
                  0
                  Таким образом, в примере выше содержимое первого блока настолько большое, что создаёт проблему, потому что, как flex-элемент, по умолчанию он стремится сжаться, чтобы вместить содержимое

                  Иногда такое подходит:
                  .flex-container {
                    min-width: max-content;
                    <...>
                  }
                    –3
                    «Таким образом, в примере выше содержимое первого блока настолько большое, что создаёт проблему, потому что, как flex-элемент, по умолчанию он стремится сжаться, чтобы вместить содержимое. В этом примере данный элемент имеет много содержимого, так что…

                    «Я задал ширину одному элементу флекс-контейнера, и не задал другому».

                    У меня объяснение короче вышло. Если очень волнует вопрос, как элемент с шириной в 50% может оказаться меньше 50%, у меня есть второе короткое объяснение:

                    «flex-shrink по умолчанию выставлен в 1».

                    Эти сопли про неочевидность флексбокса хороши только для того, чтоб под ними написать статью про «а давайте не флексбокс». Но сами по себе они очень слабенькие. Точно так же можно накатать слезливую статью о том, что html плохой и неочевидный, потому что <body> имеет какие-то дефолтные margin, хотя никто об этом его не просил.
                      0

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


                      Картинка


                      Это может оказаться нежелательным выравниванием. Например, если имя окажется слишком длинным и не влезет в одну строку — то либо фотография "уедет" вниз, либо кнопки оторвутся от неё.


                      Так что без выделения двух колонок тут не обойтись.

                        +1

                        Я сейчас заканчиваю сайт, полностью на grid, сделал его. Это просто кайф.

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

                        Самое читаемое