Новый стандарт разметки для веб: сетка, Flexbox и Box-выравнивание

Верстка без CSS позволяла достигнуть неплохих результатов. Но подобные методы перестали работать в эпоху адаптивного дизайна.

К счастью, у нас есть Flexbox, Box Alignment Module и CSS Grid Layout.

Новые значения свойства Display

И grid, и flexbox – это новые значения свойства display. Чтобы сделать элемент flex-контейнером, мы используем display: flex, чтобы сделать его сеточным контейнером, мы используем display: grid.

Как только мы это сделаем, потомки нашего flex- или grid-контейнера станут элементами flex или grid по умолчанию.

display: flex

В первом примере у нас есть три элемента в контейнере, которому задано значение display: flex. Это всё, что нам нужно сделать, чтобы начать использовать flexbox.

По умолчанию для контейнера значения свойств будут равны:

  • flex-direction: row;
  • flex-wrap: no-wrap;
  • align-items: stretch;
  • justify-content: flex-start.

Изначальные значения для flex элементов:

  • flex-grow: 0
  • flex-shrink: 1
  • flex-basis: auto

Мы рассмотрим эти свойства и их значения позже. А пока всё, что вам нужно сделать, это указать display: flex для родительского элемента, и flexbox заработает.

display: grid

Чтобы расположить элементы в сетке, мы используем display: grid. Чтобы увидеть поведение сетки, рассмотрим пример с пятью вкладками.

Добавление display: grid не приведет к кардинальным изменениям. Но дочерние элементы теперь станут частью сетки. Они включены в одну колонку сетки и отображаются один под другим. Сетка создаёт неявные ячейки для хранения каждого элемента.

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

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

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

И снова видим в действии поведение по умолчанию. Мы не позиционировали ни один из элементов сетки, но они расположились по одному на ячейку. Значения по умолчанию для сеточного контейнера следующие:

  • grid-auto-flow: row
  • grid-auto-rows: auto
  • align-items: stretch
  • justify-items: stretch
  • grid-gap: 0

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

Box Alignment Module

В обоих примерах мы уже видим в действии значения, определённые в Box Alignment Module. Этот модуль берёт на себя всё выравнивание и распределение пространства в flexbox и делает его доступным для других модулей. Поэтому, если применяете flexbox, то вы уже используете и Box Alignment Module.

Посмотрим, как работает этот модуль в flexbox и сетке, а также на проблемы, которые он помогает решить.

Колонки одинаковой высоты

Колонки одинаковой высоты легко создать с помощью табличной вёрстки, но трудно, используя позиционирование и плавающие элементы. В примере с плавающими элементами, приведенном ниже, блоки содержат разный объём контента. У нас нет возможности «сообщить» другим блокам, что они должны иметь ту же высоту, что и первый блок, поскольку они никак не связаны друг с другом.

Если задать родительскому элементу display: grid или flex, то возникает зависимость дочерних элементов друг от друга. Она позволяет свойствам Box Alignment создавать колонки одинаковой высоты.

В примере, приведенном ниже, элементы содержат различные объемы контента. Фон элементов выровнен, но контент его скрывает, как это было с плавающими элементами. Поскольку эти элементы выстроены в строку, свойство, контролирующее это поведение – align-items. Создание колонок одинаковой высоты требует, чтобы значением свойства было stretch .

То же самое мы увидим и для сетки. В примере, приведенном ниже, продемонстрировано простейшее сеточное расположение: колонка контента и боковая колонка. Я снова использую долевые значения: боковая колонка имеет 1 долю доступного пространства, а основной контент – 3 доли. Цвет фона боковой колонки распространяется до низа контента. Значение по умолчанию свойства align-items: stretch.

Выравнивание в Flexbox

В случае с flexbox, когда используем свойство align-items, мы выравниваем элементы внутри flex-контейнера по поперечной оси. Главная ось определяется свойством flex-direction. В первом примере главная ось – строка. Затем мы выравниваем элементы по поперечной оси – по высоте flex-контейнера. Высота flex-контейнера определяется высотой элемента с наибольшим объёмом контента.

Выравнивание в Flexbox

Также можно задать высоту контейнеру.

Выравнивание в Flexbox - 2

Вместо значения по умолчанию stretch можно использовать и другие:

  • flex-start;
  • flex-end;
  • baseline;
  • stretch.

Чтобы контролировать выравнивание по главной оси, используйте свойство justify-content. Значение по умолчанию – flex-start, поэтому наши элементы выровнены по левому краю. Можно использовать любое из следующих значений:

  • flex-start;
  • flex-end;
  • center;
  • space-around;
  • space-between.

Особенно интересны space-between и space-around. С помощью space-between свободное пространство распределяется поровну между элементами.

Вместо значения по умолчанию stretch можно использовать и другие:

Значение space-around распределяет свободное пространство по обоим краям элементов.

Вместо значения по умолчанию stretch можно использовать и другие: - 2

