На первый взгляд в свойстве margin CSS нет ничего сложного и непонятного. Оно помогает освободить пространство вокруг любого элемента, отодвинув в сторону все остальное. Однако это свойство может оказаться более непредсказуемым, чем вы привыкли думать.
Первое, что узнают о CSS те, кто решил приступить к его изучению, — это, конечно же, блочная модель форматирования документа (The CSS Box Model). Одним из элементов этой модели как раз и является отступ (margin), прозрачная область, окружающая блок, другими словами — его внешние поля, которые отодвигают прочь от содержимого все остальные элементы. Свойства margin-top, margin-right, margin-bottom и margin-left были описаны еще в CSS1, наряду с сокращением margin, которое позволяет определять значения всех четырех свойств сразу.
Отступы кажутся довольно простой вещью, однако в этой статье будет рассмотрены те особенности их применения, которые все еще непонятны многим пользователям, а именно — как отступы взаимодействуют друг с другом и что такое их схлопывание.
Содержание статьи
Соседствующие блоки
Совершенно пустые блоки
Родительский и первый или последний дочерний элемент
Объединяются только отступы блоков
Что препятствует схлопыванию отступов
Создание контекста форматирования блока
Что выбрать для своего лендинга?
Отступы в ориентированном на поток мире
Блочная модель CSS
Итак, в первую очередь необходимо определить, что подразумевается под словосочетанием «блочная модель» (Box Model) и как менялось содержание этого понятия по мере выхода разных версий CSS.
Блочная модель описывает то, как различные элементы блока (бокса, коробки) — содержимое, внутренние поля, границы и отступы — отображаются на странице и взаимодействуют друг с другом. В CSS1 блочная модель была детализирована с помощью диаграммы ASCII, которую вы можете видеть на изображении ниже:
Описание блочной модели CSS в CSS1
Внешние поля для каждой из сторон блока и сокращение magrin были описаны в CSS1.
Спецификация CSS2.1 располагает иллюстрацией, которая демонстрирует блочную модель и дает определение терминов, которые используются для описания различных блоков. Спецификация описывает content box, padding box, border box и margin box. Как вы можете видеть, любой блочный элемент состоит из этого набора боксов: в центре — контент, его обрамляют внутренние поля (padding), затем следуют границы и потом — внешние поля, отступы.
Описание блочной модели CSS в CSS2
Схлопывание отступов
Спецификация CSS1 в своем определении отступов содержит фразу, что вертикальные отступы могут схлопываться (margins collapse), вкладываться друг в друга. Именно этот момент многих сбивает с толку. Схлопывание отступов небеспричинно, ведь раньше CSS использовался в качестве языка форматирования документов.
Данный процесс описывает ситуацию, когда у блочных элементов расположенных рядом друг с другом по вертикали, отступы не суммируются, а объединяются между собой, то есть если за нижним отступом заголовка следует параграф с верхним отступом, то между ними большого разрыва создано не будет.
При схлопывании отступов они объединяются, меньший отступ, по сути, заканчивается в большем.
Отступы объединяются:
- между соседствующими блоками;
- между абсолютно пустыми блоками;
- между родительским и первым или последним дочерним элементом.
Рассмотрим каждый из этих сценариев в отдельности, а затем перейдем к тому, что препятствует схлопыванию отступов.
Соседствующие блоки
Проще всего объяснить, что такое margin collapse, на примере того, как рушатся границы между соседствующими блоками. За исключением ситуаций, упомянутых ниже, если у вас есть два элемента, расположенных друг под другом, нижний отступ первого элемента объединится с верхним отступом следующего элемента.
В примере ниже вы увидите три div-элемента. У первого верхний и нижний отступ равен 50 пикселям, у второго — 20 пикселей, у третьего — 3em. Расстояние между первыми двумя элементами составляет 50 пикселей, поскольку меньший отступ вкладывается в больший. Расстояние между вторым и третьим элементом равен 3ем, так как 3em больше, чем 20 пикселей нижнего отступа второго элемента.
CSS:
.wrapper { border: 5px dotted black; } .example1 { margin: 50px 0 50px 0; } .example2 { margin: 20px 0 20px 0; } .example3 { margin: 3em 0 3em 0; } body { font: 1.4em/1.3 "Gill Sans", "Gill Sans MT", Calibri, sans-serif; margin: 2em 3em; } .box { background-color: rgb(55,55,110); color: white; padding: 20px; border-radius: .5em; }
Совершенно пустые блоки
Если блок пуст, то его верхний и нижний отступ могут объединиться друг с другом. В изображенном ниже примере у элемента с классом «empty» верхние и нижние отступы равны 50 пикселям, однако пространство между первым и третьим элементами не 100 пикселей, а 50. Это связано с тем, что два отступа схлопнулись. Добавление чего-либо в этот пустой блок (даже внутренних полей) приведет к тому, что верхние и нижние отступы будут использоваться, а не схлопываться.
CSS:
.wrapper { border: 5px dotted black; } body { font: 1.4em/1.3 "Gill Sans", "Gill Sans MT", Calibri, sans-serif; margin: 2em 3em; } .box { background-color: rgb(55,55,110); color: white; border-radius: .5em; } .empty { margin: 50px 0 50px 0; }
Родительский и первый или последний дочерний элемент
Это сценарий схлопывания отступов, который сбивает с толку многих, поскольку не является интуитивно понятным.
В примере ниже имеется div-блок с классом «wrapper» (оболочка): для удобства понимания граница этого блока имеет красный цвет. Все три дочерних элемента имеют отступы размером в 50 пикселей. Однако первый и последний элементы находятся на одном уровне с краями оболочки: между элементом и оболочкой нет 50-пиксельного поля.
CSS:
.wrapper { outline: 1px solid red; } .box { margin: 50px; } body { font: 1.4em/1.3 "Gill Sans", "Gill Sans MT", Calibri, sans-serif; margin: 2em 3em; } .box { background-color: rgb(55,55,110); color: white; padding: 20px; border-radius: .5em; }
Это связано с тем, что отступ дочернего элемента схлопывается с любым отступом на родительском элементе. Вы увидите это, если посмотрите на первый дочерний элемент с помощью DevTools. Выделенная желтая область — это внешние поля, отступы.
DevTools может помочь вам увидеть, где именно заканчиваются отступы
Объединяются только отступы блоков
Из последнего примера можно узнать еще кое-что полезное о схлопывании отступов. В CSS2 возможно объединение внешних полей только тех объектов, которые соседствуют друг с другом вертикально: это верхние и нижние поля, если вы находитесь в горизонтальном режиме записи. Таким образом, левый и правый отступы не объединяются и не выходят за пределы обертки.
Примечание: следует помнить, что поля объединяются только в направлении блока, например, между абзацами.
Что препятствует схлопыванию отступов
Отступы не будут объединяться, если элемент имеет абсолютное позиционирование или он плавающий. Но предположим, что вы столкнулись с ситуацией, когда случился один из описанных выше сценариев. Как вы можете предотвратить процесс коллапса отступов?
Первое, что способно остановить схлопывание, — наличие между соседними элементами еще чего-нибудь.
Например, отступы пустого блока не объединятся, если они будут иметь видимые границы. В приведенном ниже примере был добавлен внутренний отступ (padding) размером в 1 пиксель. Благодаря этому, 50-пиксельные внешние отступы не схлопнулись.
CSS:
.wrapper { border: 5px dotted black; } body { font: 1.4em/1.3 "Gill Sans", "Gill Sans MT", Calibri, sans-serif; margin: 2em 3em; } .box { background-color: rgb(55,55,110); color: white; border-radius: .5em; } .empty { margin: 50px 0 50px 0; padding: 1px; }
Вполне логично, что если блок ничего не содержит, не имеет видимых границ и отступов, то он и должен быть невидимым. Это может быть пустой элемент абзаца, помещенный в разметку вашего лендинга. Вы, вероятно, не хотели бы, чтобы это привело к появлению больших пробелов между другими абзацами из-за того, что их отступы сохраняются. Добавьте что-нибудь в пустой блок, и вы получите эти пробелы.
То же самое происходит и с отступами на первом или последнем дочерних элементах, которые схлопываются через «родителя». Если мы добавим границу к «родителю», отступы дочерних элементов сохранятся:
CSS:
.wrapper { border: 5px dotted black; } .box { margin: 50px; } body { font: 1.4em/1.3 "Gill Sans", "Gill Sans MT", Calibri, sans-serif; margin: 2em 3em; } .box { background-color: rgb(55,55,110); color: white; padding: 20px; border-radius: .5em; }
Этот сценарий тоже логичен: если наличие обертки вокруг элементов, которая не отображается визуально, важно для семантики, вы, возможно, не захотите, чтобы из-за нее между элементами образовывались большие пустоты. Однако в нем было больше смысла, когда основной формой контента в интернете был текст, но теперь, когда в дизайне используется много других элементов, уже менее полезен.
Создание контекста форматирования блока
Новый контекст форматирования блока (BFC) также предотвращает коллапс отступов в содержащемся элементе. Если еще раз взглянуть на пример с первым и последним дочерними элементами, чьи отступы выходят за пределы обертки, и задать свойству display этой обертки значение flow-root, создавая, таким образом, новый BFC, то отступы останутся внутри.
CSS:
.wrapper { outline: 1px solid red; display: flow-root; } .box { margin: 50px; } body { font: 1.4em/1.3 "Gill Sans", "Gill Sans MT", Calibri, sans-serif; margin: 2em 3em; } .box { background-color: rgb(55,55,110); color: white; padding: 20px; border-radius: .5em; }
Изменение значения свойства overflow на auto будет оказывать тот же эффект, так как это также создает новый BFC, хотя он может привести к появлению полос прокрутки, которые могут оказаться вам не нужны.
Контейнеры Flex и Grid
Контейнеры Flex и Grid устанавливают контексты форматирования Flex и Grid для своих дочерних элементов, так что те ведут себя в блочном макете несколько по-другому. К примеру, отступы в этих режимах верстки не схлопываются:
«Контейнер Flex устанавливает новый контекст форматирования для своего содержимого. Это то же самое, что и создание контекста форматирования блока, за исключением того, что вместо блочного макета используется гибкий макет. И в нем отступы контейнера и контента не объединяются друг с другом».
Если вернуться к примеру выше и сделать обертку во flex-контейнере, отображая элементы при помощи flex-direction: column, вы можете видеть, что отступы теперь заключены в обертку. Кроме того, отступы между соседними гибкими элементами не схлопываются, таким образом между этими элементами будет свободное пространство в 100 пикселей:
CSS:
.wrapper { outline: 1px solid red; display: flex; flex-direction: column; } .box { margin: 50px; } body { font: 1.4em/1.3 "Gill Sans", "Gill Sans MT", Calibri, sans-serif; margin: 2em 3em; } .box { background-color: rgb(55,55,110); color: white; padding: 20px; border-radius: .5em; }
Что выбрать для своего лендинга?
Из-за схлопывания отступов нужно заранее продумать стратегию работы с ними на своем лендинге. Самое простое, что можно сделать, — это определить отступы только верхней или нижней части элементов. Таким образом, вам не придется быть свидетелем частого margin collapse, потому как сторона элемента с отступом будет прилегать к стороне другого элемента без отступа. Конечно, этот вариант не позволит вам решить проблему объединения отступов дочерних элементов, но, честно говоря, она не так часто встречается.
Понимание того, почему это происходит, позволит вам оперативно находить подходящее решение: можете, к примеру, превратить родительский блок во flex-контейнер (с помощью display: flow-root, который создаст BFC), либо добавить однопиксельный padding.
Знать, почему отступы объединяются и не объединяются, крайне важно, ведь только так вы сможете эффективно разобраться в каждой конкретной ситуации. Что бы вы ни выбрали, обязательно поделитесь этой информацией со своей командой. Довольно часто причина схлопывания отступов окутана ореолом тайны, так что бывает не сразу понятно, как всему этому противостоять. Комментарий в коде поможет решить проблему более оперативно.
Процентная запись
Когда в CSS вы используете проценты, надо помнить, что это всегда процент от чего-то.
Поля (и отступы), заданные с использованием процентов, всегда будут в процентах от инлайнового размера (ширина в горизонтальном режиме записи) родителя. Это означает, что при использовании процентов размер поля (или отступа) будет одинаковым по всему элементу.
В примере ниже обертка шириной 200 пикселей, внутри которой находится блок, чьи отступы равны 10%. Это означает, что отступы равны 20 пикселям, потому что 10% от 200 — это 20.
CSS:
* { box-sizing: border-box; } .wrapper { border: 5px dotted black; width: 200px; } .box { background-color: rgb(55,55,110); color: white; padding: 20px; border-radius: .5em; margin: 10%; } body { font: 1.4em/1.3 "Gill Sans", "Gill Sans MT", Calibri, sans-serif; margin: 2em 3em; }
Отступы в ориентированном на поток мире
На протяжении всей статьи речь шла об отступах между соседствующими друг с другом по вертикали элементами, однако современный CSS заставляет смотреть на вещи в контексте потока. Поэтому, когда говорят о вертикальных отступах, говорят об отступах в размерности блока. Эти поля будут сверху и снизу, если мы находимся в горизонтальном режиме письма, или справа и слева, если в вертикальном.
При этом подходе проще говорить о начале блока и его конце, а не его верхе и низе. Для простоты CSS ввел спецификацию логических свойств и значений. Она сопоставляет относительные свойства потока с физическими словами позиционирования.
Для отступов это дает следующие сопоставления (если мы работаем на английском или в любом другом горизонтальном режиме записи с направлением текста слева направо):
margin-top = margin-block-start
margin-right = margin-inline-end
margin-bottom = margin-block-end
margin-left = margin-inline-start
Также появились два новых сокращения, которые позволяют устанавливать оба блока за раз или оба инлайн:
margin-block
margin-inline
В примере ниже были использованы ориентированные на поток «ключи», после режим записи был изменен, и вы можете видеть, как отступы следуют за направлением текста и не привязаны к физическому верху или низу.
Блок при горизонтальном и вертикальном режимах письма.
CSS:
* { box-sizing: border-box; } .wrapper { border: 5px dotted black; inline-size: 200px; } .horizontal-tb { writing-mode: horizontal-tb; margin-bottom: 1em; } .vertical-rl { writing-mode: vertical-rl; } .box { background-color: rgb(55,55,110); color: white; padding: 20px; border-radius: .5em; margin-block-start: 30px; margin-block-end: 10px; margin-inline-start: 2em; margin-inline-end: 5%; } body { font: 1.4em/1.3 "Gill Sans", "Gill Sans MT", Calibri, sans-serif; margin: 2em 3em; }
Заключение
Теперь вы знаете об отступах CSS чуточку больше. Вкратце перечислим основные моменты, описанные в статье:
- схлопывание отступов — это ситуация, когда меньший отступ вкладывается в больший. Понимание того, почему это происходит и как этому помешать, поможет вам решить любые проблемы, причиной которых может стать схлопывание;
- установка отступов в одном направлении — одно из наиболее простых и эффективных решений проблемы их коллапса;
- не забывайте делиться со своей командой найденными решениями и оставлять комментарии в коде;
- оставьте в прошлом физические слова позиционирования (снизу, сверху и т.д.), поскольку сеть становится все более нейтральной к режиму записи.
Инструмент «Произвольный HTML» панели редактора лендингов LPgenerator позволяет интегрировать дополнительные элементы (галереи, внешние формы, виджеты, плееры и т. п.) с применением сторонних кодов (например, JS). Вы можете вставить в свою страницу любой HTML-код.
Высоких вам конверсий!
По материалам: smashingmagazine.com