Flexbox в CSS

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

Если говорить коротко про документацию флексов, то верстка с Flexbox дает нам простые решения некогда непростых задач. Например, когда нужно выровнять элемент по вертикали, или прижать подвал к низу экрана, или просто вставить несколько блоков в один ряд, так чтобы они занимали все свободно пространство. Подобные задачи решаются и без flex. Но как правило, эти решения больше похожи на «костыли», и это еще больше усложняется когда нужно сверстать адаптивный под мобильные устройства макет. Тогда как с flexbox такие задачи решаются элегантно и именно так, как задумывает flex-модель (без добавления вспомогательных стилей и прочего).

CSS Flexible Box Layout Module (CSS модуль для макетов с гибкими блоками), коротко flexbox (Флексбокс), создана, чтобы убрать недостатки при создании самых разных HTML конструкций, в том числе адаптированных под разную ширину и высоту, и сделать верстку логичной и простой. А логичный подход, как правило работает в неожиданных местах, там где результат не проверялся - логика наше все!

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

Читайте также: Grid в CSS

Базовые знания

FlexBox состоит из Контейнера и его Дочерних элементов (items) (гибких элементов).

  • Главная ось - главное направление движения элементов внутри контейнера. Направление главной оси можно изменить с помощью свойства flex-direction. Обратите внимание, что при смене осей, меняются только направления движения блоков внутри, а начало, конец и размер контейнера остаются прежними.

  • Начало и конец главной оси - элементы располагаются от начала и до конца контейнера.

  • Поперечная ось - направление движения элементов, когда они не умещаются в контейнер по направлению главной оси. Поперечная ось всегда перпендикулярна (⊥) главной.

  • Начало и конец поперечной оси - по поперечной оси заполняются ряды от начала и до конца контейнера. В каждом таком ряду располагаются элементы (читайте ниже).

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

Для включения flexbox, любому HTML элементу достаточно присвоить css свойство display:flex; или display:inline-flex;.

<style>
	.flex{ display: flex; }
</style>
<div class="flex">
	<div class="item">1</div>
	<div class="item">2</div>
</div>

После включения flex свойства, внутри контейнера создаются две оси: главная и поперечная (перпендикулярная (⊥), кросс ось). Все вложенные элементы (первого уровня) выстраиваются по главной оси. По умолчанию главная ось горизонтальная и имеет направление слева направо (→), а кросс ось соответственно вертикальная и направлена сверху вниз (↓).

Главную и кросс оси можно поменять местами, тогда элементы будут располагаться сверху вниз (↓) и когда перестанут вмещаться в высоту то будут двигаться слева направо (→) - то есть оси просто поменялись местами. При этом начало и конец расположения элементов не меняется - меняются только направления (оси)! Именно поэтому нужно представлять себе оси внутри контейнера. Однако не нужно думать, что есть какие-то там «физические» оси и они на что-то влияют. Ось тут - это только лишь направление движения элементов внутри контейнера. Например, если мы указали выравнивание элементов по центру основной оси и потом изменили направление этой основной оси, то изменится и выравнивание: элементы были в середине по горизонтали, а стали в середине по вертикали... См. пример.

Еще одной важной особенностью Флекс-бокс является наличие рядов в поперечном направлении. Чтобы понять о чем речь, давайте представим что есть главная горизонтальная ось, много элементов и они не «лезут» в контейнер, поэтому переходят на другой ряд. Т.е. контейнер выглядит так: контейнер, внутри него два ряда, в каждом ряду по несколько элементов. Представили? А теперь запомните, что выравнивать по вертикали мы можем не только элементы, но и ряды! Как это работает хорошо видно в примере к свойству align-content. А вот так это выглядит схематически:

CSS свойства, которые могут влиять на модель построения макета: float, clear, vertical-align, columns не работают во flex конструкции. Тут используется другая модель построения макета и эти css свойства просто игнорируются.

