Заметки с тегом «bem»

Миксины для модификаторов блоков

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

Мне кажется, что это может быть подходящим решением в данной ситуации.

Примеры

// namespace
@block: ~".cool-widget";

// block
@{block} {
  // primary styles
  position: relative;
  &__title {
    font: bold 20px/1 sans-serif;
  }
  &__body {
    font: normal 14px/1.4 sans-serif;
    color: white;
  }
}

Добавим «тему», изменяющую оформление некоторых элементов.

@{block} {
  // red modifier
  &_red {
    @{block} {
      .cool-widget_red();
    }
  }
}

// red theme
.cool-widget_red() {
  &__title {
    color: red;
  }
  &__body {
    background: red;
  }
}

К одному модификатору можно подмешать несколько миксинов.

@{block} {
  // blue modifier
  &_blue {
    @{block} {
      .cool-widget_big-title();
      .cool-widget_blue();
    }
  }
}

А так же можно создать стили для «глобального» модификатора.

@{block} {
  // global modifiers
  .ie6 & {
    .cool-widget_ie();
  }
  .no-js & {
    .cool-widget_no-js();
  }
}

Исходный LESS-файл и результат — https://gist.github.com/mistakster/619b94f50ad7b6c861ba

Комментарии к заметке: 7

Декомпозиция блоков в стиле БЭМ

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

/* Плохой пример! Не повторяйте такого */
.block {
    &__element {
    }
    &__child {
        color: black;
    }
    &__element_active &__child {
        color: red;
    }
}

Если приглядеться внимательно к получившейся конструкции, то можно найти аналогию с модификатором блока. Так оно и есть. Опишем элемент как новый блок с именем block-element и всё встанет на свои места.

/* Хороший пример. Декомпозиция упростила
   взаимоотношения между элементами */
.block {
    &__element {
    }
}
.block-element {
    &__child {
        color: black;
    }
    &_active &__child {
        color: red;
    }
}

На одних и тех же «физических» DOM-элементах мы разместили два «логических» блока.

<div class="block">
    <div class="block__element block-element">
        <span class="block-element__child">Some text</span>
    </div>
</div>

Миксин для генерации Блока

<article class="block">
    <h1 class="block__title">
        <span class="block__title-wrap">My cool title</span>
    </h1>
    …
</article>

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

.cool-title(@color: yellow) {
    font-size: normal 24px/1 sans-serif;
    &-wrap {
        background: @color;
    }
}

.block {
    &__title {
        .cool-title();
    }
}

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

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

.cool-title(@color: yellow) {
    font-size: normal 24px/1 sans-serif;
    &__wrap {
        background: @color;
    }
}

.block-title {
    .cool-title();
}

Опять у нас на одних и тех же DOM-элементах прекрасно уживаются два блока.

<article class="block">
    <h1 class="block__title block-title">
        <span class="block-title__wrap">My cool title</span>
    </h1>
    …
</article>
Комментарии к заметке: 2

Генерация названий селекторов в стиле БЭМ с помощью препроцессоров

Методология БЭМ никогда не подразумевала того, что длинные названия классов в селекторах типа .block__element_modifier будут записываться вручную. В Яндексе для этого сразу используются специальные утилиты, которые генерируют стили и разметку блока. Если вы используете препроцессоры для генерации CSS, то вам тоже не нужно каждый раз повторять названия блока или элементов. Сейчас я расскажу, как это делается.

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

.header {
    .title {
        font: bold 24px/1 sans-serif;
    }
}

После трансформации получаем CSS:

.header .title {
    font: bold 24px/1 sans-serif;
}

Вместо «родительской части» селектора можно использовать символ &. Удобно, когда нужно применить свойства и выбранному элементу, и его потомкам или псевдо-классам.

.header {
    a {
        &, &:hover, &:focus {
            color: white;
            text-decoration: none;
        }
    }
}

CSS:

.header a,
.header a:hover,
.header a:focus {
    color: white;
    text-decoration: none;
}

