Привет! Это первая статья по HTML&CSS из серии. И начать я решил именно с Flexbox. В интернете уже довольно много гайдов по Flexbox в CSS. Но чаще всего это шпаргалки, которые не сильно помогают разобраться как на самом деле все работает. Я постараюсь раскрыть тему на реальных примерах. А в конце статьи поделюсь лайфхаками, которыми сам постоянно пользуюсь в работе.

Если вам больше нравится воспринимать информацию в видео-формате, то можете посмотреть ролик на YouTube:

Что такое Flexbox и зачем он

Flexbox в CSS — это специальный инструмент, который помогает управлять расположением элементов на веб-странице.

Базовый пример

Flexbox особенно полезен для создания гибких и адаптивных макетов, так как конфигурацию flex-сетки легко менять относительно разных размеров устройств.

Основное применение – создание адаптивной сетки

Песочница

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

Div с классом container – это элемент с белой обводкой из примера. Он будет выступать в роли flex-контейнера. А внутри этого блока будут лежать сами элементы с классом item. Изначально наш пример без специальных flex-стилей выглядит следующим образом.

<div class="container">
  <div class="item item-1">item 1</div>
  <div class="item item-2">item 2</div>
  <div class="item item-3">item 3</div>
</div>
Пример без стилей

Пример будет доступен в песочнице на Codepen. Вы можете параллельно сами повторять все действия из статьи, так информация будет усваиваться еще лучше.

display: flex

Значение flex для контейнера задается через свойство display. По-умолчанию это свойство имеет значение display: block.

Мы прописываем значение display: flex. И тогда наш блок превращается во flex-контейнер.

.container {
  display: flex;
}
Пример с применением display: flex

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

flex-direction

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

Если написать значение column у flex-direction, то направление элементов поменяется.

.container {
  display: flex;
  flex-direction: column;
}
flex-direction: column

У flex-direction также доступны и реверсивные свойства. Например, row-reverse отображает значения по горизонтали в обратном порядке.

.container {
  display: flex;
  flex-direction: row-reverse;
}
flex-direction: row-reverse

А column-reverse выставляет значения в обратном порядке по вертикали.

.container {
  display: flex;
  flex-direction: column-reverse;
}

flex-wrap

Если добавить больше элементов в пример, то мы увидим что они ужимаются в 1 ряд и не переходят на новую строку.

<div class="container">
  <div class="item item-1">item 1</div>
  <div class="item item-2">item 2</div>
  <div class="item item-3">item 3</div>
  <div class="item item-4">item 4</div>
  <div class="item item-5">item 5</div>
  <div class="item item-6">item 6</div>
  <div class="item item-7">item 7</div>
  <div class="item item-8">item 8</div>
  <div class="item item-9">item 9</div>
</div>
flex-wrap: no-wrap

За перенос элементов на новую строку отвечает свойство flex-wrap. Если мы укажем flex-wrap: wrap, то элементы перенесутся на новую строку.

.container {
  display: flex;
  flex-wrap: wrap; /* default - flex-wrap: no-wrap */
}
flex-wrap: wrap

Если указать значение wrap-reverse, то элементы начнут выстраиваться начиная с нижней линии и дальше перейдут уже сверху на новую строку.

.container {
  display: flex;
  flex-wrap: wrap-reverse;
}
flex-wrap: wrap-reverse

justify-content

Свойство justify-content позволяет управлять расстоянием между элементами по главной оси.

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

.container {
  display: flex;
  justify-content: flex-end;
}
justify-content: flex-end

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

.container {
  display: flex;
  justify-content: flex-start; /* default */
}
justify-content: flex-start

Рассмотрим остальные возможные значения ниже.

Значение center сжимает все элементы в центр.

.container {
  display: flex;
  justify-content: center;
}
justify-content: center

Значение space-beetwen выставляет одинаковое расстояние между элементами, но прижимает их по краям.

.container {
  display: flex;
  justify-content: space-between;
}
justify-content: space-between

Значение space-around выставляет одинаковое расстояния вокруг каждого элемента. Создается эффект двойного отступа между элементами, потому что плюсуются отступы от каждого элемента.

.container {
  display: flex;
  justify-content: space-around;
}
justify-content: space-around

Значение space-evenly создает расстояние одинаково по краям и между элементами.

.container {
  display: flex;
  justify-content: space-evenly;
}
justify-content: space-evenly

align-items

Следующее важное свойство для flex-контейнера – это свойство align-items. Оно задает правила как выравнивать элементы по вертикали, то есть по дополнительной оси.

Но чтобы посмотреть как оно работает, нам нужно сделать элементы разной высоты. Для этого добавим разное количество строк в каждом item.

