Всё о магии отступов в CSS Flexbox

Stas Bagretsov
6 min readApr 13, 2019

--

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

Перевод двух статей:

The peculiar magic of flexbox and auto margins

Spacing Between Multi-line Flexbox Child Elements

👉Мой Твиттер — там много из мира фронтенда, да и вообще поговорим🖖. Подписывайтесь, будет интересно: ) ✈️

Причудливая магия flexbox и автоматических отступов

В фронт-энд разработке зачастую вы сталкиваетесь с состоянием “я знаю, что чего-то не знаю”. Я могу прекрасно знать какой инструмент в CSS мне нужен, но также могу даже не иметь представления о том как его использовать или какой у него верный синтаксис. В общем, в моей голове появляется совершенно пустая комната и когда я хочу что-то в ней отыскать, то нахожу там маленькую записку с неразборчивым почерком.

И одна из таких тем, это то, как взаимодействуют flexbox и margin.

Возьмём этот пример:

.parent {
display: flex
}
.child {
margin: auto;
}

Что он делает? Кажется я припоминаю, что есть несколько модных штук, которые вы можете провернуть с его помощью и на этой неделе я уже подзабыл про них, после прочтения отличного старенького поста от Sam Provenza о том как auto-margin’ы и flexbox работают вместе. Но я еще не совсем понимал концепцию этого всего дела, даже после прочтения поста и не понял бы, если бы не начал сам делать демки.

В этом посте, Sam описывает то, как margin:auto влияет на flex-элементы, таким образом:

Если вы применяете автоматические внешние отступы на flex-элементе, то этот элемент автоматически заберет в отступ все дополнительное свободное пространство контейнера, в зависимости от направления, в котором применяется auto-margin.

Давайте разберем этот момент и представим, что у нас есть родительский div с div’ом потомком внутри:

<div class="parent">
<div class="child"></div>
</div>

И предположим, что мы используем следующий CSS для стилизации этих div’ов:

.parent {
display: flex;
height: 400px;
background-color: #222;
}
.child {
background-color: red;
width: 100px;
height: 100px;
}

Результат будет примерно таким:

Когда мы добавляем margin-left: auto для .child элемента, как тут:

.child {
background-color: red;
width: 100px;
height: 100px;
margin-left: auto;
}

То увидим мы вот такое:

Странно, да? Левый margin отталкивает от родителя таким образом, что потомок оказывается в дальнем правом углу. Но всё становится ещё страннее, когда мы выставляем всем внешним отступам значение auto:

.child {
background-color: red;
width: 100px;
height: 100px;
margin: auto;
}

Это как если бы мы применили знаменитый трюк с центрированием, выставив justify-content и align-items на center, потому потомок решает остаться в центре родителя, как горизонтально, так и вертикально. Похожим образом, если мы выставим margin-left и margin-top на auto, мы можем спихнуть flex-элемент в нижний правый угол родителя:

Когда Sam говорит, “этот элемент будет автоматически отодвинут по указанному внешнему отступу, чтобы занять свободное место в flex-контейнере”, то в моей пустой комнате это интерпретируется как-то примерно так:

Выставление свойства margin flex-потомку, оттолкнет его в указанном направлении. Выставив margin-left на auto, вы оттолкнете потомка вправо. Выставив margin-top на auto, вы оттолкнете потомка вниз.

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

Почему это полезно знать? Я думаю, что есть несколько моментов, когда justify-self или align-self могут быть немного ни тем, что вам нужно, в то время как использование автоматических внешних отступов даёт вам дополнительную гибкость для работы с элементами страницы. Я видел много демок, включая те, которые делала Sam для своего поста и все они в основном использовались для расстановки элементов в меню навигации. В общем, отталкивание одного элемента в меню на самый низ или далеко вправо flex-родителя это определенно полезнейшая штука в подобных сценариях.

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

Интервалы между многострочными flexbox потомками

Однострочные интервалы это легко. А давайте-ка растянем наши flexbox элементы на несколько строк.

Одна строка

В однострочном дизайне, сделать отступ между элементами довольно легко. Это похоже на то, как вы возможно уже делаете это с нефлексбоксными родственными элементами. Если у .parent стоит display: flex и потомкам нужен интервал в 12px между ними, без этого же интервала по краям, то это сделать довольно легко:

<div class="parent">
<div class="child"></div>
<div class="child"></div>
</div>
.parent {
display: flex;
}
.child + .child {
margin-left: 12px;
}

Многострочность для адаптивного дизайна

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

.parent {
display: flex;
flex-direction: column;
}
.child + .child {
margin-top: 12px;
}
@media screen and (min-width: 321px) {
.parent {
flex-direction: row;
}
.child + .child {
margin-top: 0;
margin-left: 12px;
}
}

Интервал всё ещё присутствует, просто указан как margin-top или margin-left, в зависимости от медиа запроса.

Многострочность для wrap’ов

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

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

Одним из возможных решений является использование простого margin. Мы можем получить надежное 12px разделение с внешним отступом по всем сторонам. Но мы также направим весь наш шаблон вниз и направо, из-за маргина, который обхватывает все наши дочерние элементы. Так что мы просто “крякнем.parent, вернув ему изначальную позицию и всё:

<div class="parent">
<div class="child child--1"></div>
<div class="child child--1"></div>
<div class="child child--2"></div>
<div class="child child--1"></div>
<div class="child child--1"></div>
</div>
.parent {
display: flex;
flex-wrap: wrap;
margin: -6px -6px;
}
.child {
margin: 6px;
}
.child--1 {
width: calc(50% - 12px);
}
.child--2 {
width: calc(100% - 12px);
}

.child — 1 и .child — 2 вариации используются просто для интереса и представляют собой любые требования к размерам.

Мы используем маргин 6px на .child, так как тут есть внешний отступ по всем сторонам элемента, который добавит до 12px в смежные .child интервалы.

--

--

Stas Bagretsov

Надеюсь верую вовеки не придет ко мне позорное благоразумие. webdev/sports/books