Мы можем расположить flex-элементы в столбик, а не в строку. Если изменим значение свойства flex-direction на column, тогда главной осью станет колонка, а поперечной осью – строка. Свойство align-items по умолчанию будет иметь значение stretch, и растягивать элементы по ширине строки.

Если нужно выровнять элементы по началу flex-контейнера, используйте flex-start.

Также можно использовать justify-items, включая space-between и space-around. Но тогда контейнер должен иметь достаточную высоту, чтобы вы увидели их действие.

Выравнивание в сеточной разметке

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

Мы можем выровнять контент внутри области сетки, используя свойства и значения, описанные в спецификации CSS Grid Layout.

Область сетки – это одна или несколько ячеек сетки. В примере, ссылка на который приведена ниже, есть сетка с четырьмя столбцами и строками.

Строки разделены отступом в 10 пикселей. Я создала три области сетки, используя строковое позиционирование. Значение перед / – это строка, где начинается контент, а значение после, где он заканчивается.

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

Выравнивание в сеточной разметке

Во втором примере я изменила значение свойства align-items для контейнера сетки на center. Также можно изменить это значение для каждого элемента сетки, используя свойство align-self. В данном случае я задала всем элементам значение center, а второму элементу - stretch.

Выравнивание в сеточной разметке - 2

В третьем примере я снова изменила значения свойств justify-items и justify-self, установив их в center и stretch.

Во всех примерах, приведенных выше, я выравнивала контент областей, обозначенных началом и концом линий сетки.

Также можно выравнивать всю сетку внутри контейнера, если области сетки имеют меньший размер, чем контейнер, которому было задано display: grid. В этом случае мы используем свойства align-content и justify-content так же, как в flexbox.

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

Чтобы переместить элементы в правый нижний угол, измените значения на end.

Так же, как и в случае с flexbox, можно использовать свойства space-around и space-between. Это может привести к нежелательному поведению, поскольку расстояние между элементами становится больше. Но и в третьем примере на CodePen мы получаем то же пространство между и вокруг элементов, что и при использовании flexbox.

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

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

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

Адаптивность по умолчанию
Адаптивная вёрстка часто заключается в соблюдении пропорций. Сеточная и flexbox разметки предоставляют более простые способы расчёта пропорций в веб-дизайне.
Flexbox обеспечивает контентно-ориентированный подход к гибкости. Это проявляется при использовании значения space-between, чтобы равномерно распределить элементы. Сначала рассчитывается количество свободного пространства, занятого элементами. Затем оставшееся пространство в контейнере делится и используется поровну для образования зазоров между элементами. Мы можем получить больший контроль над распределением контента, используя свойства, применяемые к самим flex-элементам:

  • flex-grow;
  • flex-shrink;
  • flex-basis.

Эти свойства чаще используются в сокращённом виде в свойстве flex. Если добавим flex: 1 1 300px к элементу, это значит:

  • flex-grow должно иметь значение 1, поэтому элементы могут увеличиваться;
  • flex-shrink должно быть 1, чтобы элементы могли уменьшаться;
  • flex-basis равно 300 пикселей.

Применив это к нашим карточкам, увидим разметку, продемонстрированную в следующем примере.

Свойство flex-basis равно 300 пикселей, и у нас три карточки в строке. Если flex-контейнер шире 900 пикселей, оставшееся место делится и распределяется поровну между элементами. Это потому, что мы задали свойству flex-grow значение 1, чтобы элементы могли увеличиваться в зависимости от значения flex-basis.

Если нужно, чтобы элементы увеличивались в разных пропорциях, тогда можно изменить значение flex-grow для одного или нескольких элементов. Чтобы первый элемент получил в три раза больше пространства, установите для него flex-grow: 3.

Доступное распределяется после того, как необходимая величина, заданная в flex-basis, будет учтена. Таким образом, первый элемент не в три раза больше, чем остальные, но получает в 3раза большую долю оставшегося пространства. Вы увидите изменения, установив значение свойства flex-basis в 0. Тогда вся ширина контейнера будет распределена в пропорциях элементов.

Инструмент, который поможет понять эти значения, Flexbox Tester. Вставляйте различные значения в тестер, и он рассчитает актуальные размеры, которые получат элементы. А также объяснит, почему они окажутся именно такого размера.

Если задать auto для flex-basis, браузер использует любой размер, установленный в flex-элементе, в качестве значения для flex-basis. Если ни одного размера не установлено, то по умолчанию он будет таким же, как значение content, то есть шириной контента.

Значения auto полезно для повторно используемых компонентов, которым может понадобиться установка размера отдельных элементов.

В следующем примере я установила свойство flex-basis для всех карточек в значение auto. Затем я задала первой карточке ширину в 350 пикселей. Поэтому свойство flex-basis первой карточки теперь будет равно 350 пикселей. Это значение используется для дальнейшего распределения пространства. Оставшиеся две карточки имеют значение flex-basis, основанное на ширине размещенного в них контента.

Вернёмся к изначальным параметрам flex: 1 1 300px, добавим ещё элементы в пример и установим flex-wrap: wrap для контейнера. Тогда элементы будут переноситься, чтобы поддерживать значение flex-basis наиболее близким к заданному.

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