<div class="item item-1">
    item 1
    <div>text</div>
    <div>text</div>
</div>
<div class="item item-2">
    item 2
    <div>text</div>
</div>
<div class="item item-3">
    item 3
</div>
align-items: stretch

Изначально мы видим, что все значения растянулись на одинаковую высоту. Это происходит потому что align-items имеет значение stretch по-умолчанию.

Дальше рассмотрим другие доступные свойства.

Значение flex-start прижимает все элементы кверху.

.container {
  display: flex;
  align-items: flex-start;
}
align-items: flex-start

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

.container {
  display: flex;
  align-items: flex-end;
}
align-items: flex-end

Значение center выровняет все элементы по центру.

.container {
  display: flex;
  align-items: center;
}
align-items: center

Значение baseline на первый взгляд похож на flex-start.

.container {
  display: flex;
  align-items: baseline;
}
align-items: baseline

Но, если мы поставим разный размер шрифта во всех элементах, то увидим, что элементы немного съезжают.

.item-1 {
  font-size: 32px;
}

.item-2 {
  font-size: 64px;
}

.item-3 {
  font-size: 16px;
}
align-items: baseline (с разным размером шрифта)

Значение baseline выравнивает элементы по нижней границе первой линии шрифта, так называемой baseline. Обратите внимание на желтую линию на скриншоте.

gap

Последнее свойство для flex-контейнера – это свойство gap. Оно выставляет расстояние между элементами. Добавим больше элементов в пример и передадим в свойство gap значение 16px.

.container {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}
gap: 16px

Мы видим, что между элементами добавились отступы в 16 пикселей.

Но мы можем выставлять разные отступы по горизонтали и по вертикали – для этого нужно передать 2 значения подряд. Попробуем передать 16px и 32px. И увидим, что первое свойство установит отступы по вертикали, а второе свойство выставит отступы по горизонтали.

.container {
  display: flex;
  flex-wrap: wrap;
  gap: 16px 32px;
}
gap: 16px 32px

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

order

И можем начать со свойства order. Это свойство задает порядок отображения конкретного элемента. По умолчанию у всех элементов стоит order: 0 и тогда элементы просто отображаются в таком порядке, в котором они объявлены в HTML.

Попробуем поменять значение order на 1 у второго элемента .

.item-2 {
  order: 1;
}
"order: 1" у "item 2"

Видим сразу же что второй элемент отправился в самый конец. Потому что у первого элемента значение order: 0, у третьего элемента значение order: 0, а у второго элемента значение order: 1 и он соответственно уходит в самый конец.

Теперь попробуем поменять order у всех элементов.

.item-1 {
  order: 5;
}

.item-2 {
  order: 3;
}

.item-3 {
  order: 1;
}
Пример с разными order

Кстати, order не обязательно должен идти подряд 1 к одному. Мы можем указывать любые значения, а CSS просто сравнивает какое больше, а какое меньше.

align-self

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

Для наглядности сделаем разную высоту для элементов и выставим align-items: center у контейнера. После этого первому элементу в свойстве align-self укажем значение flex-start.

.container {
  display: flex;
  align-items: center;
}

.item-2 {
  align-self: flex-start;
}
align-self: flex-start

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

Если поменяем значение на flex-end, то элемент перескочит вниз.

.item-2 {
  align-self: flex-end;
}
align-self: flex-end

И так можно менять каждый элемент по-отдельности. Для свойства align-self доступны такие же значения, как и для свойства align-items у flex-контейнера – flex-start, flex-end, center, baseline, stretch.

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

flex-grow

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

Свободное пространство контейнера

Обратите внимание, что значение flex-grow – это не соотношение элементов друг к другу.

По-умолчанию flex-grow имеет значение 0. Именно поэтому изначально элементы никак не делят пространство между собой. Если укажем 1, то все элементы становятся одинаковыми, потому что они равномерно разделили между собой это свободное место.

.item {
  flex-grow: 1;
}
flex-grow: 1

Теперь попробуем перезаписать значение flex-grow самого первого элемента на 0, и увидим, что он снова уменьшился.

.item {
  flex-grow: 1;
}

.item-1 {
  flex-grow: 0;
}
Дележка свободного пространства

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

Теперь попробуем прописать разное значение flex-grow для всех элементов.

.item-1 {
  flex-grow: 1;
}

.item-2 {
  flex-grow: 2;
}

.item-3 {
  flex-grow: 3;
}

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

Дележка свободного пространства

Условно говоря, первому элементу достанется 1 доля от желтого блока, второму элементу достанется 2 доли, а третьему элементу достанется 3 доли.

Кстати, это свойство перестанет работать, если контент и так не будет помещаться на экран. Например, добавим еще элементов и попробуем указать для .item-1 значение flex-grow больше 0.

