Комментарии 44
И обнулять, как правило, ничего не приходится, т.к. отступ у блока сверху обычно больше, чем между его элементами. Его тоже прописываем как margin-top. Результирующий будет только один, который больше.
Сколько сложностей. Проще же элементам списка дать половинный margin, а контейнеру половинный padding.
ul {
padding: .5rem;
}
li {
margin: .5rem;
}
У любой строки текста из-за line-height будет отступ сверху и снизу, который вы никак не уберёте (вариант с line-height=1 как правило не катит ибо не дружит с переносом текста). То есть вам в любом случае придётся учитывать, что у некоторый блоков будут симметричные отступы сверху и снизу. Поэтому лучше сразу всё и делать на симметричных отступах. Это и кода меньше займёт и лучше дружит с переносами блоков на новую строку.
.block__item + .block__item {
margin-top: 1em;
}
Никаких last-child и прочего. Нужно придерживаться принципа, что стили должны как можно реже отменяться и переопределяться. То есть вместо «задать всем, а потом последнему отменить» нужно действовать по методу «задать сразу тем, кому нужно». Это и сокращение количества стилей (меньше рябит в глазах от каскада в девтулс), и семантически правильнее.
Надо было в cms для лендинга завести поле для подзаголовка. Все шло нормально, прописала в шаблоне, что надо выводить поле с таким-то именем, и обернула в тег
<h2 class="subtitle"></h2>
Смотрю у подзаголовка нарисовались отступы, хотя все стили скинуты ресетом. В стилях класса «subtitle» я ни чего подобного не писала. Стерла «subtitle», а отступы есть. Поменяла на «h3» — все норм. Начала более внимательно просматривать какие стили применяются и нашла что-то вроде: h1 + h2 { margin: 10px;}
Отступы идут в направлении потока дом дерева, блок сам себя не толкает.
Единственный аргумент в пользу первого пункта и не выдерживает никакой критики.
Блок себя не толкает, а вот элемент блока (как например элемент списка, где сам список является блоком) вполне себя толкает, в зависимости от своего контекста, а именно в зависимости от того что за элемент находится перед ним. В CSS нельзя задать зависимость от последующих элементов, но можно от предыдущих, в том и смысл задавать в таких случаях margin именно что левый и верхний.
Кстати о смысле, какой смысл в том что элемент толкает что-то от себя вправо или вниз? Не особо большой, если вообще есть, не находите? Не чувствуется ли куда ли больший смысл в том что элемент может отталкиваться от чего-нибудь? Почувствуется, если задуматься ненадолго. В каком коде больше смысла, как думаете?
1.
.list {}
.list__item {
// ... whatever else
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
2.
.list {}
.list__item {
// .. whatever else
& + & {
margin-top: 10px;
}
}
Не чувствуется ли куда ли больший смысл в том что элемент может отталкиваться от чего-нибудь?
А кроме того, элемент остается нейтральным и отталкивается только при определенных условиях.
Тем не менее даже в рамках набора правил взаимодействий элементов в блоке между собой, задание именно что отступа элемента от предыдущего имеет больше семантического смысла нежели задание отступа от элемента для последующих за ним (с обнулением, в случае если он последний или без него).
Кроме того, даже если не брать во внимание семантический смысл, чисто технически задание отступа в зависимости от предыдущего элемента дает значительно большие возможности и свободу стилизации без необходимости хаков/усложнений или изменений в верстке. Вот, например, ситуация:
.list__item {
& + & {
margin-top: 2px;
}
&--big + & {
margin-top: 4px;
}
& + &--big {
margin-top: 10px;
}
&--big + &--big {
margin-top: 20px;
}
}
У элемента отступ от предыдущего 2 или 4 пикселя, в зависимости от того, что это за предыдущий элемент:
list__item
или list__item--big
. И у элемента отступ от предыдущего 10 или 20 пикселей, в случае если этот элемент сам list__item
или list__item--big
, плюс аналогично все так же есть зависимость от вида предыдущего элемента.Получились, так сказать, правила взаимодействий.
А теперь как реализовать данные отступы, если мы будем задавать нижний отступ и что-нибудь обнулять? Никак. Нижние отступы и обнуления это просто-напросто недостаточный набор инстументов для реализации в данном случае. При этом контекстно зависимые верхние отступы в данном случае более чем достаточный набор инстументов.
Однако ситуаций много разных, требования к проекту и верстке тоже везде разные, где-то нужна поддерживаемость, где-то нужно как можно проще или как можно непробиваемее, не везде возможно сочетать все плюсы и обойтись без минусов, где нужны именно что нижние отступы по о-очень замысловатым причинам, где-то комбинация их или даже супер-замысловатая и сложноподдерживаемая комбинация. Тем не менее, несмотря на все многообразие возможных ситуаций, если говорить о наиболее предпочтительном способе задания отступов для большинства случаев — это именно что отступы вверх и влево. Преподанный же автором «известный стандарт» задания отступов является несомненно известной вещью, вот только не стандартом, а крайне часто применяемым и крайне редко уместным или предпочтительным методом задания отступов, приводящим к более серьезным ошибкам верстки и более сложному процессу их исправления.
& + & {
margin-top: 2px;
}
&--big + & {
margin-top: 4px;
}
& + &--big {
margin-top: 10px;
}
&--big + &--big {
margin-top: 20px;
}
Для тех, кто собирает имена классов по кусочкам (что в css, что в js) — есть отдельный котёл в аду.
А чем модификаторы хуже?
:last-child
, :first-child
и тому подобные делают ровно ту же самую работу что и модификаторы в большинстве случаев, однако, я уверен, что вы не будет выступать против собирания конструкций с псевдоклассами вот такого рода: .list__item {
&:first-child {
// .. whatever
}
}
Так что вопрос тут действительно в том «а чем модификаторы хуже?», только относится он к псевдоклассам — чем модификаторы хуже псевдоклассов? Чем они хуже, что за их собирание через
&--
нужен отдельный котел в аду?Я избегаю таких конструкций из-за того, что они не ищутся поиском.
Например: хочу в коде найти где определяется правило для класса .list__item--big, и не найду его. А если я новый человек на проекте и еще не изучил особенности кода, то это может стать причиной бага.
Мне понравился доклад Вадима Макеева на эту тему.
В данном случае вопрос был в использовании оператора
&
для сборки модификаторов. При использовании чистого css вместо scss не поменяется ни порядок задания правил, ни их логика, только синтаксис и единственный уровень вложенности. И это как раз та ситуация, где когнитивная нагрузка от дополнительного уровня абстракции в виде синтаксиса scss является достаточно малой, для того чтобы плюсы от его наличия перевешивали и снижали суммарный уровень нагрузки.Грубо говоря плюсов настолько больше по сравнению с минусами конкретно в ситуации с оператором
&
и модификаторами с псевдоклассами, что практически не бывает ситуаций где чистый css выиграл бы по сравнению с этим. По поддерживаемости, пониманию кода, написанию кода и т.д… Одно, пожалуй, требование — от человека работающего с этим кодом будет ожидаться знание и понимание css. Неважно новый он на проекте и видел ли sass до этого.
Ну и так уж и быть, еще условие — отсутствие усложнений и маразмов в остальных местах проекта, будь то стили, скрипты или верстка, хоть сколько-нибудь имеющие отношение к этим самым классам. Если человек пишет так, что потом непонятно что происходит, то сборка модификаторов через
&--
будет лишь еще одним его инструментом для усложнения. Ничего с этим не поделать. Без этого инструмента он будет использовать другой, и неизвестно хуже станет от другого инструмента или лучше. Поэтому я предлагаю этот момент все-таки на учитывать в рассуждении на эту тему. И тогда станет яснее видно явное превосходство одного подхода над другим.Специально для вас, кусочек типичного кода, который получается у средней руки программиста при попытке следования БЭМ-у и экономии на нажатиях клавиш (ведь именно в этом основное преимущество &, да?)
.panel-report
min-width 700px
height 100%
&__head
justify-content center
align-content center
min-height 40px
margin 10ps
padding 10px
background $panel-head
&__head-title
color $panel-title
text-align left
max-width 50%
padding 10px
overflow hidden
& > h1
font-weight normal
white-space nowrap
padding 10px
margin 0
overflow hidden
text-overflow ellipsis
font-size: 20px
&__wrapper
width 100%
display flex
&--center
.bench__blocks
justify-content center
&__blocks
width 100%
display flex
&__block
margin 10px
border 1px solid $panel-border
border-radius 4px
background $panel-block-bg
font-size 14px
max-width 50%
flex 1
display flex
flex-direction column
&__title
font-size 16px
overflow hidden
flex 0 0 60px
display flex
align-items center
&--info
cursor pointer
user-select none
&:hover
background $hover-bg
&:before
content '['
font-family monospace
margin-left -1ch
&:after
content ']'
font-family monospace
margin-right -1ch
&--text
line-height 20px
font-weight 600
padding 10px
color $panel-text
text-align center
overflow hidden
text-overflow ellipsis
flex 1 1 auto
&__content
padding 8px
background $panel-value-bg
margin 12px
margin-top 0
border 1px solid $panel-border
border-radius 4px
display flex
flex-direction column
flex 1 1 auto
&__entity
line-height 25px
height 25px
color $panel-text
& > th
font-weight normal
&__entity-head
vertical-align middle
&__entity-row
vertical-align middle
& > em
font-style normal
&__power
width 20px
padding 0 6px
&:after
content '.'
&__pos
min-width 34px
text-align right
padding 0 6px
&__name
text-align right
white-space nowrap
text-overflow ellipsis
overflow hidden
font-size inherit
max-width 180px
&__val
text-align right
font-size inherit
padding-left 8px
&__br
height 25px
width 1px
display block
background $panel-border
&__bar
background-color $panel-bar-blue
border none
margin 0
height 15px
float left
&.oil
background-color $panel-bar-orange
&__marker
position relative
margin auto
cursor pointer
flex 0 0 46px
&__marker-indicator
width: 34px;
height: 34px;
position: relative;
background-size: 34px 34px
Все имена изменены, все совпадения случайны.
Сколько времени вам понадобится, чтобы найти место объявления стилей для следующего селектора?
.panel-report__title--info:hover:before
И 2 встречных вопроса:
1. Как часто приходится искать такие селекторы?
2. Если писать всё полностью, то зачем вам препроцессор в принципе? Проще нативный CSS использовать.
10 секунд — довольно быстро для "дойти до нужной директории, найти нужный файл, найти нужное место в файле не запутавшись в контекстах".
А были бы селекторы целиком — вы бы нашли искомое за пару секунд через ctrl+shift+f.
Когда правишь баги вёрстки — очень часто. Когда новый код пишешь, конечно ничего искать не надо.
- Так многие (и я в том числе) уже отказались от препроцессоров в пользу css-next.
Нет, конечно, когда я работаю с каким-то блоком, я сразу ищу и открываю соответствующий ему файл. И дальше он у меня перед глазами, элементы и состояния ищутся легко.
Применимость css-next для меня не вполне понятна, потому что с одной стороны в ней нем многих реально нужных вещей (которые есть в препроцессорах), но зато есть много очень передовых (но не так уж необходимых) рюшечек из будущих спецификаций и костыльный синтаксис вложенности.
Экономию нажатий я не приводил в качестве аргумента и не считаю экономию нажатий аргументом, имеющим сколько-нибудь существенный вес для его учета в абсолютном большинстве контекстов.
Тот код, что вы привели в качестве примера ужасного кода, действительно ужасен по многим параметрам и это очень печальная ситуация, если «программист средней руки» пишет стили таким образом.
А как вам вот такая переработка этого же кода? Видите ли вы в нем существенные минусы из-за собирания модификаторов через
&--
, и сложно ли в нем будет найти стили селектора .panel-report__title--info:hover:before
?.panel-report {
min-width: 700px;
height: 100%;
}
.panel-report__head {
justify-content: center;
align-content: center;
min-height: 40px;
margin: 10ps;
padding: 10px;
background: $panel-head;
}
.panel-report__head-title {
color: $panel-title;
text-align: left;
max-width: 50%;
padding: 10px;
overflow: hidden;
& > h1 { // ! Спорное решение
font-weight: normal;
white-space: nowrap;
padding: 10px;
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
font-size: 20px;
}
}
.panel-report__wrapper {
width: 100%;
display: flex;
&--center {
.bench__blocks { // ! Здесь явно пошло что-то не так в БЭМ
justify-content: center;
}
}
}
.panel-report__blocks {
width: 100%;
display: flex;
}
.panel-report__block {
margin: 10px;
border: 1px solid $panel-border;
border-radius: 4px;
background: $panel-block-bg;
font-size: 14px;
max-width: 50%;
flex: 1;
display: flex;
flex-direction: column;
}
.panel-report__title {
font-size: 16px;
overflow: hidden;
flex: 0 0 60px;
display: flex;
align-items: center;
&:hover {
// Добавлено для иллюстрации взамного расположения
}
&:before {
// Аналогично
}
&:after {
// Аналогично
}
&--info {
cursor: pointer;
user-select: none;
&:hover {
background: $hover-bg;
&:before {
content: '[';
font-family: monospace;
margin-left: -1ch;
}
&:after {
content: ']';
font-family: monospace;
margin-right: -1ch;
}
}
}
&--text {
line-height: 20px;
font-weight: 600;
padding: 10px;
color: $panel-text;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
flex: 1 1 auto;
}
}
.panel-report__content {
padding: 8px;
background: $panel-value-bg;
margin: 12px;
margin-top: 0;
border: 1px solid $panel-border;
border-radius: 4px;
display: flex;
flex-direction: column;
flex: 1 1 auto;
}
.panel-report__entity {
line-height: 25px;
height: 25px;
color: $panel-text;
& > th {
font-weight: normal;
}
}
.panel-report__entity-head {
vertical-align: middle;
}
.panel-report__entity-row {
vertical-align: middle;
& > em { // ! Нужен ли здесь > ?
font-style: normal;
}
}
.panel-report__power {
width: 20px;
padding: 0 6px;
&:after {
content: '.';
}
}
.panel-report__pos {
min-width: 34px;
text-align: right;
padding: 0 6px;
}
.panel-report__name {
text-align: right;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: inherit;
max-width: 180px;
}
.panel-report__val {
text-align: right;
font-size: inherit;
padding-left: 8px;
}
.panel-report__br {
height: 25px;
width: 1px;
display: block;
background: $panel-border;
}
.panel-report__bar {
background-color: $panel-bar-blue;
border: none;
margin: 0;
height: 15px;
float: left;
&.oil { // ! Что-то здесь не то
background-color: $panel-bar-orange
}
}
.panel-report__marker {
position: relative;
margin: auto;
cursor: pointer;
flex: 0 0 46px;
}
.panel-report__marker-indicator {
width: 34px;
height: 34px;
position: relative;
background-size: 34px 34px;
}
Не удивляйтесь только отступам, это немного другой момент, хоть и частично связанный.
И конкретно это место в коде можно написать несколькими вариантами в рамках все тех же правил, но уже это не касается модификаторов, а только сборки сочетаний :before и :hover. Вот, например, один из них:
&--info {
cursor: pointer;
user-select: none;
&:before,
&:after {
display: none;
font-family: monospace;
}
&:before {
content: '[';
margin-left: -1ch;
}
&:after {
content: ']';
margin-right: -1ch;
}
&:hover {
background: $hover-bg;
&:before,
&:after {
display: block;
}
}
}
На практике искать определения классов (имеется в виду по всему проекту) приходится на пару порядков реже, чем писать. Так что лучше вынести общее имя блока за скобки, чем городить его несколько десятков раз, создавая почву для опечаток и ошибок копипаста.
Да и не нужно быть семи пядей во лбу, чтобы догадаться поискать по имени блока без модификатора — эта мысль приходит на автомате любому человеку, хоть сколько-нибудь понимающему БЭМ. Новый человек, не новый — не играет роли в данном случае. В самом крайнем случае — спросить!
Список здесь представлен как простой пример, так как сложный в разы дольше разбирать.
Забудьте про список и про равные итемы.
Возьмем для примера это:
![](https://habrastorage.org/webt/ik/9o/or/ik9oord67nsuj1v03hwphybjy5w.png)
Отступ от заголовка к дате 20 пикселей.
Или для вас отступ от даты к заголовку 20 пикселей?
Если снести эту дату.
То отступ от заголовка станет уже 30 пикселей или для вас такой расклад приемлем? никогда не приходилось потом подгонять нужный отступ?
Судя только по картинке и не зная бизнес-логику и другие возможные комбинации элементов в этом блоке, могу предположить разбиение на блоки и отступы примерно вот так:
![image](https://habrastorage.org/getpro/habr/comment_images/802/0c8/572/8020c8572a919b2af98bd350033e554d.png)
1. Заголовок — никаких отступов никуда
2. Дата+Статус, назовем этот блок Контент — никаких отступов никуда, но если Контент идет после Заголовка, то делаем отступ вверх
3. Чат — никаких отступов никуда, но если идет после Контента, то отступ до Контента тот что на скриншоте. Если возможна такая ситуация что Контента нет, а Чат есть, то мы легко можем дописать отступ вверх от Чата до Заголовка
А вот что происходит внутри Контента и какие там между собой отступы у элементов внутри уже дело самого блока Контент, но принцип остается тот же.
Понимаете теперь что смысл ищется исходя из, так сказать, бизнес-логики блоков, и что отступам желательно реализовывать именно что бизнес-логику, а не выравнивание по дизайну? (За выравнивание по дизайну отвечает размер отступа, а не факт его наличия/отсутствия и не условие его наличия/отсутствия)
Никогда не приходилось увольнять крикоруких дизайнеров, которые лепят отступы от балды?
.
Конечно можно прописать p + h2{...}, p + p{...}, но тогда ваши стили будут ужасны для всех.
Причем это все можно стандартизировать.
Или может быть уже есть таковые стандарты?
Сколько раз сталкивался с проблемами padding и margin, особенно когда несколько человек делают front-end. Впору брошюру делать )
css-tricks.com/centering-css-complete-guide
Организация отступов в верстке (margin/padding)