LESS (а с недавнего времени и SASS) подставляют в новый селектор вместо & часть родительского селектора без каких-либо изменений. Так же нет ограничений на последующие или предыдущие символы в новом селекторе. Поэтому вместо каскада можно генерировать простые селекторы.

.header {
    &__link {
        &, &:hover, &:focus {
            color: white;
            text-decoration: none;
        }
    }
}

CSS:

.header__link,
.header__link:hover,
.header__link:focus {
    color: white;
    text-decoration: none;
}

Аналогично формируем простые селекторы для модификаторов элементов.

.header {
    background: white;
    &__title {
        font: bold 24px/1 sans-serif;
        &_featured {
            font-size: 30px;
        }
    }
}

CSS:

.header {
    background: white;
}
.header__title {
    font: bold 24px/1 sans-serif;
}
.header__title_featured {
    font-size: 30px;
}

Вложенные селекторы превратились в простые.

Важно знать

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

Название блока используется только один раз

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

.block {
    // стили блока
}

Элементы идут на втором уровне вложенности

.block {
    // стили блока
    &__element {
        // стили элемента
    }
    &__title {
    }
}

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

.block {
    &__element {
        &-wrapper {
            // Пример плохого элемента.
            // Его будет трудно отыскать.
        }
    }
}

Никогда не делайте подобного. Такой селектор очень трудно будет найти в коде. В примере название элемента element-wrapper разорвано на две части. Пишите название элементов полностью даже если они частично повторяют уже существующие.

Псевдо-классы, псевдо-элементы и модификаторы элементов допускается писать на третьем уровне вложенности

.block {
    &__element {
        &_modifier {
            // стили модификатора элемента
        }
        &_modifier_value {
            // модификатор со значением не разбиваем на части
        }
        &:hover {
            // псевдо-класс — это тоже модификатор
        }
    }
}

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

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

.block {
    &__element_active {
        // модификатор элемента на втором уровне
    }
    &__element_modifier_good {
        // пример модификатора со значением
    }
}

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

Модификаторы блока могут участвовать в каскаде и располагаются на втором уровне

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

.block {
    background: white;
    &__title {
        color: black;
        font: bold 24px/1 sans-serif;
    }
    &_featured {
        background: black;
    }
    &_featured &__title {
        color: white;
        font-size: 30px;
    }
}

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

.block {
    background: white;
}
.block__title {
    color: black;
    font: bold 24px/1 sans-serif;
}
.block_featured {
    background: black;
}
.block_featured .block__title {
    color: white;
    font-size: 30px;
}

Заключение

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

Эта статья была любезно переведена на английский язык Варей Степановой и опубликована во «Frontend Babel» — Generating BEM selectors with CSS preprocessors.

Комментарии к заметке: 15

Простые селекторы для разметки статьи в WordPress

Все браузеры начинают разбор CSS селекторов справа налево. Кстати, так же поступают и популярные библиотеки при поиске DOM элементов по заданному селектору.

Пусть в CSS файле, например, указан селектор section.main .article p { … }. Во время отрисовки проверяется, что текущий элемент p. Если это так, то браузер будет искать в дереве всех предков с классом article, а затем предков section с классом main. Только к элементам, для которых были найдены все требуемые предки, и будут применяться декоративные свойства.

Очевидно, что самые быстрые селекторы – это те, которые требуют меньше проверок: div, .main, #sidebar. Такие селекторы называются простыми. Каскадным селекторам (например, как в предыдущем случае) требуется гораздо больше времени для прохождения таких проверок. Так в БЭМ, по возможности, используются только простые селекторы или последовательности простых селекторов (например, a.menu__item-link:hover), чтобы обеспечить максимальную скорость отображения страниц.

С обычной вёрсткой проблемы добавить нужные классы не возникает. А вот для контента, создаваемого пользователем, такие классы придётся назначать автоматически. Для своего блога на Вордпрессе я написал небольшой фильтр.

