Знакомимся с 'тихими' placeholder

Reading time ~9 minutes

Статья посвящена вопросу “тихих” placeholder’ов в препроцессоре Sass. Что это такое и в чем преимущество их использования.

Оригинал статьи размещен здесь - Understanding placeholder selectors.

Препроцессор Sass предоставляет несколько способов создания одного фрагмента кода, который будет многократно использоваться внутри CSS-кода.

Например, можно воспользоваться миксинами (

1
mixins
) для вставки группы CSS-свойств (или CSS-правил) в CSS-коде.

Или же использовать директиву

1
@extend
для расширения набора CSS-свойств одного HTML-элемента за счет CSS-свойств другого HTML-элемента.

В Sass версии 3.2 введена новая концепция под названием

1
placeholder
, которая делает использование директивы
1
@extend
еще более эффективным способом.

Но прежде чем мы перейдем к рассмотрению этого нововведенния, давайте остановимся на моменте, каким образом работает расширение (

1
@extend
) CSS-свойств в Sass.

Как работает @extend

Директива

1
@extend
в препроцессоре Sass позволяет CSS-селекторам с легкостью обмениваться между собой своими CSS-свойствами. Лучше всего вышесказанное можно проиллюстрировать на живом примере:

.icon {
  transition: background-color ease .2s;
  margin: 0 .5em;
}

.error-icon {
  @extend .icon;
  /* здесь - специфичные стили класса .error-icon */
}

.info-icon {
  @extend .icon;
  /* здесь - специфичные стили класса .info-icon */
}

Результатом компиляции этого SCSS-кода в CSS-код будет следующий фрагмент:

.icon, .error-icon, .info-icon {
  transition: background-color ease .2s;
  margin: 0 .5em;
}

.error-icon {
  /* здесь - специфичные стили класса .error-icon */
}

.info-icon {
  /* здесь - специфичные стили класса .info-icon */
}

Рассмотрим “механизм” показанного выше примера более детально. В нем директива

1
@extend
играет ключевую роль. С помощью нее селекторы
1
.error-icon
и
1
.info-icon
наследуют свойства селектора
1
.icon
. При изменении CSS-свойств селектора
1
.icon
автоматически будут меняться свойства селекторов
1
.error-icon
и
1
.info-icon
, так как они наследуют определенный набор CSS-свойств у селектора
1
.icon
. Довольно изящный подход, не правда ли?

А вот теперь наступает интересный момент. Что, если элемент с классом

1
.icon
не планируется использовать и он даже не будет присутствовать в HTML-разметке? Но CSS-свойства этого элемента нам нужны для стилизации элементов
1
.error-icon
и
1
.info-icon
.

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

И тут наступает момент для выхода на сцену героя этой статьи - селектора

1
placeholder
(его еще называют “тихим”
1
placeholder
‘ом):

Тихий placeholder

Знакомимся с селектором placeholder

Селекторы

1
placeholder
были введены в Sass как раз для того, чтобы решать вышеназванную проблему. Синтаксис
1
placeholder
очень похож на синтаксис обычных CSS-классов, только вместо точки (
1
.
) перед именем ставиться символ процента
1
%
.

Селекторы

1
placeholder
имеют одну специфичную для них особенность - они никак не проявляют себя в скомпилированном CSS-коде. Можно сказать по другому - вы никогда не найдете селекторов
1
placeholder
в результирующем CSS-коде (поэтому они и носят такое название - “тихие”
1
placeholder
). В скомпилированном CSS-коде будут только селекторы, которые используют “тихие”
1
placeholder
‘ы, но никак не сами “тихие”
1
placeholder
‘ы.

Вернемся назад, к нашему начальному примеру. Заменим в нем имя класса

1
.icon
на имя “тихого” placeholder’а -
1
%icon
:

%icon {
  transition: background-color ease .2s;
  margin: 0 .5em;
}

.error-icon {
  @extend %icon;
  /* здесь - специфичные стили класса .error-icon */
}

.info-icon {
  @extend %icon;
  /* здесь - специфичные стили класса .info-icon */
}

В результате скомпилированный CSS-код будет выглядеть таким образом:

.error-icon, .info-icon {
  transition: background-color ease .2s;
  margin: 0 .5em;
}

.error-icon {
  /* здесь - специфичные стили класса .error-icon */
}

.info-icon {
  /* здесь - специфичные стили класса .info-icon */
}

Обратите внимание на важный момент - класс

1
.icon
теперь не присутствует в результирующем CSS-коде! Его там нет!

