Все, что вам нужно знать о свойстве margin в CSS

0
901
views

Перевод статьи «Everything You Need To Know About CSS Margins».

Когда мы начинаем изучать CSS, одной из первых мы осваиваем тему блочной модели. Свойство margin — часть этой модели. Это свойство определяет прозрачное пространство вокруг блока, отодвигающее от этого блока другие элементы. Свойства margin-top, margin-right, margin-bottom и margin-left были описаны еще в CSS1, наряду со свойством margin, позволяющим задать все отступы одновременно.

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

Блочная модель в CSS

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

Блочная модель это то, как разные части блока — содержимое (content), внутренние (padding) и внешние (margin) отступы, граница (border) — располагаются друг относительно друга и взаимодействуют между собой.

В CSS1 блочная модель была изображена в виде ASCII-диаграммы, как показано на иллюстрации:

Все четыре свойства margin (по одному для каждой стороны) и сокращенный вариант написания (просто margin) были определены еще в CSS1.

В спецификации CSS2.1 есть иллюстрация, показывающая блочную модель и также определяющая термины, которые мы все еще используем для описания различных блоков. В этой спецификации описаны content box, padding box, border box и margin box. Каждый из этих блоков определяется краями контента, внутреннего оступа, границы и внешнего отступа соответственно.

Сейчас у нас есть спецификация блочной модели в третьей версии. Определения блочной модели (и в частности внешних отступов) в ней такие же, как в CSS2, поэтому в нашей статье мы будем пользоваться последними.

Схлопывание внешних отступов

Спецификация CSS1, определяя внешние отступы, также определяла такое явление как схлопывание (collapse) вертикальных внешних отступов. С тех самых пор это поведение является причиной постоянного раздражения при работе с margin.

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

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

Внешние отступы схлопываются в разных ситуациях. Рассмотрим их по очереди, а затем разберемся, как предотвратить схлопывание.

Смежные блоки-сиблинги

(Вообще сиблинги — собирательное определение детей одних родителей, т. е., братья и сестры. Здесь имеются в виду одноранговые блоки, скажем, идущие подряд абзацы, — прим. перев.).

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

В примере, приведенном ниже, у нас есть три элемента div. У первого есть верхний и нижний margin в 50 px. У второго тоже есть такие отступы, но по 20 px. У третьего есть верхний и нижний margin по 3 em.

See the Pen Smashing: margins, adjacent siblings by rachelandrew (@rachelandrew) on CodePen.

В итоге расстояние между первым и вторым элементом — 50 px, потому что меньший margin в 20 px поглотился большим. А отступ между вторым и третьим элементами — 3 em, потому что 3 em больше, чем 20 px (нижний margin второго элемента).

Полностью пустые блоки

Если блок пустой, его внешние отступы (верхний и нижний) могут схлопнуться друг с другом. В следующем примере элемент с классом empty имеет верхний и нижний margin (по 50 px каждый). Но в итоге расстояние между первым и третьим элементами не 100 px, а 50 px. Так получилось, потому что верхний и нижний отступы схлопнулись. Если в этот пустой блок добавить что-нибудь (хотя бы даже внутренний отступ — padding), схлопывание внешних отступов не произойдет.

See the Pen Smashing: margin collapsing on an empty element by rachelandrew (@rachelandrew) on CodePen.

Родительский элемент и первый или последний потомок

Этот сценарий чаще всего застает людей врасплох, потому что он не то чтобы сильно интуитивен. В следующем примере у нас есть элемент div с классом wrapper. Также ему назначена красная обводка, чтобы его было лучше видно. Все три элемента-потомка имеют margin в 50 px (т. е., внешние отступы со всех сторон). Но в результате мы не получаем отступа от верхней и нижней границы родительского элемента.

See the Pen Smashing Margins: margin on first and last child by rachelandrew (@rachelandrew) on CodePen.

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

Схлопываются только вертикальные отступы

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

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

Что может предотвратить схлопывание внешних отступов

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

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

Например, отступы совершенно пустого блока не схлопнутся с отступами верхнего и нижнего блоков, если этот пустой блок имеет границу или внутренний отступ. В приведенном ниже примере я добавила в пустом блоке padding в 1 px. Теперь над ним и под ним появились внешние отступы в 50 px.

See the Pen Smashing: empty boxes with padding do not collapse margins by rachelandrew (@rachelandrew) on CodePen.

За таким поведением стоит определенная логика. Если блок совершенно пуст, не имеет обозначенных границ или внутренних отступов, он попросту невидимый. Это может быть, например, пустой абзац, добавленный в разметку вашей CMS. Если ваша CMS добавляет лишние элементы вроде абзацев, вы, вероятно, не захотите, чтобы из-за этого появлялись огромные промежутки между абзацами (а они появились бы благодаря отступам). Добавьте в пустой блок что угодно — и вы получите эти промежутки.

Аналогичное поведение мы можем наблюдать у отступов первого и последнего потомка, которые схлопываются с родительскими отступами. Если мы добавим границу (border) для родительского элемента, отступы элементов-потомков останутся внутри.

See the Pen Smashing Margins: margin on first and last child doesn’t collapse if the parent has a border by rachelandrew (@rachelandrew) on CodePen.

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

