
Привет, Хабр. Я продолжаю отвечать на вопросы из собеседований на должность фронтендера. Сегодня я отвечу на следующий вопрос: «В чём отличия между значениями block, inline, flex, inline-flex, grid и inline-grid для свойства display?»
Прошу внимания. Мой ответ будет основан на теории, которую я описал в статье «Зачем нужно использовать свойство display?». Прочитайте, пожалуйста, сначала её.
Последний технический момент. Во всех примерах я использую <body> в качестве родительского контейнера. На картинках он обозначен голубой пунктирной линией.
А теперь переходим к статье.
Ключевое слово flow
Ключевое слово flow является базовым. Именно благодаря ему вы узнали про разницу между «блочными» и «строчными» элементами. Посмотрим, в чём же разница.
▍ Начальное расположение
Во всех курсах, попадавшихся мне, при рассмотрении свойства display есть одна общая черта. Преподаватели говорили, что элементы могут располагаться друг под другом или друг за другом. Показывали примеры со значением block и inline.
И у меня всегда был вопрос: «А как же значения flex, grid ,inline-flex и inline-grid?» Ведь в них же тоже используются ключевые слова block и inline.
Мы не будем поступать так же. Рассмотрим все значения. Первыми будут — block, flex и grid.

Элементы выстроились в столбец. И так получается, потому что у каждого значения используется ключевое слово block.
Похожий алгоритм существует и при ключевом слове inline. В значениях inline, inline-flex и inline-grid оно выстраивает элементы в строку.

Если элементы не помещаются внутри родителя, то они переносятся на новую строку. Правда, есть разница. При значении inline элемент может частью остаться на одной строке, а другой перенесётся.

Оставшиеся значения такого не позволяют. Элементы полностью переносятся на новую строку.

Так произошло, потому что значение inline является особым. Оно создаёт inline box элемент, а не inline-level, как все остальные inline-* значения. И это не единственный случай выделения inline значения. Далее мы увидим другие.
▍ Свойства width и height
Говоря о данных свойства, нужно учитывать, что значение для них определённо всегда. Либо мы явно это делаем, либо браузеры делают это за нас неявно. Сначала рассмотрим пример, в котором нет установленного значения.

Значение свойства width рассчитано относительно такого же свойства у родительского элемента. Проще говоря, они равны. А значение свойства height зависит от высоты контента.
Теперь попробуем установить значения сами. Например, 100px.

Браузеры применили его корректно. Запомните, пожалуйста, этот момент. Далее он нам пригодится.
При использовании ключевого слова inline свойства width и height рассчитываются в зависимости от размеров контента элемента.

Попробуем явно установить те же значения для свойств width и height, какие использовали для ключевого слова block.

Cюрприз. Элемент со значением inline не поменял свои размеры, а все остальные это сделали.
▍ Свойство margin
У свойства margin вместе с ключевым словом block есть несколько неожиданных нюансов. С большой вероятностью вы слышали про схлопывание внешних отступов. Посмотрим ещё раз, к чему приводит данное поведение, установив margin: 10px для всех элементов.

Отступ между элементом со значением block и элементом со значением flex — 10px, а не 20px. Получается, браузеры не суммируют значения. Они их накладывают. А потом выбирают наибольшее.
Также к этому явлению относится случай, когда значения, установленные для дочерних элементов, выходят за границы родителя сверху и снизу. В нашем примере так происходит у элемента со значением block и grid.
Хорошо, что это поведение можно исправить. Свойства padding, border и overflow отменяют схлопывание после добавления их к родителю. В наш пример я добавлю padding: 10px к элементу <body>.

Свойство margin можно использовать так, чтобы браузеры динамически рассчитывали значение от границ родителя. Есть значение auto. По классике тут есть нюансы.
Вы же помните, что по умолчанию свойство width равно значению свойства width родителя? Другими словами, для расчётов значения auto нет места. Так что мы обязаны установить значение для свойства width. Я буду использовать 100px.
Второй нюанс заключается в том, что по вертикали значение не рассчитается. Для демонстрации этого поведения я установлю height: 200px для элемента <body>.