function my_content_filter_callback($parts)
{
    $tag = strtolower($parts[1]);
    $attrs = $parts[2];
    $cls_replacement = ' class="t-' . strtolower($tag);

    if (strpos($attrs, ' class=') === false) {
        $attrs = $cls_replacement . '"' . $attrs;
    } else {
        $attrs = str_replace(' class="', $cls_replacement . ' ', $attrs);
    }

    return '<' . $tag . $attrs . '>';
}

function my_content_filter($content)
{
    if (!is_feed()) {
        $out = preg_replace_callback('#<([a-zA-Z0-9]+)([^>]*)>#',
            'my_content_filter_callback', $content);
    } else {
        $out = $content;
    }
    return $out;
}

add_filter('the_content', 'my_content_filter', 20);

Фильтр вызывается при отображении статьи. Каждому тегу в статье добавляется класс с именем тега. Например, <p> становится <p class="t-p">, а <div class="video"> становится <div class="t-div video">.

<h3 class="t-h3">Элемент article</h3>
<p class="t-p">Новый элемент <em class="t-em">article</em> — это специальный вид
    <em class="t-em">section</em>, который обозначает независимую и самодостаточную
    часть страницы. На его месте можно использовать <em class="t-em">section</em>,
    но <em class="t-em">article</em> добавляет больше семантического значения.</p>

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

.t-h3 { font-size: 1.5em; font-weight: bold; margin: 1em 0 0.5em; }
.t-p { margin: 1em 0; }
.t-em { font-style: italic; }

Подведу итоги:

  • небольшое увеличение объема HTML кода почти никак не сказывается на трафике для пользователя, так как по сети данные передаются в сжатом виде;
  • нет ручной работы по добавлению классов элементам;
  • при декорировании в CSS используются простые селекторы;
  • максимальная скорость при отрисовке страницы в браузере.
Оставте свой комментарий

Вёрстка независимыми блоками

Кажется, в крупных проектах рунета «БЭМ» становится стандартом де-факто.

Если хочется попробовать и получить некоторые плюсы от разработки в стиле БЭМ, но нет возможности внедрять всю технологическую цепочку, то стоит для начала перенять именование классов. Разумеется, это будет уже не БЭМ. Тем не мене, методом проб и ошибок ребятам из Яндекса удалось выработать хорошую методику именования CSS-селекторов для абсолютно-независимых блоков.

Блок

Канонические имена блоков в БЭМ начинаются с префикса b- и отвечают на вопрос «Зачем нужен это блок?»:

.b-menu {}

Я в своей практике префикс всегда опускаю, так как лично для меня – это лишние символы не несущие никакой смысловой нагрузки.

.menu {}

Элемент

Элемент – часть блока, которая не имеет смысла вне этого блока и не может быть использована вне него.

<nav class="menu">
    <h3 class="menu__head">Menu</h3>
    <ul class="menu__list">
        <li class="menu__item">Item 1</li>
        <li class="menu__item">Item 2</li>
        <li class="menu__item">Item 3</li>
    </ul>
</nav>

Селектор элементов именуем следующим образом: .имя-блока__имя-элемента.

Модификатор

Модификатор служит для изменения внешнего вида (возможно, и поведение) блока или элемента. Селектор записывается в виде: .имя-блока_тип-модификатора_значение-модификатора или .имя-блока__имя-элемента_тип-модификатора_значение-модификатора

Назначив тегу блока или элемента класс модификатора, можно изменить его внешний вид.

<nav class="menu menu_footer">
    <h3 class="menu__head">Menu</h3>
    <ul class="menu__list">
        <li class="menu__item">Item 1</li>
        <li class="menu__item menu__item_state_current">Item 2</li>
        <li class="menu__item">Item 3</li>
    </ul>
</nav>

Модификатор menu_footer у блока menu, например, меняет размер шрифта у меню в подвале страницы

.menu { font-size: 16px; }
.menu_footer { font-size: 12px; }

А модификатор menu__item_state_current у элемента menu__item может поменять цвет фона у текущего раздела

.menu__item { background: black; color: white; }
.menu__item_state_current { background: yellow; color: black; }

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

Дополнительные материалы

Комментарии к заметке: 17