CSS свойства Flexbox

Flexbox содержит разные css правила для управления всей flex конструкцией. Одни нужно применять к основному контейнеру, а другие к элементам этого контейнера.

Для контейнера

display:

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

  • flex - элемент растягивается на всю ширину и имеет свое полное пространство среди окружающих блоков. Происходит перенос строк в начале и в конце блока.
  • inline-flex - элемент обтекается другими элементами. При этом его внутренняя часть форматируется как блочный элемент, а сам элемент — как встроенный.

flex и inline-flex отличаются тем что по-разному взаимодействуют с окружающими элементами, подобно display:block и display:inline-block.

flex-direction:

Изменяет направление главной оси контейнера. Поперечная ось меняется соответственно.

  • row (default) - направление элементов слева направо (→)
  • column - направление элементов сверху вниз (↓)
  • row-reverse - направление элементов справа налево (←)
  • column-reverse - направление элементов снизу вверх (↑)

Нужно понимать, что при переходе с row на column или с row-reverse на column-reverse меняется только направление осей и больше ничего. Начало и конец расположения блоков остается неизменным (см. картинку в начале). Т.е. если при row элементы начинали свой путь справа/сверху, то при column все останется также - изменится только направление... (см. пример свойства flex-wrap)

flex-wrap:

Управляет переносом непомещающихся в контейнер элементов.

  • nowrap (default) - вложенные элементы располагаются в один ряд (при direction=row) или в одну колонку (при direction=column) независимо от того помещаются они в контейнер или нет.
  • wrap - включает перенос элементов на следующий ряд, если они не помещаются в контейнер. Так включается движение элементов по поперечной оси.
  • wrap-reverse - тоже что wrap только перенос будет не вниз, а вверх (в обратном направлении).
flex-flow: direction wrap

Объединяет оба свойства flex-direction и flex-wrap. Они часто используются вместе, поэтому чтобы писать меньше кода было создано свойство flex-flow.

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

/* только flex-direction */
flex-flow: row;
flex-flow: row-reverse;
flex-flow: column;
flex-flow: column-reverse;

/* только flex-wrap */
flex-flow: nowrap;
flex-flow: wrap;
flex-flow: wrap-reverse;

/* сразу оба значения: flex-direction и flex-wrap */
flex-flow: row nowrap;
flex-flow: column wrap;
flex-flow: column-reverse wrap-reverse;
justify-content:

Выравнивает элементы по основной оси: если direction=row, то по горизонтали, а если direction=column, то по вертикали.

  • flex-start (default) - элементы будут идти с начала (в конце может остаться место).
  • flex-end - элементы выравниваются по концу (место останется в начале)
  • center - по центру (место останется слева и права)
  • space-between - крайние элементы прижимаются к краям (место между элементами распределяется равномерно)
  • space-around - свободное пространство равномерно распределяется между элементами (крайние элементы не прижимаются к краям). Пространство между краем контейнера и крайними элементами будет в два раза меньше чем пространство между элементами в середине ряда.
  • space-evenly - тоже что space-around, только расстояние у крайних элементов до краев контейнера такое же как и между элементами.
align-content:

Выравнивает ряды, в которых находятся элементы по поперечной оси. То же что justify-content только для противоположной оси.

Заметка: Работает только когда высота контейнера фиксированная (выше чем ряды внутри него).

Т.е. если flex-direction: row, то это свойство будет выравнивать невидимые ряды по вертикали ¦. Тут важно заметить, что высота блока должна быть задана жестко и должна быть больше высоты рядов иначе сами ряды будут растягивать контейнер и любое их выравнивание теряет смысл, потому что между ними нет свободного места... А вот когда flex-direction: column, то ряды движется по горизонтали и ширина контейнера почти всегда больше ширины рядов и выравнивание рядов сразу приобретает смысл...

