Полное руководство по Flexbox

Введение

Модуль разметки Flexbox (от английского Flexible Box – гибкий блок), находящийся на данный момент на этапе «Возможная рекомендация» стандартизации W3C (W3C Candidate Recommendation) нацелен на обеспечение более эффективного способа разметки, выравнивания и распределения места между элементами в контейнере, даже если их размер неизвестен и/или определяется динамически (вот почему его назвали «гибкий»).

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

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

Что наиболее важно, разметка Flexbox не зависит от направления, в противоположность обычным разметкам (блокам, которые ориентированы вертикально и строчным элементам, располагающимся горизонтально).

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

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

Основные понятия и термины

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

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

Основные понятия и термины

В основном, элементы будут размещены либо вдоль главной оси (от точки main-start до main-end), либо вдоль поперечной оси (от точки cross-start до cross-end):

  • main axis – это главная ось flex-контейнера, вдоль которой размещаются flex-элементы. Будьте осторожны, она не обязательно располагается горизонтально, ее положение зависит от свойства flex-direction (смотрите ниже);
  • main-start | main-end - flex-элементы располагаются внутри контейнера, начиная от точки main-start, и доходят до точки main-end;
  • main size – это ширина или высота flex-элемента, в зависимости от основной величины. В качестве значения свойства main size может быть установлено значение ширины или высоты;
  • cross axis – поперечная ось, перпендикулярная главной оси. Ее направление зависит от направления главной оси.
  • cross-start | cross-endflex-строки заполняются элементами и размещаются в контейнере, начиная со стороны cross-start по направлению к стороне cross-end;
  • cross size – ширина или высота flex-элемента, в зависимости от выбранной размерности. Свойство может иметь значение либо ширины, либо высоты поперечной размерности.

Свойства родительского класса (flex-контейнера)

Свойства родительского класса (flex-контейнера)

display

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

.container {
  display: flex; /* или inline-flex */
}

Отметим, что CSS-столбцы (columns) не имеют эффекта во flex-контейнере.

flex-direction

flex-direction

Это свойство задает главную ось, определяющую направление, в котором размещаются flex-элементы во flex-контейнере. Flexbox (за исключением опционального обертывания) является концептом однонаправленной разметки.

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

.container {
  flex-direction: row | row-reverse | column | column-reverse;
}
  • row (по умолчанию): слева направо для ltr; справа налево для rtl;
  • row-reverse: справа налево для in ltr; слева направо для rtl;
  • column: так же как row, но сверху вниз;
  • column-reverse: так же как row-reverse, но снизу вверх.

flex-wrap

flex-wrap

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

.container{
  flex-wrap: nowrap | wrap | wrap-reverse;
}
  • nowrap (по умолчанию): однострочный / слева направо для ltr; справа налево для rtl;
  • wrap: многострочный / слева направо для ltr; справа налево для rtl;
  • wrap-reverse: многострочный / справа налево для ltr; слева направо для rtl.

flex-flow (применяется к родительскому элементу flex-контейнера)

Это сокращенная форма свойств flex-direction и flex-wrap, которые вместе определяют главную и поперечную оси flex-контейнера. По умолчанию задается значение row nowrap:

flex-flow: <‘flex-direction’> || <‘flex-wrap’>

justify-content

justify-content

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

.container {
  justify-content: flex-start | flex-end | center | space-between | space-around;
}
  • flex-start (по умолчанию): элементы выравниваются к началу строки;
  • flex-end: элементы выравниваются к концу строки;
  • center: элементы выравниваются по центру строки;
  • space-between: элементы распределяются в строке равномерно: первый элемент располагается в начале строки, последний – в конце;
  • space-around: элементы распределяются в строке равномерно с одинаковым расстоянием между собой.

align-items

align-items

Это свойство определяет то, как по умолчанию располагаются flex-элементы относительно поперечной оси на текущей строке. Его можно считать версией justify-content для поперечной оси (перпендикулярной главной):

.container {
  align-items: flex-start | flex-end | center | baseline | stretch;
}
  • flex-start: граница элементов cross-start располагается на линии cross-start;
  • flex-end: граница элементов cross-start располагается на линии cross-end;
  • center: элементы располагаются по центру поперечной оси;
  • baseline: выравнивание элементов происходит согласно базовой линии;
  • stretch (по умолчанию): элементы растягиваются для того, чтобы заполнить контейнер (с учетом значений min-width/max-width).

align-content

align-content

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

Примечание: это свойство не будет работать, если есть только одна строка flex-элементов:

