Самый понятный учебник современной CSS-верстки
УРОК Nº 8.
Самый понятный учебник современной CSS-верстки
Режим макета Flexbox ["Гибкий блок"] предлагает альтернативу плавающим элементам Floats для создания общего вида веб-страницы. В то время как плавающие элементы позволяют нам только горизонтально располагать наши блоки, flexbox дает нам полный контроль над выравниванием, направлением, порядком и размером наших блоков.
В настоящее время веб переживает серьезные изменения, поэтому стоит немного поговорить о состоянии отрасли. В последнее десятилетие плавающие элементы Floats были единственным вариантом для создания сложной веб-страницы. В результате они хорошо поддерживаются даже в устаревших браузерах, а разработчики используют их для создания миллионов веб-страниц. Это означает, что вы неизбежно столкнетесь с плавающими элементами во время своей карьеры веб-разработчика (так что предыдущий урок не был пустой тратой времени).
Изначально плавающие элементы предназначались для макетов в журнальном стиле (см. статью Floats для контента). Несмотря на все изученное на прошлом уроке, виды макетов, создаваемых с помощью плавающих элементов, несколько ограничены. Даже простой макет боковой панели, с технической точки зрения, является немного хаком. Чтобы избавиться от этих ограничений, был изобретен Flexbox.
Наконец-то мы достигли того момента, когда поддержка браузерами достигла критической массы и разработчики могут начать создавать полноценные сайты с использованием flexbox. Мы рекомендуем использовать flexbox для компоновки веб-страниц как можно чаще, оставляя плавающие элементы для тех случаев, когда вам нужно, чтобы поток страницы обтекал блок (например, при верстке в журнальном стиле), или для поддержки устаревших веб-браузеров.
На этом уроке мы шаг за шагом рассмотрим всю модель макета flexbox. Вы научитесь создавать практически любой макет, который предложит веб-дизайнер.
Пример для этого урока относительно прост, но он наглядно демонстрирует все важные свойства flexbox. В итоге мы получим что-то похожее на это:
Для начала нам нужен пустой HTML-документ, в котором нет ничего, кроме строки меню. Создайте новый Atom-проект под названием flexbox, в котором будут храниться все файлы примеров для этого урока. Затем создайте файл flexbox.html и добавьте в него следующую разметку:
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'/>
<title>Some Web Page</title>
<link rel='stylesheet' href='styles.css'/>
</head>
<body>
<div class='menu-container'>
<div class='menu'>
<div class='date'>Aug 14, 2016</div>
<div class='signup'>Sign Up</div>
<div class='login'>Login</div>
</div>
</div>
</body>
</html>
Далее нужно создать таблицу стилей styles.css. Выглядеть это будет не очень презентабельно: просто синяя полоса меню во всю ширину с белым блоком в ней. Обратите внимание, что для центрирования меню мы будем использовать flexbox вместо уже привычной техники auto-margin.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.menu-container {
color: #fff;
background-color: #5995DA;
/* синий */
padding: 20px 0;
}
.menu {
border: 1px solid #fff;
/* для отладки */
width: 900px;
}
Напоследок скачайте несколько изображений для нашего примера веб-страницы. Распакуйте их в проект flexbox, сохранив родительский каталог images. Ваш проект должен выглядеть примерно так:
Flexbox использует два типа блоков, которые мы никогда не видели раньше: "flex контейнеры" [flex containers] и "flex элементы" [flex items]. Задача flex контейнера - сгруппировать несколько flex элементов и определить, как они будут располагаться.
Каждый HTML-элемент, являющийся прямым дочерним элементом flex контейнера, представляет собой "элемент" [flex item]. Flex элементами можно манипулировать по отдельности, но в основном их расположение определяет контейнер. Основная задача flex элементов - сообщить контейнеру, сколько элементов ему нужно расположить.
Как и в случае с макетами на основе плавающих элементов [floats], создание сложных веб-страниц с помощью flexbox сводится к созданию вложенных блоков. Вы выравниваете кучу flex элементов внутри контейнера, и, в свою очередь, эти элементы могут служить flex контейнерами для своих собственных элементов. Работая с примерами этого урока, помните, что основная задача компоновки страницы не изменилась: мы по-прежнему просто перемещаем группу вложенных блоков.
Первый шаг в использовании flexbox - превращение одного из наших HTML-элементов в flex-контейнер. Для этого мы используем свойство display, которое должно быть знакомо нам по уроку Блочная модель в CSS. Значение flex сообщает браузеру, что все в блоке должно отображаться с помощью flexbox, а не блоковой модели, как это делается по умолчанию.
Добавьте следующую строку в наше правило .menu-container, чтобы превратить его в flex контейнер:
.menu-container {
/* ... */
display: flex;
}
Это активирует режим верстки flexbox - без него браузер будет игнорировать все свойства flexbox, которые мы сейчас рассмотрим. Явное определение flex-контейнеров означает, что вы можете смешивать и сочетать flexbox с другими моделями верстки (например, с плавающими элементами и всем тем, что мы будем изучать в разделе Продвинутое позиционирование).
Ура! У нас есть flex контейнер с одним flex элементом в нем. Однако наша страница будет выглядеть точно так же, как и раньше, потому что мы не указали контейнеру, как отображать его элемент.
После того как вы создали flex-контейнер, нужно задать горизонтальное выравнивание его элементов. Для этого предназначено свойство justify-content. Мы можем использовать его, чтобы выровнять по центру наше .menu, как показано ниже:
.menu-container {
/* ... */
display: flex;
justify-content: center;
/* добавьте это */
}
Это имеет тот же эффект, что и добавление объявления margin: 0 auto к элементу .menu. Но обратите внимание, что мы добавили свойство к родительскому элементу (flex-контейнеру), а не непосредственно к элементу, который мы хотим выровнять по центру (flex-элементу). Управление элементами через их контейнеры является общей темой в flexbox, и это немного отличается от того, как мы позиционировали блоки до сих пор.
Другие значения для justify-content показаны ниже:
Попробуйте изменить justify-content на flex-start и flex-end. Это позволит выровнять меню по левой и правой стороне окна браузера соответственно. Не забудьте изменить его обратно на center, прежде чем двигаться дальше. Последние две опции полезны только когда в контейнере находятся несколько flex-элементов.
"Подумаешь! Мы уже умеем выравнивать влево/вправо с помощью плавающих элементов и центрировать с помощью auto-margins" - скажете вы. Верно. Но Flexbox не проявляет себя по-настоящему, пока в контейнере всего один элемент. Свойство justify-content также позволяет равномерно распределить элементы внутри контейнера.
Измените наше правило .menu следующим образом:
.menu {
border: 1px solid #fff;
width: 900px;
display: flex;
justify-content: space-around;
}
Это превращает наше .menu во вложенный flex контейнер, а значение space-around распределяет его элементы по всей ширине. Вы должны увидеть что-то вроде этого:
Flex-контейнер автоматически распределяет дополнительное горизонтальное пространство по обе стороны от каждого элемента. Значение space-between аналогично, но оно только добавляет дополнительное пространство между элементами. Это именно то, что требуется для нашей страницы. Поэтому обновите строку justify-content:
justify-content: space-between;
Конечно, вы также можете использовать здесь center, flex-start, flex-end, если хотите сдвинуть все элементы в ту или иную сторону. Но давайте оставим значение space-between.
Flex контейнеры умеют позиционировать только элементы, находящиеся уровнем ниже (то есть свои дочерние элементы). Их нисколько не волнует, что находится внутри тех flex элементов. Это означает, что группировка flex элементов - еще одно оружие в вашем арсенале для создания HTML-макета. Если обернуть множество элементов в дополнительный <div>, получится совершенно другая веб-страница.
Предположим, вы хотите, поместить ссылки Sign Up и Login в правую часть страницы (см. скриншот ниже). Для этого достаточно поместить их в другой <div>:
<div class='menu'>
<div class='date'>Aug 14, 2016</div>
<div class='links'>
<div class='signup'>Sign Up</div>
<!-- это вложено -->
<div class='login'>Login</div>
<!-- и это вложено -->
</div>
</div>
Вместо трех элементов в нашем flex-контейнере .menu теперь только два (.date и .links). В соответствии с существующим поведением пространства между ними, они будут привязаны к левой и правой стороне страницы.
Но теперь нам нужно разместить элемент .links, поскольку он использует стандартный режим блокового размещения. Решение: больше вложенных flex-контейнеров! Добавьте новое правило в наш файл styles.css, которое превратит элемент .links в flex-контейнер:
.links {
border: 1px solid #fff;
/* для отладки */
display: flex;
justify-content: flex-end;
}
.login {
margin-left: 20px;
}
Это позволит разместить наши ссылки именно там, где мы хотим. Обратите внимание, что поля по-прежнему работают так же, как и в блоковой модели CSS. Как и в обычной блоковой модели, в flexbox особое значение имеют auto отступы (подробнее об этом в конце урока).
Белые рамки нам больше не понадобятся. Можно их смело удалить.
До сих пор мы манипулировали горизонтальным выравниванием, но flex-контейнеры могут также определять вертикальное выравнивание своих элементов. Это то, что просто невозможно сделать с плавающими элементами.
Чтобы изучить это, нам нужно добавить заголовок под нашим меню. Добавьте следующую разметку в файл flexbox.html после элемента .menu-container:
<div class='header-container'>
<div class='header'>
<div class='subscribe'>Subscribe ▾</div>
<div class='logo'><img src='images/awesome-logo.svg'/></div>
<div class='social'><img src='images/social-icons.svg'/></div>
</div>
</div>
Затем добавьте несколько базовых стилей, чтобы выровнять его по элементу .menu:
.header-container {
color: #5995DA;
background-color: #D6E9FE;
display: flex;
justify-content: center;
}
.header {
width: 900px;
height: 300px;
display: flex;
justify-content: space-between;
}
Все это должно быть знакомо, однако сценарий немного отличается от нашего меню. Так как .header имеет явную высоту, элементы могут быть расположены вертикально внутри него. В официальной спецификации это называется "выравнивание по оси" [cross-axis] - вскоре узнаете, почему. Но для наших целей это можно назвать "вертикальным" выравниванием.
Вертикальное выравнивание определяется добавлением свойства align-items к flex-контейнеру. Чтобы наша страница-пример соответствовала приведенному выше скриншоту, добавьте следующую строку:
.header {
/* ... */
align-items: center;
/* добавьте это */
}
Варианты свойства align-items аналогичны justify-content:
Большинство из вышеназванных вариантов довольно просты. С вариантом stretch стоит немного поэкспериментировать, потому что stretch позволяет отобразить фон каждого элемента. Давайте рассмотрим это ближе, добавив в файл styles.css следующее:
.header {
/* ... */
align-items: stretch; /* Измените это */
}
.social,
.logo,
.subscribe {
border: 1px solid #5995DA;
}
Блок для каждого элемента расширяется на всю высоту flex-контейнера, независимо от того, сколько контента он содержит. Чаще всего такое поведение используется для создания колонок одинаковой высоты с переменным количеством контента в каждой из них - что очень сложно сделать с плавающими элементами.
Перед тем как двигаться дальше, обязательно удалите вышеуказанные изменения и вертикально отцентрируйте наш контент внутри .header.
Flexbox - это более мощная альтернатива grid-сеткам на основе плавающих элементов. Он может не только отображать элементы в виде сетки, но и изменять их выравнивание, направление, порядок и размер. Для создания сетки нам понадобится свойство flex-wrap.
Добавьте ряд фотографий в файл flexbox.html, чтобы нам было с чем работать. Поместите их внутрь <body>, под элементом .header-container:
<div class='photo-grid-container'>
<div class='photo-grid'>
<div class='photo-grid-item first-item'>
<img src='images/one.svg'/>
</div>
<div class='photo-grid-item'>
<img src='images/two.svg'/>
</div>
<div class='photo-grid-item'>
<img src='images/three.svg'/>
</div>
</div>
</div>
Опять же, соответствующие CSS должны быть вам знакомы по предыдущим секциям:
.photo-grid-container {
display: flex;
justify-content: center;
}
.photo-grid {
width: 900px;
display: flex;
justify-content: flex-start;
}
.photo-grid-item {
border: 1px solid #fff;
width: 300px;
height: 300px;
}
Все должно работать, как и ожидалось, но посмотрите, что произойдет, если мы добавим больше элементов, чем поместится в flex-контейнер. Вставьте две дополнительные фотографии в .photo-grid:
<div class='photo-grid-item'>
<img src='images/four.svg'/>
</div>
<div class='photo-grid-item last-item'>
<img src='images/five.svg'/>
</div>
По умолчанию они вытекают за край страницы:
Такое вытекание за край еще можно оправдать, если вы пытаетесь создать баннер с горизонтальной прокруткой кучи фотографий, но это совсем, совсем не то, чего мы хотим. Мы хотим, чтобы не помещающиеся элементы смещались в следующий ряд. Для этого воспользуемся свойством flex-wrap:
.photo-grid {
/* ... */
flex-wrap: wrap;
}
Теперь наши flex-элементы ведут себя так же, как плавающие элементы, только flexbox дает нам больше контроля над тем, как "лишние" элементы выравниваются в последней строке с помощью свойства justify-content. Например, последняя строка сейчас выровнена по левому краю. Попробуйте выровнять ее по центру, обновив наше правило .photo-grid, например, так:
.photo-grid {
width: 900px;
display: flex;
justify-content: center;
/* Измените это */
flex-wrap: wrap;
}
Добиться этого с макетами на основе плавающих элементов невероятно сложно.
"Направление" означает, в каком направлении контейнер отображает свои элементы - горизонтально или вертикально. До сих пор все контейнеры, которые мы видели, использовали горизонтальное направление по умолчанию, что означает, что элементы рисуются один за другим в одной строке, а затем переходят в следующий столбец, когда заканчивается место.
Одна из самых удивительных вещей в flexbox - его способность преобразовывать ряды в столбцы с помощью всего одной строки CSS. Попробуйте добавить следующее объявление flex-direction в наше правило .photo-grid:
.photo-grid {
/* ... */
flex-direction: column;
}
Это изменит направление контейнера по сравнению со стандартным значением ряда [row value]. Вместо сетки наша страница теперь имеет одну вертикальную колонку:
Ключевым условием адаптивного дизайна является одна HTML-разметка как для мобильных, так и ноутбуков/ПК. Небольшая проблема состоит в том, что большинство мобильных макетов имеют одну колонку, в то время как большинство макетов для ноутбуков/ПК располагают элементы горизонтально. Только представьте, насколько полезным станет flex-direction, когда мы начнем создавать адаптивные макеты.
Обратите внимание, что колонка прижимается к левой стороне своего flex-контейнера, несмотря на наше объявление justify-content: center;. Когда вы меняете направление контейнера, вы также меняете направление свойства justify-content. Теперь оно относится к вертикальному выравниванию контейнера, а не к горизонтальному.
Чтобы горизонтально выровнять колонку, нам нужно задать свойство align-items для нашей .photo-grid:
.photo-grid {
/* ... */
flex-direction: column;
align-items: center;
/* Добавьте это */
}
До сих пор существовала тесная взаимосвязь между порядком расположения HTML-элементов и тем, как блоки отображаются на веб-странице. С помощью плавающих элементов или техники flexbox, которую мы сейчас изучаем, единственный способ заставить блок отображаться перед или после другого блока - это перемещение по HTML-разметке. Однако скоро все изменится.
Свойство flex-direction тоже позволяет контролировать порядок отображения элементов с помощью свойств row-reverse и column-reverse. Чтобы увидеть это в действии, преобразуем наш столбец обратно в сетку, но на этот раз мы изменим порядок отображения:
.photo-grid {
width: 900px;
display: flex;
justify-content: center;
flex-wrap: wrap;
flex-direction: row-reverse;
/* <--- Реально офигенно круто! */
align-items: center;
}
Теперь оба ряда отображаются справа налево, а не слева направо. Но обратите внимание, что порядок меняется только для каждой строки: первый ряд начинается не с 5, а с 3. Это полезное поведение для многих распространенных паттернов дизайна (в частности, обратный порядок колонок открывает много возможностей для мобильных макетов). В следующем разделе вы научитесь еще большей детализации.
Переупорядочивание элементов внутри таблицы стилей - это серьезное дело. До появления flexbox для этого приходилось прибегать к хакам JavaScript. Однако не стоит злоупотреблять своими новыми возможностями. Как уже говорилось в начале учебника, всегда следует отделять структуру от презентации. Изменение порядка является чисто визуальным - ваш HTML должен быть понятен и без применения этих стилей.
Весь этот урок до сего момента был посвящен позиционированию flex элементов через их родительские контейнеры. Однако также существует возможность управлять отдельными элементами. В оставшейся части урока мы сместим фокус с flex контейнеров на элементы, которые в них содержатся.
Добавление свойства order к flex элементу определяет его порядок в контейнере, не затрагивая окружающие элементы. По умолчанию его значение равно 0, а увеличение или уменьшение этого значения перемещает элемент вправо или влево соответственно.
Это можно использовать, например, чтобы поменять местами порядок элементов .first-item и .last-item в нашей сетке. Мы также должны изменить значение row-reverse из предыдущего раздела обратно на row, потому что так будет немного легче распознать наши исправления:
.photo-grid {
/* ... */
flex-direction: row;
/* Обновите это */
align-items: center;
}
.first-item {
order: 1;
}
.last-item {
order: -1;
}
В отличие от настроек row-reverse и column-reverse в flex контейнере, порядок действует вне границ рядов/колонок. Приведенный выше фрагмент поменяет местами первый и последний элементы, даже если они находятся в разных рядах.
То же самое мы можем сделать с вертикальным выравниванием. Что если мы хотим, чтобы ссылка "Подписаться" [Subscribe] и иконки соцсетей находились внизу заголовка, а не в центре? Выровняйте их по отдельности! Здесь на помощь приходит свойство align-self. Добавление этого свойства к элементу flex отменяет значение align-items из его контейнера:
.social
.subscribe {
align-self: flex-end;
margin-bottom: 20px;
}
Это должно отправить их в нижнюю часть .header. Обратите внимание, что и margin и padding действуют так, как от них ожидается.
Элементы можно выравнивать и другими способами, используя те же значения, что и свойство align-items, перечисленные ниже для удобства.
Все наши примеры были связаны с элементами с фиксированной шириной или определяемой контентом шириной. Это позволило нам сосредоточиться на аспектах позиционирования flexbox, но это также означает, что мы игнорировали природу "гибкого блока". Flex элементы являются гибкими: они могут сжиматься и растягиваться в соответствии с шириной своих контейнеров.
Свойство flex определяет ширину отдельных элементов в flex контейнере. Точнее, оно позволяет им иметь гибкую ширину. Оно работает как некий коэффициент веса [weight], указывающий flex контейнеру, как распределить дополнительное пространство между элементами. Например, элемент со значением flex, равным 2, будет увеличиваться в два раза быстрее, чем элементы с стандартным значением, равным 1.
Для дальнейших экспериментов нам нужен футер. Вставьте его после элемента .photo-grid-container:
<div class='footer'>
<div class='footer-item footer-one'></div>
<div class='footer-item footer-two'></div>
<div class='footer-item footer-three'></div>
</div>
Затем, немного CSS:
.footer {
display: flex;
justify-content: space-between;
}
.footer-item {
border: 1px solid #fff;
background-color: #D6E9FE;
height: 200px;
flex: 1;
}
Этот flex: 1; велит элементам растягиваться в соответствии с шириной .footer. Поскольку все они имеют одинаковый вес [weight], они будут растягиваться одинаково:
Если увеличить вес одного из элементов, он будет расти быстрее остальных. Например, мы можем заставить третий элемент расти в два раза быстрее, чем два других, с помощью следующего правила:
.footer-three {
flex: 2;
}
Сравните это со свойством justify-content, которое распределяет дополнительное пространство между элементами. Это выглядит похоже, но теперь мы распределяем это пространство в самих элементах. В результате мы полностью контролируем размещение гибких элементов в своих контейнерах.
Мы даже можем сочетать гибкие блоки с блоками фиксированной ширины. В flex: initial происходит возврат к конкретному свойству ширины элемента. Это позволяет нам по-разному комбинировать статические и гибкие блоки.
Мы хотим, чтобы наш футер вел себя так, как на скриншоте выше. Гибкий блок в центре, а те, что по обе стороны, фиксированной ширины. Все, что для этого нужно сделать - добавить следующее правило в нашу таблицу стилей:
.footer-one
.footer-three {
background-color: #5995DA;
flex: initial;
width: 300px;
}
Без этого flex: initial; объявление flex: 1; было бы унаследовано от правила .footer-item, в результате чего свойства ширины игнорировались бы. initial исправляет это, и мы получаем гибкий макет, который также содержит элементы фиксированной ширины. Измененяя размер окна браузера, мы увидим, что в футере изменяется только размер среднего блока.
Это довольно распространенная компоновка, и не только в футерах. Например, на многих сайтах есть боковая панель фиксированной ширины (или несколько боковых панелей) и гибкий блок контента, содержащий основной текст страницы. По сути, это более насыщенная версия футера, который мы только что создали.
Auto отступы [auto-margins] в flexbox - это нечто особенное. Они могут использоваться как альтернатива дополнительному <div> при попытке выровнять группу элементов по левому/правому краю контейнера. Воспринимайте auto-margin как "разделитель" для flex элементов в одном контейнере.
Давайте посмотрим, как выровнять наши элементы в .menu, чтобы получилось следующее:
<div class='menu-container'>
<div class='menu'>
<div class='date'>Aug 14, 2016</div>
<div class='signup'>Sign Up</div>
<div class='login'>Login</div>
</div>
</div>
При перезагрузке страницы пункты должны равномерно распределиться по нашему меню, как в начале главы. Мы можем воспроизвести желаемый макет, вставив auto-margin между пунктами, которые мы хотим отделить, как показано ниже:
.signup {
margin-left: auto;
}
auto-margin съедает все лишнее пространство в flex-контейнере, поэтому вместо равномерного распределения элементов, мы перемещаем .signup и все следующие элементы (.login) в правую часть контейнера. Получится точно такой же макет, как и раньше, но без лишнего вложенного <div> для группировки. Иногда приятнее оставить HTML более простым.
Flexbox дал нам тонну потрясающих новых инструментов для верстки веб-страниц. Сравните эти приемы с тем, что мы могли сделать с float элементами, и станет ясно, что flexbox - это более совершенный способ компоновки современных веб-сайтов:
Помните, что эти свойства flexbox - всего лишь язык, позволяющий указывать браузерам, как расположить множество HTML-элементов. Самое сложное - это не написание HTML- и CSS-кода, а концептуальное (на листе бумаги) определение поведения всех необходимых блоков для создания желаемого макета.
Когда дизайнер передает вам эскиз для воплощения, ваша первая задача - нарисовать на нем кучу блоков и определить, как они должны складываться, растягиваться и сжиматься, чтобы добиться желаемого дизайна. Как только вы это сделаете, вам будет довольно легко создать код, используя новые техники flexbox.
Режим верстки flexbox должен использоваться для большинства ваших веб-страниц, но есть некоторые вещи, с которыми он не очень хорошо справляется, например, мягкая настройка положения элементов и предотвращение их взаимодействия с остальной частью страницы. После того как на следующем уроке мы рассмотрим эти виды продвинутых техник позиционирования, вы станете экспертом по позиционированию в HTML и CSS.