Это свойство мало где нужно и вместо него чаще используется align-items (см.ниже).

  • stretch (default) - ряды растягиваются заполняя строку полностью
  • flex-start - ряды группируются в верхней части контейнера (в конце может остаться место).
  • flex-end - ряды группируются в нижней части контейнера (место останется в начале)
  • center - ряды группируются по центру контейнера (место останется по краям)
  • space-between - крайние ряды прижимаются к краям (место между рядами распределяется равномерно)
  • space-around - свободное пространство равномерно распределяется между рядами (крайние элементы не прижимаются к краям). Пространство между краем контейнера и крайними элементами будет в два раза меньше чем пространство между элементами в середине ряда.
  • space-evenly - тоже что space-around, только расстояние у крайних элементов до краев контейнера такое же как и между элементами.
align-items:

Выравнивает элементы по поперечной оси внутри ряда (невидимой строки). Т.е. сами ряды выравниваются через align-content, а элементы внутри этих рядов (строк) через align-items и все это по поперечной оси. По главной оси такого разделения нет, там нет понятия рядов и элементы выравниваются через justify-content.

  • stretch (default) - элементы растягиваются заполняя строку полностью
  • flex-start - элементы прижимаются к началу ряда
  • flex-end - элементы прижимаются к концу ряда
  • center - элементы выравниваются по центру ряда
  • baseline - элементы выравниваются по базовой линии текста
column-gap:
gap:

Задает размер промежутка между элементами контейнера. Соответственно:

  • column-gap: — раздвигает только колонки.
  • gap: — раздвигает и колонки и ряды.

Размеры можно указать в абсолютных или относительных величинах: px, em, ... или в %.

Поддержка gap {percent} ( у column-gap поддержка точно такая же ):

Для элементов контейнера

flex-grow:

Задает коэффициент увеличения элемента при наличии свободного места в контейнере. По умолчанию flex-grow: 0 т.е. никакой из элементов не должен увеличиваться и заполнять свободное место в контейнере.

По умолчанию flex-grow: 0

Примеры:

  • Если всем элементам указать flex-grow:1, то все они растянуться одинаково и заполнять все свободное место в контейнере.
  • Если одному из элементов указать flex-grow:1, то он заполнит все свободное место в контейнере и выравнивания через justify-content работать уже не будут: свободного места нет выравнивать нечего...
  • При flex-grow:1. Если один из них имеет flex-grow:2, то он будет в 2 раза больше, чем все остальные
  • Если все flex-блоки внутри flex-контейнера имеют flex-grow:3, то они будут одинакового размера
  • При flex-grow:3. Если один из них имеет flex-grow:12, то он будет в 4 раза больше, чем все остальные

Как это работает? Допустим, что контейнер имеет ширину 500px и содержит два элемента, каждый из которых имеет базовую ширину 100px. Значит в контейнере остается 300 свободных пикселей. Теперь, если первому элементу укажем flex-grow:2;, а второму flex-grow: 1;, то блоки займут всю доступную ширину контейнера и ширина первого блока будет 300px, а второго 200px. Объясняется это тем, что доступные 300px свободного места в контейнере распределились между элементами в соотношении 2:1, +200px первому и +100px второму.

Заметка: в значении можно указывать дробные числа, например: 0.5 - flex-grow:0.5

flex-shrink:

Задает коэффициент уменьшения элемента. Свойство противоположное flex-grow и определяет как элемент должен сжиматься, если в контейнере не остается свободного места. Т.е. свойство начинает работать, когда сумма размеров всех элементов больше чем размер контейнера.

По умолчанию flex-shrink:1

Допустим, что контейнер имеет ширину 600px и содержит два элемента, каждый из которых имеет ширину 300px - flex-basis:300px;. Т.е. два элемента полностью заполняют контейнер. Первому элементу укажем flex-shrink: 2;, а второму flex-shrink: 1;. Теперь уменьшим ширину контейнера на 300px, т.е. элементы должны сжаться на 300px чтобы находится внутри контейнера. Сжиматься они будут в соотношении 2:1, т.е. первый блок сожмется на 200px, а второй на 100px и новые размеры элементов станут 100px и 200px.