Поддерживаем пропорции с помощью сеточной разметки

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

Ранее мы уже сталкивались с единицей измерения fr сетки. Она работает аналогично flex-grow, когда значение свойства flex-basis установлено в 0. Она присваивает элементу долю доступного пространства в контейнере сетки.

В приведенном ниже примере первой колонке задана доля в 2fr, остальным двум – 1fr. Поэтому пространство делится на четыре части: первой колонке достается две и по одной части каждой из оставшихся двух колонок.

Смешение абсолютных единиц и долей приемлемо. В следующем примере есть колонка в 2fr, колонка в 1fr и колонка в 300 пикселей. Сначала убирается колонка с абсолютной шириной, а затем оставшееся место делится на три части, из которых две части присваиваются первой колонке и одна часть – второй.

Элементы находятся внутри определённых строк и столбцов: они не распределяются по ширине строки, как в примере с flexbox. Это происходит потому, что в CSS Grid Layout мы создаём двумерную разметку, а затем размещаем в ней элементы.

В flexbox мы рассчитываем, сколько контента войдет в одномерную строку или столбец. При этом дополнительные строки или столбцы рассматриваются как новые flex-контейнеры.

Чтобы создать столько колонок определённого размера, сколько войдёт в контейнер, используя grid и синтаксис repeat.

Также можно соединить долевые единицы измерения и абсолютную ширину, чтобы создать сетку с таким количеством 200-пиксельных ячеек, которое войдёт в контейнер, и распределить оставшееся пространство поровну.

В этом случае мы получим преимущества двухмерной разметки, но всё равно будем иметь гибкие размеры ячеек. И всё это без использования медиа-запросов. В этом примере мы также можем видеть расхождения в спецификациях CSS Grid Layout и flexbox разметки. Если flexbox заканчивает работу, распределяя элементы в одном измерении, сетка с этого только начинает работу.

Разделение порядка в исходном коде и визуальном представлении

В flexbox мы не можем изменить позиционирования flex-элементов. Можно выбрать направление их размещения, установив flex-direction в row, row-reverse или column, column-reverse. А также установить порядок, который контролирует очерёдность визуального представления элементов.

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

grid-column и grid-row – это сокращенные свойства для набора grid-column-start, grid-row-start, grid-column-end и grid-row-end. Значение до / – это строка, в которой контент начинается, а значение после – строка, на которой он заканчивается.

Также можно задать имена вашим строкам. Это происходит при создании сетки в контейнере. Задайте названия в квадратных скобках, а затем размещайте элементы, используя имена вместо индексов.

Можно иметь несколько строк с одинаковыми именами, а затем обращаться к ним по имени и индексу.

Вы можете использовать ключевое слово span, объединяя несколько строк или, к примеру, все строки до третей, названной col. Этот тип размещения полезен для создания компонентов, которые располагаются в различных местах разметки.

В примере, приведенном ниже, я хочу, чтобы некоторые элементы заняли шесть объединённых колонок, а остальные заняли по три. Я использую автоматическое размещение, чтобы расположить элементы, но, когда сетка встретит элемент с классом wide, значением начала контента будет auto, а значением завершения контента - span 6. Поэтому контент начнётся, следуя правилам автоматического размещения, но при этом займет две объединённые колонки.

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

По умолчанию сетка продолжит заполнение дальше, поэтому однажды оставив пробел, она не возвращается назад, чтобы его заполнить. Но если задать grid-auto-flow значение dense, сетка будет возвращаться и «дозаполнять» пробелы, нарушая порядок элементов в дереве DOM.

Также существует способ размещения элементов при помощи свойства grid-template-areas. Чтобы сделать это, для начала нужно задать имя каждому потомку контейнера сетки.

Затем мы располагаем элементы в значении свойства grid-template-areas. Если хотите полностью изменить контент, основываясь на медиа-запросах, отредактируйте значение только одного свойства.

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

Проблемы доступности контента при изменении порядка

Используя разметку flexbox или CSS Grid Layout, нужно быть осторожными при таком изменении порядка элементов. Спецификация flexbox гласит:
Разработчики обязаны использовать порядок только для изменения визуального, а не логического порядка следования контента. Таблицы стилей, использующие порядок для логической переориентации контента, не соответствуют спецификации.

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

Сеточная разметка предоставляет разработчикам большие возможности по размещению элементов. Но это не отменяют корректного порядка следования элементов. Свойства порядка и расположения элементов не влияют на порядок в не визуальных устройствах (таких как синтезаторы речи). Также переориентация элементов сетки визуально не затрагивает изначальный порядок последовательной навигации.

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

Новая система сеточной разметки CSS

Я не осветила все аспекты сеточной и flexbox разметки в этом обзоре. Моей целью было показать сходства и различия в их спецификациях и вбросить туда Box Alignment Module.

Сергей Бензенкоавтор-переводчик статьи «The New Layout Standard For The Web CSS Grid, Flexbox And Box Alignment»