Ух, с ключевым словом block закончили. На очереди ключевое слово inline. Тут тоже есть свои нюансы. Определю margin: 10px для всех элементов.


Свойство margin у элемента со значением inline выходит за пределы родителя по вертикали. Это поведение напоминает случай с ключевым словом block.
Только есть разница. Это поведение отменить нельзя. У остальных элементов свойство отображается корректно. Дополнительно скажу, что так же будут отображены свойства padding и border.
А что со значением auto? Его не получится использовать. Браузеры игнорируют margin: auto.



▍ Свойство text-align
С помощью margin нельзя позиционировать элементы с ключевым словом inline. Как же быть? Свойство text-align служит для этой задачи.
Для примера я установлю text-align: center для элемента <body>.

Ключевое слово flex
У ключевых слов block и inline много неочевидных моментов. Поэтому я радовался, как ребёнок, когда появилось ключевое слово flex. Ведь теперь проблем будет меньше! Сейчас всё покажу.
В своём рассказе я буду использовать два термина. Флекс-контейнер и флекс-элемент. Первый обозначает родительский элемент, к которому применено значение flex или inline-flex. Второй означает дочерний элемент внутри флекс-контейнера.
Что ж, давайте начнём.
▍ Начальное расположение
Давайте добавим display: flex для элемента <body> и посмотрим, что получится.

Скорее всего, этот пример не является неожиданностью для вас. На всех курсах говорят, что внутри флекс-контейнера есть основная и дополнительная оси. Элементы выстраиваются друг за другом вдоль основной оси.
Давайте остановимся и вдумаемся в данное поведение. Что происходит?
Раньше, если нам нужно было изменить расположение элементов, мы меняли значение свойства display. А теперь меняй, не меняй, результат не изменится. Тут всем правят оси!
По этой причине существует свойство flex-direction, которое управляет ими. Изменяя его значение, мы получаем новое расположение элементов. Например, при значении column элементы отобразятся в столбец.

Перенос элементов тоже был изменён. Теперь по умолчанию он отключён. Элементы всегда будут на одной строке.

Включить его можно. Дополнительное свойство flex-wrap и значение wrap помогут.

▍ Свойства width и height
Внутри флекс-контейнера свойства width и height зависят от осей. Значение свойства по основной оси всегда рассчитывается от размеров контента, а по дополнительной — совпадает с таким же свойством у родителя.
При значении row значение свойства width зависит от ширины контента, а свойство height совпадает с таким же свойством родителя. Это видно, если определить высоту для родителя.

При значении column происходит всё наоборот.

Ещё мы можем легко установить значение для свойств без сюрпризов.

Конечно, всё гладко не могло пройти. Наверняка вы слышали про свойства flex-basis, flex-grow и flex-shrink. Их алгоритм работы не входит в тему статьи. Но есть момент, который важен.
При использовании флекс-контейнера эти свойства устанавливают значение для свойств width и height. Только не сразу для двух. А только для того свойства, которое совпадает с основной осью. Таким образом при flex-direction: row свойства влияют на свойство width, а при flex-direction: column — на свойство height.
▍ Свойство margin
Давайте добавим margin: 10px к флекс-элементам.

Мы видим, что браузеры отсчитывают значения от границ родительского элемента. Посмотрим, что будет с переключёнными осями. Для элемента <body> добавим flex-direction: column.

По-прежнему свойство margin не может выйти за пределы родителя. А ещё значения между элементами не накладываются друг на друга, а суммируются. Таким образом, отступ между ними 20px.
При использовании значения auto браузеры будут равномерно распределять пространство между флекс-элементами. В этом процессе есть ряд нюансов. Рассмотрим пример с одним флекс-элементом, у которого явно не определены свойства width и height.

Упс. Элемент перестал тянуться по дополнительной оси! Дело в том, что для расчёта итогового значения свойства margin требуется знать размеры элемента. А мы их же явно не указали. Вот браузеры делают так, что размеры флекс-элемента рассчитывались от контента.
Вот что будет, если задать значение 100px явно для свойств width и height.