Заметка: в значении можно указывать дробные числа, например: 0.5 - flex-shrink:0.5

flex-basis:

Устанавливает базовую ширину элемента - ширину до того как будут высчитаны остальные условия влияющие на ширину элемента. Значение можно указать в px, em, rem, %, vw, vh и т.д. Итоговая ширина будет зависеть от базовой ширины и значений flex-grow, flex-shrink и контента внутри блока. При auto элемент получает базовую ширину относительно контента внутри него.

По умолчанию: auto

Иногда лучше установить ширину элемента жестко через привычное свойство width. Например, width: 50%; будет означать, что элемент внутри контейнера будет ровно 50%, однако при этом все также будут работать свойства flex-grow и flex-shrink. Такое может быть нужно, когда элемент растягивается контентом внутри него, больше указанного во flex-basis. Пример смотрите в заметках.

flex-basis будет «жестким», если обнулить растяжение и сжатие: flex-basis:200px; flex-grow:0; flex-shrink:0;. Все это можно записать так flex:0 0 200px;.

flex: {grow shrink basis}

Короткая запись трех свойств: flex-grow flex-shrink flex-basis.

По умолчанию: flex: 0 1 auto

Однако можно указать и одно, и два значения:

flex: none; /* 0 0 auto */

/* число */
flex: 2; /* flex-grow (flex-basis переходит в 0) */

/* не число */
flex: 10em;    /* flex-basis: 10em */
flex: 30px;    /* flex-basis: 30px */
flex: auto;    /* flex-basis: auto */
flex: content; /* flex-basis: content */

flex: 1 30px; /* flex-grow и flex-basis */

flex: 2 2; /* flex-grow и flex-shrink (flex-basis переходит в 0) */

flex: 2 2 10%; /* flex-grow и flex-shrink и flex-basis */
align-self:

Позволяет изменить свойство align-items, только для отдельного элемента.

По умолчанию: от align-items контейнера

  • stretch - элемент растягиваются заполняя строку полностью
  • flex-start - элемент прижимаются к началу строки
  • flex-end - элемент прижимаются к концу строки
  • center - элемент выравниваются по центру строки
  • baseline - элемент выравниваются по базовой линии текста

order:

Позволяет менять порядок (позицию, положение) элемента в общем ряду.

По умолчанию: order: 0

По умолчанию элементы имеют order: 0 и ставятся в порядке их появления в HTML коде и направления ряда. Но если изменить значение свойства order, то элементы будут выстраиваться в порядке значений: -1 0 1 2 3 .... Например если одному из элементов указать order: 1, то сначала будут идти все нулевые, а потом элемент с 1.

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

Заметки

Чем отличается flex-basis от width?

Ниже важные различия между flex-basis и width / height:

  • flex-basis работает только для главной оси. Это значит что при flex-direction:row flex-basis контролирует ширину (width), а при flex-direction:column контролирует высоту (height). Смотрите пример.

  • flex-basis применяется только к flex элементам. А значит если отключить flex у контейнера это свойство не будет иметь эффекта.

  • Абсолютные элементы контейнера не участвуют во flex конструкции... А значит, flex-basis не влияет на элементы flex контейнера, если они абсолютны position:absolute. Им нужно будет указать width / height.

  • При использовании css свойства flex три значения: flex-grow / flex-shrink / flex-basis можно скомбинировать и записать коротко - flex:0 0 50%. Тогда как при использовании width, grow и shrink придется писать отдельно. Например: flex:0 0 50% == width:50%; flex-shrink:0;. Иногда это просто неудобно.

По возможности все же отдавайте предпочтение flex-basis. Используйте width только когда не подходит flex-basis.

Отличие flex-basis от width - баг или фича?

