Перейти к содержанию

Использование примесей

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

В отличие от других CSS-препроцессоров, в Less любой объявленный селектор может использоваться как примесь. Примесь (от англ. mix-in) — набор свойств и селекторов, расширяющий поведение другой сущности (селектора).

Рассмотрим типичное объявление, которое уже миллионы раз встречалось в вашем CSS-коде:

.bordered {
  border-top: dotted 1px #333;
  border-bottom: solid 2px #333;
}

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

.article {
  .bordered;
  color: #443d3d;
}

После компиляции селектор .bordered безвозмездно отдаст все свои свойства и вложенные правила селектору .article. При этом в конечном CSS-файле будут объявлены оба этих селектора.

.bordered {
  border-top: dotted 1px #333;
  border-bottom: solid 2px #333;
}
.article {
  border-top: dotted 1px #333;
  border-bottom: solid 2px #333;
  color: #443d3d;
}

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

.bordered() {
  border-top: dotted 1px #333;
  border-bottom: solid 2px #333;
}

.article {
  .bordered;
  color: #443d3d;
}

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

.article {
  border-top: dotted 1px #333;
  border-bottom: solid 2px #333;
  color: #443d3d;
}

Неважно, будут ли указаны скобки при вызове примеси (.bordered();) или нет (.bordered;) — результат будет одинаковым. Однако, я бы советовал их указывать из-за того, что примеси могут иметь параметры.

Примеси с параметрами

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

.bordered(@_color) {
  border-top: dotted 1px @_color;
  border-bottom: solid 2px @_color;
}

.article {
  .bordered(#ccc);
  color: #443d3d;
}

Такая конструкция напоминает функции в JavaScript или других языках программирования, но на деле ей не является, так как работает скорее как макрос.

.article {
  border-top: dotted 1px #cccccc;
  border-bottom: solid 2px #cccccc;
  color: #443d3d;
}

Параметров может быть неограниченное число, главное — чтобы они помещались на вашем экране и были читаемы.

Разделять параметры при объявлении примеси и её вызове можно запятыми (@a, @b), либо точкой с запятой (@a; @b).

Пример 3.4.1

В этом примере отображена возможность работы с вложенными селекторами внутри примеси.

.clearfix() {
  &:before,
  &:after {
    display: table;
    content: '';
  }

  &:after {
    clear: both;
  }
}

.navbar {
  .clearfix();
}

Ничего необычного, получен вполне ожидаемый результат:

.navbar:before,
.navbar:after {
  display: table;
  content: '';
}
.navbar:after {
  clear: both;
}

Замечу ещё раз, что из-за указанных скобок у селектора .clearfix, соответствующий класс не будет объявлен после компиляции.

Значения параметров по умолчанию

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

.bordered(@_color: #ccc) {
  border-top: dotted 1px @_color;
  border-bottom: solid 2px @_color;
}

.article {
  .bordered();
  color: #443d3d;
}

Эта запись не имела бы смысла, но так как указано значение по умолчанию, то при компиляции будет подставлено именно оно:

.article {
  border-top: dotted 1px #cccccc;
  border-bottom: solid 2px #cccccc;
  color: #443d3d;
}

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

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

Пример 3.4.2

Иногда необходимо изменить не все параметры, а лишь некоторые из них. Делается это следующим образом:

.transition(@function: ease, @duration: 0.3s, @property: all) {
  transition-timing-function: @function;
  transition-duration: @duration;
  transition-property: @property;
}

.link {
  .transition(@duration: 1s);
}

Вместо того, чтобы писать все три значения для переменных, я указал конкретную переменную и обновил её значение. Компилятор возьмёт значения по умолчанию для переменных @function и @property, а значение переменной @duration будет взято из объявления вызова примеси.

.link {
  transition-timing-function: ease;
  transition-duration: 1s;
  transition-property: all;
}

Пример 3.4.3

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

.transition(@function: ease, @duration: 0.3s, @property: all) {
  transition-timing-function: @function;
  transition-duration: @duration;
  transition-property: @property;
}

.block {
  .transition(linear);
}

.link {
  .transition(linear, 0.5s);
}

После компиляции получится следующая картина:

.block {
  transition-timing-function: linear;
  transition-duration: 0.3s;
  transition-property: all;
}
.link {
  transition-timing-function: linear;
  transition-duration: 0.5s;
  transition-property: all;
}

Пример 3.4.4

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

@global: 3;

.calc(@a: 1, @b: 2, @c: @global) {
  @d: @a + @b + @c;
  content: @d;
}

.test {
  .calc();
}

Судя по правилам математики должно получиться 6.

.test {
  content: 6;
}

Мысли и советы

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