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

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

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

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

Примеры

// 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

Группирование CSS-правил в Media Queries и производительность

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

Из файла test.less

@tablets: ~"(min-width: 768px) and (max-width: 979px)";
@mobile: ~"(max-width: 767px)";

.widget {
  float: left;
  width: 25%;

  @media @tablets { float: none; width: 100%; }
  @media @mobile { display: none; }

  > .title {
    font-size: 18px;

    @media @tablets { font-size: 16px; font-weight: bold; }
  }

}

получаем test.css

.widget {
  float: left;
  width: 25%;
}
@media (min-width: 768px) and (max-width: 979px) {
  .widget {
    float: none;
    width: 100%;
  }
}
@media (max-width: 767px) {
  .widget {
    display: none;
  }
}
.widget > .title {
  font-size: 18px;
}
@media (min-width: 768px) and (max-width: 979px) {
  .widget > .title {
    font-size: 16px;
    font-weight: bold;
  }
}

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

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

  • Одинаковые последовательности символов отлично сжимаются при передаче с HTTP-сервера, если настроено сжатие файлов.
  • Отдельные блоки Media Queries почти не влияют на производительность браузера при рендеринге страницы.

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

Оставте свой комментарий