Контент внутри flex элемента распирает его и не может выйти за его пределы. Однако если установить ширину через width или max-width, а не flex-basis, то элемент внутри flex контейнера сумеет выйти за пределы этого контейнера (иногда нужно именно такое поведение). Пример:

Примеры Flex верстки

В примерах нигде не используются префиксы для кроссбраузерности. Сделал я так для удобного чтения css. Поэтому примеры смотрите в последних версиях Chrome или Firefox.

#1 Простой пример с выравниванием по вертикали и горизонтали

Начнем с самого простого примера - выравнивание по вертикали и горизонтали одновременно и при любой высоте блока, даже резиновой.

<div class="parent">
	<span class="child">Текст по середине</span>
</div>

<style>
	.parent { display: flex; }
	.child { margin: auto; }
</style>

Или так, без блока внутри:

<div class="center-text">
	Текст по середине
</div>

<style>
.center-text {
	display: flex;
	justify-content: center;
	align-items: center;
}
</style>

#1.2 Разделение (разрыв) между элементами флекс блока

Чтобы расположить элементы контейнера по краям и произвольно выбрать элемент после которого будет разрыв, нужно использовать свойство margin-left:auto или margin-right:auto.

#2 Адаптивное меню на flex

Сделаем меню в самом верху страницы. На широком экране оно должно быть справа. На среднем выравниваться по середине. А на маленьком каждый элемент должен быть на новой строке.

<div class="nav">
	<a href="#">Главная</a>
	<a href="#">О нас</a>
	<a href="#">Продукты</a>
	<a href="#">Контакты</a>
</div>

<style>
.nav {
	display: flex;
	justify-content: flex-end; /* разместим справа */

	background: #6e9cc3;
}