.container {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
  • flex-start: строки располагаются в начале контейнера;
  • flex-end: строки располагаются в конце контейнера;
  • center: строки располагаются посередине контейнера;
  • space-between: строки распределяются равномерно; первая строка – в начале контейнера, а последняя – в конце;
  • space-around: строки размещены равномерно на одинаковом расстоянии друг от друга;
  • stretch (по умолчанию): строки растягиваются, чтобы заполнить оставшееся пространство.

Свойства дочерних элементов

(flex-элементы)

(flex-элементы)

Order

Order

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

.item {
  order: <integer>;
}

flex-grow

flex-grow

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

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

.item {
  flex-grow: <number>; /* default 0 */
}

Отрицательные числа недопустимы.

flex-shrink

Это свойство определяет для flex-элементов возможность сжиматься в случае необходимости:

.item {
  flex-shrink: <number>; /* default 1 */
}

Отрицательные числа недопустимы.

flex-basis

Это свойство определяет размер элементов по умолчанию перед распределением оставшегося пространства:

.item {
  flex-basis: <length> | auto; /* по умолчанию установлено auto */
}

flex

Это свойство является сокращенной формой для комбинации свойств flex-grow, flex-shrink и flex-basis. Второй и третий параметры (flex-shrink и flex-basis) задаются опционально. Значения по умолчанию: 0 1 auto:

.item {
  flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}

align-self

align-self

Это свойство позволяет переопределить выравнивание, заданное по умолчанию (или определенное свойством align-items) для отдельных flex-элементов.

Доступные значения вы можете найти в описании свойства align-items:

.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

Обратите внимание, что float, clear и vertical-align не работают с flex-элементами.

Примеры

Давайте начнем с очень простого примера, решив практически ежедневную проблему: идеальное выравнивание по центру. Нет ничего проще, если использовать flexbox:

.parent {
  display: flex;
  height: 300px; /* Или что угодно */
}

.child {
  width: 100px;  /* Или что угодно */
  height: 100px; /* Или что угодно */
  margin: auto;  /* Магия! */
}

Этот пример основывается на том факте, что свойство margin, установленное в значение auto, поглощает лишнее пространство. Поэтому такое задание отступа выравнивает элемент ровно по центру от обеих осей.

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

Мы хотим, чтобы они были красиво, равномерно распределены по горизонтальной оси таким образом, чтобы при изменении размера окна браузера, все смотрелось хорошо (без использования медиа запросов):

.flex-container {
  /* Сначала создаем контекст flex-разметки */
  display: flex;

  /* Затем определяем направление потока и хотим ли мы, чтобы элементы были обернуты. 
   * Помните, что это то же самое, что и: 
   * flex-direction: row;
   * flex-wrap: wrap;
   */
  flex-flow: row wrap;
    /* Теперь мы определяем, как распределять оставшееся пространство */
  justify-content: space-around;
}

Готово! Все остальное - уже вопросы оформления. Здесь располагается демонстрация этого примера. Перейдите по ссылке и попробуйте изменить размер окна, чтобы посмотреть, что произойдет:

HTML:

<ul class="flex-container">
  <li class="flex-item">1</li>
  <li class="flex-item">2</li>
  <li class="flex-item">3</li>
  <li class="flex-item">4</li>
  <li class="flex-item">5</li>
  <li class="flex-item">6</li>
</ul>

CSS:

.flex-container {
  padding: 0;
  margin: 0;
  list-style: none;
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;

  -webkit-flex-flow: row wrap;
  justify-content: space-around;
}
.flex-item {
  background: tomato;
  padding: 5px;
  width: 200px;
  height: 150px;
  margin-top: 10px;

  line-height: 150px;
  color: white;
  font-weight: bold;
  font-size: 3em;
  text-align: center;
}

Давайте попробуем еще кое-что. Допустим, у нас на самом верху веб-сайта находится меню навигации, выровненное вправо, но мы хотим, чтобы на экранах среднего размера оно располагалось по центру, а для устройств с маленьким экраном – располагалось в один столбец. Все достаточно просто:

CSS:

/* Большие экраны*/
.navigation {
  display: flex;
  flex-flow: row wrap;
  /* Выравнивание элементов к концу строки по главной оси*/
  justify-content: flex-end;
}
/* Средние экраны */
@media all and (max-width: 800px) {
  .navigation {
    /* Для экранов среднего размера, мы центрируем элементы, равномерно распределяя свободное пространство между ними */
    justify-content: space-around;
  }
}
/* Небольшие экраны */
@media all and (max-width: 500px) {
  .navigation {
    /* Для небольших экранах мы используем направление не по строкам, а по столбцам */
    flex-direction: column;
  }
}

HTML:

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

CSS:

.navigation {
  list-style: none;
  margin: 0; 
  background: deepskyblue;
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
  -webkit-flex-flow: row wrap;
  justify-content: flex-end;
}
.navigation a {
  text-decoration: none;
  display: block;
  padding: 1em;
  color: white;
}
.navigation a:hover {
  background: darken(deepskyblue, 2%);
}
@media all and (max-width: 800px) {
  .navigation {
    justify-content: space-around;
  }
}
@media all and (max-width: 600px) {
  .navigation {
    -webkit-flex-flow: column wrap;
    flex-flow: column wrap;
    padding: 0;
  }
    .navigation a { 
    text-align: center; 
    padding: 10px;
    border-top: 1px solid rgba(255,255,255,0.3); 
    border-bottom: 1px solid rgba(0,0,0,0.1); 
  }
  .navigation li:last-of-type a {
    border-bottom: none;
  }
}

Давайте попробуем сделать еще лучше, поиграв с "гибкостью" flex-элементов. Как насчет мобильной разметки в три столбца с заголовком и подвалом во всю ширину? И с выводом элементов, независимым от порядка, заданного исходным кодом:

CSS:

.wrapper {
  display: flex;
  flex-flow: row wrap;
}

/* Задаем для всех элементов ширину в 100% */
.header, .main, .nav, .aside, .footer {
  flex: 1 100%;
}

/* В этом случае для ориентации на мобильные устройства  мы полагаемся на исходный порядок:
 * 1. заголовок
 * 2. навигация
 * 3. основная часть
 * 4. боковая панель
 * 5. подвал
 */

/* Средние экраны*/
@media all and (min-width: 600px) {
  /* Обе боковые панели располагаются в одной строке */
  .aside { flex: 1 auto; }
}

/* Большие экраны */
@media all and (min-width: 800px) {
  /* Инвертируем порядок вывода первой боковой панели и основной части, и указываем основному элементу необходимость занимать максимум в два раза больше места, чем две боковые панели   */
  .main { flex: 2 0px; }

  .aside-1 { order: 1; }
  .main    { order: 2; }
  .aside-2 { order: 3; }
  .footer  { order: 4; }
}

HTML:

<div class="wrapper">
  <header class="header">Header</header>
  <article class="main">
    <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>  
  </article>
  <aside class="aside aside-1">Aside 1</aside>
  <aside class="aside aside-2">Aside 2</aside>
  <footer class="footer">Footer</footer>
</div>

CSS:

.wrapper {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;

  -webkit-flex-flow: row wrap;
  flex-flow: row wrap;

  font-weight: bold;
  text-align: center;
}

.wrapper > * {
  padding: 10px;
  flex: 1 100%;
}

.header {
  background: tomato;
}

.footer {
  background: lightgreen;
}

.main {
  text-align: left;
  background: deepskyblue;
}

.aside-1 {
  background: gold;
}

.aside-2 {
  background: hotpink;
}

@media all and (min-width: 600px) {
  .aside { flex: 1 auto; }
}

@media all and (min-width: 800px) {
  .main    { flex: 2 0px; }
  .aside-1 { order: 1; } 
  .main    { order: 2; }
  .aside-2 { order: 3; }
  .footer  { order: 4; }
}

Использование префиксов для flexbox

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

А все потому, что спецификация flexbox менялась с течением времени, создав "old" (старую), "tweener"(промежуточную), и "new" (новую) версии.

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

В качестве альтернативы, ниже приведен Sass метод @mixin для помощи с некоторыми префиксами, который к тому же раскрывает идею того, какие действия должны быть предприняты:

SCSS:

@mixin flexbox() {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
}

@mixin flex($values) {
  -webkit-box-flex: $values;
  -moz-box-flex:  $values;
  -webkit-flex:  $values;
  -ms-flex:  $values;
  flex:  $values;
}

@mixin order($val) {
  -webkit-box-ordinal-group: $val;  
  -moz-box-ordinal-group: $val;     
  -ms-flex-order: $val;     
  -webkit-order: $val;  
  order: $val;
}

.wrapper {
  @include flexbox();
}

.item {
  @include flex(1 200px);
  @include order(2);
}

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

Разделена «версиями» flexbox на:

  • (new) – означает поддержку последнего синтаксиса из спецификации (например, display: flex;).
  • (tweener) – означает поддержку дополнительного неофициального синтаксиса от 2011 года (например, display: flexbox;).
  • (old) – означает поддержку старого синтаксиса от 2009 года (например, display: box;).
ChromeSafariFirefoxOperaIEAndroidiOS
21+ (new)
20- (old)
3.1+ (old)
6.1+ (new)
2-21 (old)
22+ (new)
12.1+ (new)10 (tweener)
11+ (new)
2.1+ (old)
4.4+ (new)
3.2+ (old)
7.1+ (new)

Браузер Blackberry версии 10+ поддерживает новый синтаксис.