Но это не единственная особенность. Когда мы рассматривали значения auto внутри ключевого слова flow, у нас не было сценария, когда значение применяется к нескольким элементам, находящимся на одной строке. А внутри ключевого слова flex это реально.
У нас будет два флекс-элемента. Для первого установим margin: auto.

Всё доступное пространство браузеры добавили к свойству margin. Как они рассчитали его? Они знают размеры родителя. Из них вычли размеры флекс-элементов. Итоговое значение равномерно распределили и добавили к элементу с margin: auto, сместив оставшиеся элементы к границам родителя.
В следующей ситуации margin: auto добавим к двум флекс-элементам.

Здесь происходит то же самое, только итоговое значение распределяется между двумя элементами.
▍ Свойство text-align
Мы рассматривали, как свойство text-align могло выравнивать элементы внутри ключевого слова flow. В случае с флекс-контейнером такое поведение не сработает. Но если к флекс-элементу применено ключевое слово block, то свойство будет работать для его контента и потомков с ключевым словом inline.

Свойство display дочерних элементов
Внутри флекс-контейнера есть только флекс-элементы. Это означает, что если у элемента было установлены значения inline или одно из inline-*, то они трансформируются в блочную альтернативу.
body {
display: flex;
}
.inline {
display: inline; /* здесь значение трансформируется в block */
}
.inline-flex {
display: inline-flex; /* здесь значение трансформируется в flex */
}
.inline-grid {
display: inline-grid; /* здесь значение трансформируется в grid */
}
Ключевое слово grid
Ключевое слово flex крутое. Только авторы стандартов не остановились на нём. А по моему мнению, прокачали, введя ключевое слово grid. Чтобы это рассмотреть, нам понадобится два термина: грид-контейнер и грид-элемент. Первый — элемент, у которого установлено display: grid или display: inline-grid. Второй — дочерний элемент грид-контейнера.
▍ Начальное расположение
Добавим display: grid к элементу <body> и посмотрим, как браузер отобразит элементы.

Правда же напоминает ключевое слово block? Но это ошибочное суждение. Мы легко можем изменить отображение. Свойство grid-auto-flow со значением column выстроит элементы в строку.

А если элементов будет слишком много, то браузеры не смогут перенести их.

Включить перенос элементов нельзя. Нам даже не помогут свойства grid-template-rows и grid-template-columns. Они создадут внутри грид-контейнера сетку, по которой можно расположить элементы. Но это уже не перенос элементов.
▍ Свойства width и height
Свойства width и height работают хитро. Начнём со свойства, относящегося к основному направлению. Браузеры стараются распределить пространство между грид-элементами в зависимости от их количества и контента. Для этого используют специальный алгоритм. Разберём его по шагам.
Шаг первый. Определяется значение свойства grid-auto-flow. Шаг второй. Если оно row, то браузеры вычитают из свойства height родителя общую высоту дочерних элементов. При значении column берётся свойство width грид-контейнера и из него вычитается общая ширина грид-элементов.
Шаг третий. Полученное значение делят на количество грид-элементов. Шаг четвёртый. При grid-auto-flow: row полученное значение добавляют к свойству height, а при grid-auto-flow: column — к свойству width.
По второму направлению значение свойств width или height также зависят от свойства grid-auto-flow. Только при любых значениях они cовпадают со значением таких же свойств у родителя.
Звучит сложно. Мне кажется, на примере будет понятнее. В нём к элементу <body> будет применено свойство grid-auto-flow. На первом изображении будет итоговый результат при значении по умолчанию (row), а на втором — при значении column.