.nav a {
	color:#fff; padding:15px 10px; text-decoration:none; text-align:center;
}
.nav a:hover { background:#5c8db7; }

/* меньше 800px */
@media all and (max-width: 800px) {
	.nav { justify-content: space-around; }
	.nav a{ flex-grow:1; /* растягиваться на всю ширину */ }
}

/* меньше 500px */
@media all and (max-width: 500px) {
	.nav { flex-direction: column; }
}
</style>

Перейдите в jsfiddle.net и изменяйте ширину секции «результат»

#3 Адаптивные 3 колонки

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

Обратите внимание, что сделать это можно без использования media правил, все на flex.

<div class="flex">
	<div class="item">1</div>
	<div class="item">2</div>
	<div class="item">3</div>
	<div class="item">4</div>
	<div class="item">5</div>
	<div class="item">6</div>
</div>

<style>
.flex{
	display: flex;
	flex-wrap: wrap;

	max-width: 700px; /* макс ширина */
	margin: 0 auto; /* выровняем по центру */
}

.item{
	flex:1 1 calc(33.33% - 30px); /* отнимем margin и скажем растягиваться */
	margin:5px;
	box-sizing:border-box; /* чтобы внутренний отступ не влиял когда там будет текст... */
	min-width:170px; /* мин. ширина блока, чтобы переносились на другой ряд */

	padding:50px 20px; font-size:400%; text-align:center; background:#b5ced8; /* для красоты */
}
</style>

Перейдите в jsfiddle.net и изменяйте ширину секции «результат»

#4 Адаптивные блоки на flex

Допустим нам нужно вывести 3 блока, один большой и два маленьких. При этом нужно чтобы блоки подстраивались под маленькие экраны. Делаем:

<div class="flex">
	<div class="mainitem">1</div>
	<div class="sideitem">
		<div class="item">2</div>
		<div class="item">3</div>
	</div>
</div>

<style>
.flex{
	display: flex;

	max-width: 700px; /* макс ширина блока */
	margin: 0 auto; /* выравниваем блок по центру */
	font-size:400%;
}

.mainitem, .item{
	flex-grow:1; /* растягивание элементов */

	background:#b5ced8;
	display: flex; justify-content: center; align-items: center; /* цифры по центру */
}

.mainitem{
	min-height:300px; /* высота основного блока */
}

.sideitem{
	/* flex:0 0 150px; */
	flex-basis:150px; /* ширина 150 */
	flex-shrink:0; /* Убираем уменьшение = 150 */

	display: flex;
	flex-direction: column;
}

.item{
	flex-basis:50%;
	min-height:150px;
	margin-left:10px;
}
.item:first-child{ margin-bottom:10px; }

/* меньше 600px */
@media screen and (max-width: 600px) {
	.flex{ flex-wrap: wrap; } /* для переноса */

	.sideitem{ flex-direction:row; flex-basis:100%; }

	.item{ margin-left:0; margin-top:10px; }
	.item:first-child{ margin-bottom:0; margin-right:10px; }
}

/* меньше 450px */
@media screen and (max-width: 450px) {
	.sideitem{ flex-wrap: wrap; } /* разрешаем перенос */
	.item{ flex-basis:100%; } /* на всю ширину */
	.item:first-child{ margin-right:0; }
}
</style>

Перейдите в jsfiddle.net и изменяйте ширину секции «результат»

#5 Галерея на flex и transition

Этот пример показывает как быстро можно сделать симпатичный аккордеон с картинками на flex. Обратите внимание на свойство transition для flex.

<div class="flex">
  <div class="item img1"></div>
  <div class="item img2"></div>
  <div class="item img3"></div>
  <div class="item img4"></div>
  <div class="item img5"></div>
</div>

<style>
.flex {
	display: flex;

	overflow:hidden; /*скроем тень*/
}
.item {
	height:300px;

	flex:20%; /* = flex-basis:20%; */

	transition: flex 300ms ease;

	box-shadow: 0 0 19px 3px #000; margin: 1px; /* красивости тени */
}
.item:hover {
	 flex: 0.1 0.1 400px;
	background-size: 100% 100%
}

.img1 { background: url('http://lorempixel.com/400/300/cats/1') 0 0 no-repeat; }
.img2 { background: url('http://lorempixel.com/400/300/cats/2') 0 0 no-repeat; }
.img3 { background: url('http://lorempixel.com/400/300/cats/3') 0 0 no-repeat; }
.img4 { background: url('http://lorempixel.com/400/300/cats/4') 0 0 no-repeat; }
.img5 { background: url('http://lorempixel.com/400/300/cats/5') 0 0 no-repeat; }
</style>

#6 Флекс во флекс (просто пример)

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

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

Еще примеры

Смотрите здесь alexriz.github.io

Поддержка браузерами - 99%

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

Чтобы знать какие префиксы актуальны на сегодня (июнь. 2019), приведу пример всех flex правил с нужными префиксами:

/* Контейнер */
.flex {

	display:-ms-flexbox;
	display:flex;

	display:-ms-inline-flexbox;
	display:inline-flex;

	-ms-flex-direction:column;
	flex-direction:column;

	-ms-flex-wrap:wrap;
	flex-wrap:wrap;

	-ms-flex-flow:column wrap;
	flex-flow:column wrap;

	-ms-flex-pack:justify;
	justify-content:space-between;

	-ms-flex-line-pack:distribute;
	align-content:space-around;
}

/* Элементы */
.flex-item {

	-ms-flex-positive:1;
	flex-grow:1;

	-ms-flex-negative:2;
	flex-shrink:2;

	-ms-flex-preferred-size:100px;
	flex-basis:100px;

	-ms-flex:1 2 100px;
	flex:1 2 100px;

	-ms-flex-item-align:center;
	align-self:center;

	-ms-flex-order:2;
	order:2;

}

Лучше если свойства с префиксами будут идти до оригинального свойства.
В этом списке нет ненужных на сегодня (по caniuse) префиксов, но вообще префиксов больше.

Видео

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

Полезные ссылки по Flex

--

Использовал при написании:

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