Расстановка полей и отступов в CSS

    Box Model

    В этой статье я хотел бы рассказать, как правильно расставлять поля (padding) и отступы (margin) в CSS.



    Прежде всего давайте вспомним определение полей и отступов согласно спецификации W3C. В боксовой модели (box model) поля — это расстояние между контентом (content) и границей блока (border). А отступы это расстояние между границей блока и границей соседнего или родительского элемента.

    Таким образом, если граница и фон элемента не заданы, то нет разницы, использовать свойство padding или margin для задания отступов, но при условии, что ширина (width) и высота (height) элемента не заданы и не изменен алгоритм расчета размеров контента с помощью свойства box-sizing.

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

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

    News

    Это блок новостей news. Он состоит из заголовка, списка новостей и ссылки «Другие новости». Дадим им следующие названия классов: news__title, news__list и news__more-link.

    <div class="news">
        <h2 class="news__title">Новости</h2>
        <ul class="news__list">
            <li class="news__list-item">...</li>
            <li class="news__list-item">...</li>
            <li class="news__list-item">...</li>
        </ul>
        <p class="news__more-link"><a href="...">Другие новости</a></p>
    </div>
    


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

    .news {
        padding: 20px 25px;
    }
    


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

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

    Negative Margins

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

    Учитывая это, задаем для заголовка отступ снизу, а для ссылки «Другие новости» отступ сверху.

    .news__title {
        margin-bottom: 10px;
    }
    
    .news__more-link {
        margin-top: 12px;
    }
    


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

    .news__list {
        margin: 10px 0 12px 0;
    }
    


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

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

    Можно задать для каждой новости кроме первой отступ сверху, либо для каждой новости кроме последней отступ снизу. Первый вариант более предпочтителен, поскольку псевдоселектор :first-child был добавлен в спецификации CSS 2.1 и имеет более широкую поддержку, в отличие от псевдоселектора :last-child, который был добавлен только в спецификации CSS версии 3.0.

    .news__list-item {
        margin-top: 18px;
    }
    
    .news__list-item:first-child {
        margin-top: 0;
    }
    


    Таким образом, правильная расстановка полей и отступов позволяет гибко менять внешний вид любого блока без внесения изменений в стили и без нарушений в дизайне. Самое главное — определить, какие элементы блока являются основными (обязательными), а какие опциональными.

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

    Popup

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

    .popup__header + .popup__text {
        margin-top: 15px;
    }
    


    jsfiddle.net/onfv42mz/1

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

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



    Еще один нюанс, о котором не все знают, связан с вертикальными отступами между соседними блоками. В определении отступов, которое я приводил выше, сказано, что отступ — это расстояние между границами текущего и соседнего блока. Таким образом, если мы расположим два блока друг под другом и зададим одному из них отступ снизу в 30px, а другому отступ сверху в 20px, отступ между ними будет не 50px, а 30px.

    .block1 {
        margin-bottom: 30px;
    }
    
    .block2 {
        margin-top: 20px;
    }
    


    jsfiddle.net/j99btnc8

    Collapse Margins

    То есть произойдет наложение отступов, и отступ между блоками будет равен наибольшему отступу, а не сумме отступов. Этот эффект также называют «схлопыванием».

    Прошу заметить, что горизонтальные отступы, в отличие от вертикальных, не «схлопываются», а суммируются. Поля (padding) также суммируются.

    Зная о «схлопывании» отступов, мы можем использовать эту особенность в свою пользу. Например, если нам необходимо расставить отступы для заголовков и текста внутри статьи, то для заголовка первого уровня зададим отступ снизу в 20px, а для заголовка второго уровня отступ сверху 20px и снизу 10px, а для всех параграфов зададим отступ сверху 10px.

    h1 {
        margin-bottom: 24px;
    }
    
    h2 {
        margin-top: 24px;
        margin-bottom: 12px;
    }
    
    p {
        margin-top: 12px;
    }
    


    jsfiddle.net/n27fms7s/1

    Теперь заголовок h2 можно расположить как после заголовка h1, так и после параграфа. В любом случае отступ сверху не будет превышать 24px.

    Общие правила



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

    1. Если соседние элементы имеют одинаковые отступы, то лучше задавать их родительскому контейнеру, а не элементам.
    2. При задании отступов между элементами, следует учитывать, обязательный это элемент или опциональный.
    3. Для списка однотипных элементов — не забывать о том, что число элементов может варьироваться.
    4. Помнить о наложении вертикальных отступов и использовать эту особенность там, где она принесет пользу.
    Netcracker
    57,81
    международная компания-разработчик IT-решений
    Поделиться публикацией

    Похожие публикации

    Комментарии 42

      0
      БЭМ радует глаз самурая :)
        +1
        .news__list-item:not(:first-child) {margin-top: 18px;}
          +2
          Можно и так. )
          Или вот еще вариант:
          .news__list-item + .news__list-item {
              margin-top: 18px;
          }
          
            +1
            В этом случае у вас растет специфичность (specificity), что ухудшает парсинг CSS кода при создании CSSOM.

            .news__list-item:not(:first-child) в этом случае лучше чем .news__list-item + .news__list-item
              0
              Согласен. Я просто привел еще один вариант селектора.
                0
                специфичность обоих записей одинаковая
              0
              * + .news__list-item {margin-top: 18px;}
              

              Так короче, и специфичность меньше
                +1
                И проблем с чтением этой конструкции больше, особенно если понадобится удлинить эту цепочку.
                  +5
                  * — зло!
                    0
                    Аргументируйте, пожалуйста.
                      0
                      На стековерфлоу. Если коротко — это один из самых медленных селекторов из-за особенностей браузеров разбора селекторов.

                      Вдобавок, может простреливать в очень неожиданные места.
                        0
                        Во-первых, приводить в пример статьи 2009-го года, где рассматривается производительность IE7,8 — как минимум не очень корректно. В реальности хоть сколько-нибудь заметные проблемы с производительностью могут наблюдаться, наверное, в случае каскада, когда универсальный селектор стоит справа, например ".class * " (помним, что браузеры разбирают селекторы справа налево, даже по вашей ссылке это написано), и стили для данного селектора вызывают reflow страницы.

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

                        Вот статья 2014 года, показывающая, что на построение DOM и layout'а тратится значительно больше времени, чем на разбор селекторов.

                        Я это всё к чему — браузеры делают умные люди, и в наши дни они (браузеры) умеют оптимизировать отображение страничек намного лучше, чем во времена того же IE8. Если специально не стрелять себе в ногу, то сложность селекторов — это не первое, о чем стоит задумываться.
                        0
                        Звездочка говорит браузеру, что он должен еще разок пробежаться по всем элементам в DOM. Это всегда зло.
                          0
                          Ну, вообще говоря, это не совсем так. CSS-селекторы разбираются браузером справа налево, поэтому в случае с "* + X" браузер сначала найдет все элементы "X", а затем, увидев селектор соседнего элемента "+" — будет проверять только соседние элементы DOM'a (По моему скромному мнению, конечно. Я не разработчик браузеров, и не знаю как на самом деле всё работает).
                            0
                            Вот именно потому что вы не знаете как на самом деле и не знаете как будет завтра, хак с "* + X" использовать не стоит, и лучше опираться на стандарт, а не конкретную реализацию браузера
                          0
                          От чтения HTML с такими классами вытекают глаза.
                            0
                            Дело привычки. У меня уже не вытекают. :)
                            0
                            Наверное поэтому frontender.info/writing-efficient-css-selectors
                          0
                          Думаю в данном случае специфичность это хорошо, поскольку позволяет задавать разные отступы между разными элементами.
                            0
                            В вашем комментарии — да, но в комментарии MiXei4, на который я отвечал, конструкция
                            X:not(:first-child)
                            

                            делает ровно то же, что и предложенная мной
                            * + X
                            
                              +1
                              Может оно и короче и делает тоже самое, но имеет проблемы с методологией подхода (в рамках БЭМ, раз тут БЭМ), читаемостью и оптимизированностью (так как любое * заставит просмотреть все элементы), в то время как
                              .news__list-item:not(:first-child)
                              

                              будет работать только с уже выбранным массивом .news__list-item
                              Да и визуально сразу понятно что речь о .news__list-item в рамках которого любой элемент кроме :first-child
                                0
                                > так как любое * заставит просмотреть все элементы

                                Поскольку селекторы разбираются браузером справа налево, сначала браузер все равно выберет массив `.news__list-item` (самый правый элемент селектора). Так что на производительности это не отразится.

                                `*` — зло, когда он стоит справа в селекторе.
                                  0
                                  Ждал именно этого замечания. Да, сейчас браузер разбирает справа налево, но в случае с "* + X" нет гарантии, что браузер не преобразует этот селектор перед разбором в «X + *» для каких-то своих оптимизаций
                                  Так же как нет гарантий, что следующее поколение движков не начнет разбирать селекторы слева направо

                                  Тоесть использование "* + X" это хак, который может перестать работать, поэтому я и написал, что есть проблема в том числе и с оптимизированостью, потому что условно браузер должен просмотреть все элементы, но так как мы знаем, что браузер разбирает справо налево, то мы абузим «недокументированную» фичу, если продолжаем так делать
                                    0
                                    Честно говоря, впервые слышу, что браузер разбирает селекторы справа налево и меня это сильно удивляет. На практике часто использую такую технику для обнуления отступа последнего блока внутри компонента:
                                    .box {

                                    &__item-1 { margin-bottom: 30px }
                                    &__item-2 { margin-bottom: 40px }
                                    &__item-3 { margin-bottom: 25px }

                                    > *:last-child {
                                    margin-bottom: 0;
                                    }
                                    }

                                    Выходит, в этом случае, браузер сначала найдёт все last-child теги на странице, а затем уже будет проверять какой из них находится внутри .box? Разве это имеет смысл?
                                      0
                                      Сначала найдёт все элементы, потом из них выберет те, которые внутри .box, потом от них возьмет :last-child
                                      Как будет на самом деле — неизвестно, движок css современных браузеров загадка, до тех пор, пока кто нибудь не возьмется провести актуальное исследование

                                      Одно ясно точно, если используется .box > *:last-child, и хочется привести к более БЭМ подходу, то есть несколько вариантов, например использование модификаторов:
                                      <div class="box">
                                        <div class="box__item box__item_1">item1</div>
                                        <div class="box__item box__item_2">item2</div>
                                        <div class="box__item box__item_3">item3</div>
                                        <div class="box__item box__item_1">item4</div>
                                      </div>
                                      


                                      .box {
                                      &__item_1 { margin-bottom: 30px }
                                      &__item_2 { margin-bottom: 40px }
                                      &__item_3 { margin-bottom: 25px }
                                      &__item:last-child { margin-bottom: 0; }
                                      }
                                      
                                        0
                                        справа налево, сначала найдет все ластчайлды данного класса.
                                        э
                          +2
                          В свое время пока не прочел книгу
                          CSS. Каскадные таблицы стилей. Эрика Мейера, часто мучился при верстке. Из тех книг что читал нигде не было указано что входит в высоту и ширину блока.
                            +2
                            Думаю любой верстальщик, должен это знать, для себя ничего нового, разве, как напоминание, за что в принципе тоже можно сказать спасибо!
                              0
                              Про простую расстановку магических цифр, пожалуй что, каждый верстальщик и знает. А вот вдумчивый подход очень важен, у меня, например, он с опытом пришел, далеко не сразу, а если вот так хорошо объясняют — так это же замечательно.
                              –12
                              Какой идиот назвал Веб технологии ТЕХНОЛОГИЯМИ? Когда до сих пор всё делается руками. В CSS нет поддержки иерархии дисплей листов блоков как во флеше и никакого это не волнует. Не волнует отсутствие стандартных JSON подобных форматов с многострочными текстами, ссылочности и бинарности. Всем по прежнему нравится рукоблудить по клавиатуре. Чем занимаются Ярмо и Гугл непонятно?
                                +1
                                Понравился подход с обязательностью/опциональностью блоков для установки отступов. Вроде мысль на поверхности, но как схема работы в голову раньше не приходила. Пригодится, спасибо.
                                  +5
                                  Спасибо за хорошо написанную статью. Стоит так же отметить, что схлопывания еще часто называют margin collapse.
                                    0
                                    Наглядное и простое изложение, спасибо! С переводом терминоголоией margin и padding не везло раньше, было много путаницы.

                                    +100500:
                                    В боксовой модели (box model) поля — это расстояние между контентом (content) и границей блока (border). А отступы это расстояние между границей блока и границей соседнего или родительского элемента.
                                      +2
                                      Вот как раз с переводом здесь всё плохо. Впрочем, как и практически везде.

                                      CSS свою терминологию взял не с воздуха, а из сложившийся области технологии, типографики. И в типографике «поле» — это margin, а никак не «padding». Аналогия с полями страницы, которые всегда пусты. Переводить padding как «поле» — верх неграмотности и только всех запутывать.

                                      В свою очередь свойство padding нужно чтобы отбивать содержимое от рамки. Название происходит от глагола to pad — отбивать. Переводится, соответственно: «отбивка».

                                      Далее, collapsing в английском имеет несколько смыслов, но тут явно имеется в виду не уничтожение или разрушение, а другой: складывание. Так же как складываются раскладушка или складной стул (синоним folding). Соответственно, смежные поля элементов складываются в одно, «поглощая» друг друга.

                                      Что интересно, сколько бы я не употреблял правильные термины, никогда не было, чтобы меня поняли неправильно. И они гораздо понятнее абстрактных «отступов». (— Подожди-подожди, какой отступ? Который маржин или паддинг?)

                                      Итого, правильный перевод:
                                      · margin — поле,
                                      · padding — отбивка,
                                      · margin collapsing — сложение полей.
                                        0
                                        «Сложение» — тоже недвусмысленный термин, его можно неправильно понять, по крайней мере в русском языке.
                                        Может быть, лучше «поглощение» полей?
                                          0
                                          С точки зрения корректности перевода вы правы. Но термин «отбивка» слишком типографский, я ни разу не слышал чтобы его употребляли верстальщики. В основном говорят «поля», «отступы», иногда «внешние поля».
                                          Термин «сложение полей» не подходит, поскольку поля как раз не складываются, а берется максимальное значение. Тогда уж «наложение полей».
                                            0
                                            Супер, спасибо!
                                          +1
                                          ИМХО, следует упомянуть еще и про такую полезнейшую вещь, как box-sizing, которая многих выручала при выходе элементов с width: 100% за пределы родительского элемента с padding-ом.
                                          Извините, у меня крайняя степень слепоты… А за статью спасибо, толково и подробно :)
                                            0
                                            Спасибо за статью, мне б это руководство, когда учился верстать)
                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                0
                                                Дополню, уточню.

                                                1. Рекомендую явно указывать вертикальные margin для каждого элемента, который в них нуждается, полагаться на влияние margin от соседнего элемента опасно, особенно при работе с пользовательским контентом.

                                                Я для этого использую миксин:

                                                @mixin ritm($valueTop, $valueBottom:$valueTop) {

                                                @if $valueTop != 0 {
                                                @include not(':first-child') {
                                                margin-top: $valueTop;
                                                }
                                                }

                                                @if $valueBottom != 0 {
                                                @include not(':last-child') {
                                                margin-bottom: $valueBottom;
                                                }
                                                }
                                                }

                                                2. Не стоит обеспечивать отступ между соседними блоками за счёт отступов дочерних элементов.

                                                3. А отступы это расстояние между границей блока и границей соседнего или родительского элемента.
                                                Верно, но тут есть тонкий момент. Стоит сделать оговорку, что margin является неотъемлемой частью блока, хоть и воспринимается как нечто попутное. Вопрос в том, что размеры блока, а конкретно его ширина, не всегда одно и тоже, что значение свойства width. Width — это ширина части бокса от border до border, либо размер контентной области в зависимости от боксовой модели, но размеры блока остаются неизменные, это стоит понимать.

                                                Each box has four edges: the margin edge, border edge, padding edge, and content edge.
                                                developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model

                                                В ней у каждого бокса есть 4 области: margin (внешние отступы), border (рамка), padding (внутренние поля), и content (контент или содержимое).
                                                developer.mozilla.org/ru/docs/Web/CSS/box_model

                                                Each box has a content area (e.g., text, an image, etc.) and optional surrounding padding, border, and margin areas;
                                                www.w3.org/TR/CSS2/box.html#box-dimensions

                                                4. Таким образом, если граница и фон элемента не заданы, то нет разницы, использовать свойство padding или margin для задания отступов, но при условии, что ширина (width) и высота (height) элемента не заданы и не изменен алгоритм расчета размеров контента с помощью свойства box-sizing.
                                                Для полноты картины, есть случаи, когда одинаковые значения margin и padding, даже при выше описанных условиях, производят различный результат, например margin: auto; ≠ padding: auto; codepen.io/matovas/pen/KzyLJZ

                                                5. Отступы же всегда задаются снаружи элемента.
                                                Перефразируем, «Отступы (margin) в обеих боксовых моделях (border-box и content-box), не влияют на значение width блока.»

                                                6. Тонкости перевода, холивара ради,
                                                — граница блока ≠ border блока, бордер — это border, а граница блока — это граница/край блока
                                                — block area = область блока (читай размеры блока), но
                                                — block area ≠ области ограниченной border'ом.
                                                Тут стоит оговориться, наука наукой, а ежедневная работа вносит свои правки в лексикон разработчиков.
                                                  0
                                                  Спасибо за дополнения. Можете про первый пункт подробнее объяснить? Желательно с примерами вызова миксина.

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

                                                Самое читаемое