Как работает CSS Flexbox: наглядное введение в систему компоновки элементов на веб-странице

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

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

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

Display: Flex

Вот пример страницы:

У нас есть 4 разноцветных div’а разных размеров, которые находятся внутри серого div’а. У каждого div’а есть свойство display: block. Поэтому каждый квадрат занимает всю ширину строки.

Чтобы начать работать с Flexbox, нам нужно сделать наш контейнер flex-контейнером. Делается это так:

1

2

3

#container {

  display: flex;

}

Вроде бы ничего особо и не изменилось — div’ы всего лишь встали в ряд. Но вы сделали что-то действительно мощное. Вы дали вашим квадратам классное свойство, называемое «flex-контекст».

Flex Direction

У flex-контейнера есть две оси: главная ось и перпендикулярная ей.

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

1

2

3

4

#container {

  display: flex;

  flex-direction: column;

}

Важно заметить, что flex-direction: column не выравнивает квадраты по оси, перпендикулярной главной. Главная ось сама меняет свое расположение и теперь направлена сверху вниз.

 

Есть еще парочка свойств для flex-direction: row-reverse и column-reverse.

Justify Content

Justify-content отвечает за выравнивание элементов по главной оси.

Вернемся к flex-direction: row.

1

2

3

4

5

#container {

  display: flex;

  flex-direction: row;

  justify-content: flex-start;

}

Justify-content может принимать 5 значений:

  1. flex-start;
  2. flex-end;
  3. center;
  4. space-between;
  5. space-around.

Space-between задает одинаковое расстояние между квадратами, но не между контейнером и квадратами. Space-around также задает одинаковое расстояние между квадратами, но теперь расстояние между контейнером и квадратами равно половине расстояния между квадратами.

Align Items

Если justify-content работает с главной осью, то align-items работает с осью, перпендикулярной главной оси.

Вернемся обратно к flex-direction: row и пройдемся по командам align-items:

  1. flex-start;
  2. flex-end;
  3. center;
  4. stretch;
  5. baseline.

Стоит заметить, что для align-items: stretch высота квадратов должна быть равна auto. Для align-items: baseline теги параграфа убирать не нужно, иначе получится вот так:

Чтобы получше разобраться в том, как работают оси, давайте объединим justify-contentс align-items и посмотрим, как работает выравнивание по центру для двух свойств flex-direction:

Align Self

Align-self позволяет выравнивать элементы по отдельности.

1

2

3

4

5

6

7

#container {

  align-items: flex-start;

}

.square#one {

  align-self: center;

}

// Only this square will be centered.

Давайте для двух квадратов применим align-self, а для остальных применим align-items: center и flex-direction: row.

 

Flex-Basis

Flex-basis отвечает за изначальный размер элементов до того, как они будут изменены другими свойствами Flexbox:

Flex-basis влияет на размер элементов вдоль главной оси.

Давайте посмотрим, что случится, если мы изменим направление главной оси:

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

Flex Grow

Это свойство немного сложнее.

Для начала давайте зададим нашим квадратикам одинаковую ширину в 120px:

По умолчанию значение flex-grow равно 0. Это значит, что квадратам запрещено расти (занимать оставшееся место в контейнере).

Попробуем задать flex-grow равным 1 для каждого квадрата:

Квадраты заняли оставшееся место в контейнере. Значение flex-grow аннулирует значение ширины.

Но здесь возникает один вопрос: что значит flex-grow: 1?

Попробуем задать flex-grow равным 999:

И… ничего не произошло. Так получилось из-за того, что flex-grow принимает не абсолютные значения, а относительные.

Это значит, что не важно, какое значение у flex-grow, важно, какое оно по отношению к другим квадратам:

Вначале flex-grow каждого квадрата равен 1, в сумме получится 6. Значит, наш контейнер поделен на 6 частей. Каждый квадрат будет занимать 1/6 часть доступного пространства а контейнере.

Когда flex-grow третьего квадрата становится равным 2, контейнер делится на 7 частей (1 + 1 + 2 + 1 + 1 + 1).

Теперь третий квадрат занимает 2/7 пространства, остальные — по 1/7.

И так далее.

Стоит помнить, что flex-grow работает только для главной оси (пока мы не поменяем ее направление).

Flex Shrink

Flex-shrink — прямая противоположность flex-grow. Оно определяет, насколько квадрату можно уменьшиться в размере.

Flex-shrink используется, когда элементы не вмещаются в контейнер.

Вы определяете, какие элементы должны уменьшиться в размерах, а какие — нет. По умолчанию значение flex-shrink для каждого квадрата равно 1. Это значит, что квадраты будут сжиматься, когда контейнер будет уменьшаться.

Зададим flex-grow и flex-shrink равными 1:

Теперь давайте поменяем значение flex-shrink для третьего квадрата на 0. Ему запретили сжиматься, поэтому его ширина останется равной 120px:

Стоит помнить что flex-shrink основывается на пропорциях. То есть, если у квадрата flex-shrink равен 6, а у остальных он равен 2, то, это значит, что наш квадрат будет сжиматься в три раза быстрее, чем остальные.

Flex

Flex заменяет flex-growflex-shrink и flex-basis.

Значения по умолчанию: 0 (grow) 1 (shrink) и auto (basis).

Создадим два квадрата:

1

2

3

4

5

6

.square#one {

  flex: 2 1 300px;

}

.square#two {

  flex: 1 2 300px;

}

У обоих квадратов одинаковый flex-basis. Это значит, что они оба будут шириной в 300px (ширина контейнера: 600px плюс margin и padding).

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

Как вещи растут и сжимаются

Когда увеличивается первый квадрат, он не становится в два раза больше второго квадрата, и когда уменьшается второй квадрат, он не становится в два раза меньше первого. Это происходит из-за того, что flex-grow и flex-shrink отвечают за темп роста и сокращения.

Немного математики

Начальный размер контейнера: 640px. Вычтем по 20px с каждой стороны для padding, и у нас останется 600px, как раз для двух квадратов.

Когда ширина контейнера становится равной 430px (потеря в 210px), первый квадрат (flex-shrink: 1) теряет 70px. Второй квадрат (flex-shrink: 2) теряет 140px.

Когда контейнер сжимается до 340px, мы теряем 300px. Первый квадрат теряет 100px, второй — 200px.

Тоже самое происходит и с flex-grow.

 

Перевод статьи «How Flexbox works — explained with big, colorful, animated gifs»

Никита Мингалеев, юный падаван