Мы видим, что всё пространство заполнено. Но по основному направлению второй грид-элемент больше, чем первый. Как так получилось?
Начнём с того, что у второго элемента больше контента, чем у первого, а, следовательно, и размеры. После создания грид-контейнера при grid-auto-flow: row браузеры вычли из высоты родителя общую высоту дочерних элементов. При grid-auto-flow: column то же самое было с шириной.
Полученное значение поделили на количество элементов, т. е. на два. А его добавили к свойству height каждого грид-элемента в первом случае и к свойству width во втором. В итоге получилось, что второй элемент остался больше, чем первый, но вместе они заполнили грид-контейнер.
Переходим ко второму направлению. Грид-элементы и тут заполнили всё пространство. Но поскольку на этом направлении они не встречаются друг с другом, то браузеры повторили значения свойств width и height родителя. Только в первом случае это было значение свойства width, а во втором — значение свойства height.
▍ Свойство margin
Внутри грид-контейнера свойство работает во многом так же, как во флекс-контейнере. Значения свойства margin между соседними элементами суммируются, а крайние — рассчитываются от границы родителя.

Значение auto работает по двум осям в зависимости от свободного пространства. При его использовании браузеры рассчитают значения свойств width и height сами, основываясь на размерах контента.


Разница заключается в том, что элементы не могут сместиться к границам родителя. Они остаются на своём месте. Это лучше всего заметно при grid-auto-flow: column.

▍ Свойство text-align
Cвойство text-align для грид-контейнера работает так же, как для флекс-контейнера. Соответственно, оно влияет на контент и элементы с ключевым словом inline, находящиеся в грид-элементе.

▍ Свойство display дочерних элементов
По аналогии с ключевым словом flex внутри грид-контейнера грид-элементы приобретают блочные варианты значения для свойства display. Происходит такая же трансформация, поэтому не буду показывать её.
Заключение
Осталось перечислить отличия.
Отличие №1. Внутри элемента с ключевым словом flow могут существовать элементы со значениями inline, inline-flex и inline-grid. Внутри ключевого слова flex и grid их не будет. Они заменяются на блочную альтернативу.
Отличие №2. В случае использования ключевого слова flow позиция элемента меняется при помощи значений свойства display. При ключевых словах flex и grid используются дополнительные свойства flex-direction и grid-auto-flow.
Отличие №3. При ключевых словах flex и grid перенос элементов по умолчанию отключён. Включить его можно только для ключевого слова flex. При ключевом слове flow он есть сразу.
Отличие №4. Когда используются ключевые слова flow и flex для расчёта значения свойств width и height, браузеры используют родительское значение этих свойств или размеры контента элемента. При ключевом слове grid значения ещё зависят от количества элементов в родителе.
Отличие №5. Если определить значение для свойств width и height у элемента со значением inline, то браузеры не применят его. При остальных значениях так не будет.
Отличие №6. Значение auto не срабатывает для элементов со значениями inline и inline-*. Для других оно работает.
Отличие №7. Значение auto работает по двум осям внутри ключевых слов flex и grid. При ключевом слове flow только по одной.
Отличие №8. При ключевых словах flex и grid, установленных у родителя, значение auto заставляет браузер пересчитать значения свойств width и height по контенту для дочерних элементов. При ключевом слове flow такого нет.
Отличие №9. Значение auto распределяет равномерно между элементами при ключевых словах flex и grid. Только при ключевом слове flex могут сместиться соседние элементы, при ключевом слове grid такого нет. С ключевым словом flow распределения пространства нет.
Отличие №10. Свойство margin может выходить за границы родителя и накладываться друг на друга внутри ключевого слова flow. При ключевых словах flex и grid такого поведения нет.
Отличие №11. Свойство text-align работают вместе с ключевым словом flow. С ключевыми словами flex и grid оно игнорируется.
P.S. Присоединяйтесь к моему ТГ каналу CSS isn't magic. Ссылка в профиле.
Telegram-канал со скидками, розыгрышами призов и новостями IT ?
Spaceoddity
Ну уж нет!. Не стоит всё так упрощать)) Рассчитанное значение height - это довольно сложная абстракция. Вспомните хотя бы самую типовую задачу "прибить футер к низу вьюпорта", со всякими min-height, height: auto и т.п. И это мы ещё позиционирование в расчёт не брали...
melnik909 Автор
Тут я понимаю, что забыл, как прибивал футер....
Spaceoddity