.item-1 {
  flex-grow: 2;
}
Пример с переполненным контейнером

Ничего не меняется. Это происходит потому что нет свободного места и делить соответственно нечего.

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

flex-basis

Следующее свойство – это свойство flex-basis. Это прям супер важное свойство и очень часто используется на практике.

Свойство flex-basis указывает, какую ширину займет элемент по-умолчанию. Может показаться, что flex-basis является заменой width, но на самом деле это не так. Свойство flex-basis устанавливает лишь оптимальную ширину, то есть если мы передадим flex-basis: 200px, то вообще не факт что элемент будет шириной в 200px – он может быть как больше, так и меньше. Давайте разбираться почему так происходит.

Для начала укажем для всех элементов flex-basis 200px:

.item {
  flex-basis: 200px;
flex-basis: 200px

Можем убедиться, что она действительно стала 200px. Давайте попробуем подставить другие значения. Например, укажем 50px.

.item {
  flex-basis: 50px;
}
flex-basis: 50px

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

Похожая история будет происходить если указать слишком большое значение. Например, попробуем во flex-basis передать 300px.

.item {
  flex-basis: 300px;
}
flex-basis: 300px

И теперь видим, что элементы наоборот занимают меньшую ширину – 262px вместо 300px. Иначе они бы просто не поместились в контейнер.

Очень часто на практике для flex-basis используют проценты. Например напишем 25%:

.item {
  flex-basis: 25%;
}
flex-basis: 25%

Видим, что все элементы заняли 1/4 часть. Таким образом создается сетка на сайтах. Пример такой сетки будет показан в конце статьи.

flex-shrink

flex-shrink помогает распределить пространство, если места в контейнере недостаточно. Создадим ситуацию, когда места недостаточно. Попробуем указать большой flex-basis для элементов, так чтобы они не помещались. Также мы добавим фиксированную ширину в 600px для flex-контейнера.

.container {
  display: flex;
  width: 600px;
}

.item {
  flex-basis: 300px;
}
flex-basis: 300px;

По факту элементы приняли ширину 200px, а не 300px, как видно на скриншоте. Хотя в идеале все три элемента должны занимать ширину 900px, если бы контейнер был побольше.

Если мы укажем flex-shrink: 0, то как раз и увидим ширину содержимого 900px. Элементы просто вылезут за пределы контейнера.

.item {
  flex-basis: 300px;
  flex-shrink: 0;
}
flex-basis: 300px; flex-shrink: 0;

Это происходит, потому что flex-shink: 0 запрещает элементам сжиматься. И они строго занимают оптимальную ширину, которую мы указали во flex-basis. Если мы поменяем значение на 1, то вернемся в исходное состояние. 1 - это значение по-умолчанию свойства flex-shrink.

Теперь попробуем дать разный flex-shrink для всех элементов и посмотрим что из этого получтися.

.item {
  flex-basis: 300px;
}

.item-1 {
  flex-shrink: 1;
}

.item-2 {
  flex-shrink: 2;
}

.item-3 {
  flex-shrink: 3;
}
Пример с разными значениями flex-shrink

Обратите внимание на реальную ширину элементов. У первого элемента ширина 150px, у второго 200px, а у третьего 250px. Почему так получилось?

Наш контейнер условно переполнялся на 300px, если бы везде была оптимальная ширина из flex-basis. В результате эти 300px отбавились у всех элементов пропорционально коэффициенту flex-shrink. Первый стал меньше на 150px, второй элемент стал меньше на 100px, а третий элемент стал меньше на 50px.

Сокращенная запись

Для этих трех свойств (flex-grow, flex-shrink и flex-basis) существует более короткая запись. Мы можем написать следующим образом:

.item {
  flex:  1 1 200px;
}

Первое значение отвечает за flex-grow, второе за flex-shrink, а третье за flex-basis. Вы можете писать как удобнее, оба варианта поравильные – и полный и сокращенный.

Лайфхаки

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

На моем видео в YouTube эти примеры разбираются подробнее. А здесь вы можете посмотреть на код и попробовать поэкспериментировать.

Центрирование элемента

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

Сетка

Следующий пример чуть посложнее. Но у меня нет ни одного проекта, где бы я не написал такую сетку для себя.

Растягивание на доступное пространство

И последний пример, который я очень часто использую в работе – это компонент с фиксированной частью. Эта фиксированная часть должна занимать столько места, сколько контента в ней содержится. В данном случае это кнопка “Отправить”. Как видим она всегда одной ширины. А вторая часть компонента должна растягиваться во все доступное пространство.

Итог

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


Подписывайтесь на телеграм‑канал Вайтишная — выкладываю там полезные материалы и пишу честно про IT