Создание блочного контекста форматирования

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

See the Pen Smashing Margins: a new block formatting context contains margins by rachelandrew (@rachelandrew) on CodePen.

Если хотите узнать больше о свойстве display: flow-root, почитайте статью “Understanding CSS Layout And The Block Formatting Context”. Изменение значения свойства overflow на auto даст тот же эффект. Это тоже создаст новый блочный контекст форматирования, хотя может привести к появлению полос прокрутки, а это порой нежелательно.

Контейнеры Flex и Grid

Flex и Grid-контейнеры устанавливают Flex и Grid-контексты форматирования для своих потомков, а потому их поведение будет отличаться от блочного макета. Одно из отличий — внешние отступы не схлопываются.

Если мы возьмем пример, который обсуждали выше, и превратим wrapper во flex-контейнер с отображением элементов с flex-direction: column, мы увидим, что теперь внешние отступы содержатся внутри обертки. Кроме того, внешние отступы между смежными flex-элементами не схлопываются друг с другом, в результате чего мы получаем расстояние в 100 px между ними (50 px нижнего отступа одного элемента плюс 50 px верхнего отступа другого).

See the Pen Smashing Margins: margins on flex items do not collapse by rachelandrew (@rachelandrew) on CodePen.

Стратегии использования margin на вашем сайте

Имея в виду схлопывание внешних отступов, стоит определить постоянный подход к работе с ними. Самый простой вариант — указывать внешние отступы только сверху или только снизу. Таким образом у вас не должны слишком часто возникать проблемы со схлопыванием, ведь сторона элемента с margin всегда будет примыкать к стороне другого элемента без margin.

Примечание. Гарри Робертс написал отличный пост о том, почему установка внешних отступов одной направленности это хорошая идея даже без учета проблем со схлопыванием.

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

Идеальным вариантом было бы при необходимости давать компонентам display: flow-root. В качестве запасного варианта для старых браузеров для создания нового блочного контекста форматирования можно использовать overflow, превращать родительский элемент во flex-контейнер или добавлять какой-нибудь пиксель внутреннего отступа. Не забывайте, что вы можете использовать функционал запросов для определения поддержки для display: flow-root — чтобы только старые браузеры получали менее оптимальное решение.

В большинстве случаев ключевым моментом в поиске решения является само понимание того, почему внешние отступы схлопываются (или не схлопываются). Понимая, как все это происходит, вы можете подобрать оптимальное решение для вашей ситуации.

Что бы вы ни выбрали, поделитесь этим с командой. Порой схлопывание отступов бывает делом загадочным, а меры, которые вы предпринимаете, чтобы его предотвратить, могут не быть очевидными для читателя кода. Помочь может оставленный комментарий (в нем вы можете даже поставить ссылку на эту статью, чтобы поделиться знаниями о схлопывании отступов).


И в завершение статьи давайте рассмотрим еще пару интересных вещей об отступах.

Указание значения margin в процентах

При использовании процентов в CSS всегда следует помнить о том, проценты от чего имеются в виду. Если вы задаете размеры margin и padding в процентах, это всегда проценты от inline-размера родителя (ширина в horizontal writing mode). Это означает, что при использовании процентов у вас будет одинаковый внутренний отступ со всех сторон элемента.

Рассмотрим пример. У нас есть обертка (wrapper) шириной в 200 px. Внутри — блок с margin: 10%. То есть, внешний отступ будет по 20 px со всех сторон, потому что 20 px это 10% от 200 px.

See the Pen Smashing Margins: percentage margins by rachelandrew (@rachelandrew) on CodePen.

Внешние отступы в «относительнопоточном мире»

В этой статье мы все время говорили о вертикальных отступах. Но в современном CSS появилась тенденция говорить о направлении в относительном ключе, а не в физическом смысле. Говоря о вертикальных отступах, мы на самом деле говорим об отступах в блочном измерении. Они могут быть верхними и нижними — если мы в horizontal writing mode (когда чтение идет слева направо и сверху вниз), но в vertical writing mode отступы становятся левыми и правыми.

Когда мы употребляем логические направления (относительно потока), нам проще говорить про начало и конец блока, а не про верх и низ. Чтобы упростить все это, 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

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

See the Pen Smashing margins: flow relative margins by rachelandrew (@rachelandrew) on CodePen.

Дополнительно по теме логических свойств и значений можно почитать вот эту статью или MDN.

Итоги

Теперь вы знаете большую часть того, что вообще стоит знать о внешних отступах! Кратко повторим:

  • Схлопывание отступов имеет большое значение. Понимание того, когда схлопывание происходит, а когда — нет, поможет вам решить любые связанные с ним проблемы.
  • Если вы будете задавать внешние отступы только для одного направления, это избавит вас от многих проблем со схлопыванием.
  • Как и во всех прочих случаях, следует делиться с командой теми решениями, которые вы приняли, и оставлять комментарии в коде.
  • Если вы будете думать о направлениях в блочном и строчном измерении, а не в физическом (верх/низ, лево/право), вам будет легче привыкнуть к тому, что веб становится независящим от направления письма.

ОСТАВЬТЕ ОТВЕТ

Please enter your comment!
Please enter your name here