@extend или @include

На первый взгляд может показаться, что “тихие”

1
placeholder
- это почти тоже самое, что и миксины (
1
mixin
). С функциональной точки зрения такое утверждение абсолютно верно - результат в браузере получается идентичным. А вот с точки зрения CSS разница очень существенная!

Давайте снова изменим наш первоначальный пример и теперь воспользуемся миксином

1
@mixin icon
:

@mixin icon {
  transition: background-color ease .2s;
  margin: 0 .5em;
}

.error-icon {
  @include icon;
  /* здесь - специфичные стили класса .error-icon */
}

.info-icon {
  @include icon;
  /* здесь - специфичные стили класса .info-icon */
}

Посмотрим на сгенерированный CSS-код:

.error-icon {
  transition: background-color ease .2s;
  margin: 0 .5em;
  /* здесь - специфичные стили класса .error-icon */
}

.info-icon {
  transition: background-color ease .2s;
  margin: 0 .5em;
  /* здесь - специфичные стили класса .info-icon */
}

С точки зрения разработки данный пример ничем не хуже примера с использованием “тихого”

1
placeholder
‘а.

Но обратите внимание на тот факт, что CSS-правила

1
transition: background-color ease .2s;
и
1
margin: 0 .5em;
дублируются между селекторами
1
.error-icon
и
1
.info-icon
, что приводит к неоправданному раздутию кода. В случае использования “тихого”
1
placeholder
этого не происходит.

Ограничения

Использование директивы

1
@extend
имеет одно ограничение, связанное с тем, что применение “тихих”
1
placeholder
‘ов никак не оправдывает себя в медиа-запросах
1
@media
.

Рассмотрим такой пример:

%icon {
  transition: background-color ease .2s;
  margin: 0 .5em;
}

@media screen {

  .error-icon {
    @extend %icon;
  }

  .info-icon {
    @extend %icon;
  }

}

Видим, что в данном случае “тихий”

1
placeholder
добавлен для селекторов, находящихся внутри медиа-запроса
1
@media
.

Однако, при попытке компиляции этого SCSS-кода в CSS-код получиться ошибка:

You may not @extend an outer selector from within @media. You may only @extend selectors within the same directive. From “@extend %icon” on line 8 of icons.scss

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

Механизм работы директивы

1
@extend
основан на добавлении одного селектора к другому селектору без необходимости дублировать CSS-свойства этих селекторов. Однако невозможно объединять селекторы, находящиеся в разных медиа-запросах
1
@media
.

Но можно поступить по другому, чтобы выйти из данной затруднительной ситуации. Любой медиа-запрос, который служит оберткой для “тихого”

1
placeholder
, распространяют свои свойства на селекторы, не размещенные внутри этого запроса.

Выражение достаточно запутанное, поэтому лучше приведу пример:

@media screen {
  %icon {
    transition: background-color ease .2s;
    margin: 0 .5em;
  }
}

.error-icon {
  @extend %icon;
}

.info-icon {
  @extend %icon;
}

Компиляция пройдет без ошибок и ее результатом будет CSS-код:

@media screen {
  .error-icon, .info-icon {
    transition: background-color ease .2s;
    margin: 0 .5em;
  }
}

Заключение

Обе директивы

1
@extend
и
1
@include
являются очень полезными инструментами, между которыми существует тонкое различие. Если вопрос производительности генерируемого CSS-кода имеет для вас важное значение или же перед вами стоит проблема повторяемости кода, то решением будет являться директива
1
@extend
. В некоторых случая
1
@extend
значительно упрощает получаемый CSS-код и улучшает его производительность.

Конечно же, ничто не мешает вам смешивать между собой директиву

1
@extend
и миксин
1
mixin
(если этого требуют обстоятельства):

@media screen {
  %icon {
    transition: background-color ease .2s;
    margin: 0 .5em;
  }
}

@mixin icon($color, $url) {
  @extend %icon;
  background-color: $color;
  background-url: url($url);
}

.error-icon {
  @include icon(red, '/images/error.png');
}

.info-icon {
  @include icon(blue, '/images/info.png');
}

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


Mangling Angular

Angular Builder поддерживает параметры среды:- NG_BUILD_MANGLE- NG_BUILD_MINIFY- NG_BUILD_BEAUTIFYМожно установить их при запуске скрипта...… Continue reading

Constructor parameter without access modifier

Published on February 04, 2024

RxJs and DestroyRef Provider

Published on January 24, 2024