Использование Sass для управления диапазоном с помощью нейминга BEM в CSS

Использование Sass для управления диапазоном с помощью нейминга BEM в CSS

От автора: управление диапазоном — это та вещь, которую вы вероятно не будете учитывать при работе с BEM CSS и Sass. У нас в распоряжении есть доступ к амперсанду (&) уже довольно давно, он предоставляет нам уровень охвата, но это теряет полезность, когда у нас есть довольно глубокий уровень вложенности. & может повести нас по неправильному пути, и последствия будут довольно непредсказуемыми.

Между тем, в JavaScript мы можем контролировать диапазон this, передавая его переменной в той точке, где нам это нужно. Часто это начало метода или объекта:

function foo() { let self = this; return function() { // Self = foo(), even int his closure return self; }
} // We need to instantiate foo() or its 'this' will be the window
let bar = new foo();

Теперь, когда self используется в приведенном выше контексте, у нас всегда есть ссылка на foo(), независимо от того, где мы находимся внутри функции. Это действительно полезно, когда мы заканчиваем скрипты setTimeout, закрытия или обработчики событий.

Вот простой пример. который иллюстрирует это. Используя эту разметку:

<div class="component"> <div class="component__child-element"></div>
</div>
<div class="component"> <div class="component__child-element"></div>
</div>

мы можем добавить следующий JavaScript:

function foo() { // Load up all component child elements let childElements = [...document.querySelectorAll('.component__child-element')]; // Loop each element and add an event listener childElements.map(element => { element.addEventListener('click', function(evt) { // Log what `this` currently is console.log(this); }); });
} // Create a new instance of foo
let bar = new foo();

В этом примере, если вы кликните на элементе component__child при открытой консоли, this зарегистрирует себя, как цель события, которая, оказывается, является элементом, который мы кликнули. Это не идеально, если вам нужна ссылка на foo(). Теперь, если мы запустим в обработчике событий тот же образец self вместо this, консоль сообщит об этом экземпляре foo():

function foo() { // Control scope of this by storing it let self = this; // Load up all component child elements let childElements = [...document.querySelectorAll('.component__child-element')]; // Loop each element and add an event listener childElements.map(element => { element.addEventListener('click', function(evt) { // Self will report itself as `foo()` console.log(self); }); });
} // Create a new instance of foo
let bar = new foo();

И как это связано с Sass?

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

<div class="component"> <div class="component__child-element"></div>
</div>
<div class="component"> <div class="component__child-element"></div>
</div>
.component { display: block; max-width: 30rem; min-height: 30rem; margin: 5rem auto; background: rebeccapurple; position: relative; border: 1px dashed rebeccapurple; &__child-element { display: block; width: 15rem; height: 15rem; border-radius: 50%; position: absolute; top: 50%; left: 50%; transform: translate3d(-50%, -50%, 0); background: white; }
}

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

Вероятно, вы заметите, что мы используем синтаксис BEM, поэтому давайте продолжим и создадим модификатор. Мы хотим получить инвертированную версию .component, которая имеет белый фон с фиолетовым кругом на нем. Итак, давайте продолжим и применим подход, который первым приходит на ум — включим его в тот же самый вложенный набор правил:

.component { // Other styles redacted for brevity // The reversed modifier flips the colors around &--reversed { background: white; border-color: lightgray; &__child-element { background: rebeccapurple; } }
}

Подождите, но почему это не работает? Проблема в том, что & имеет область .component—reversed, поэтому &__child-element компилируется в .component—reversed__child-element, который не существует в разметке.

Нам на помощь приходит $self!

Как и в JavaScript, который мы рассматривали раньше, нам нужно перенести нашу начальную область в переменную с именем $self. Вы можете сделать это следующим образом:

.component { $self: &;
}

Это означает, что везде, где мы используем в нашем компоненте $self, она будет компилироваться в .component. Итак, давайте реорганизуем этот обратный модификатор, чтобы он действительно работал:

.component { $self: &; // Hey look, it's our new best friend! display: block; max-width: 30rem; min-height: 30rem; // Other styles redacted &--reversed { background: white; border-color: lightgray; // Here, we use $self to get the correct scope #{ $self }__child-element { background: rebeccapurple; } }
}

Скомпилированный CSS для этого элемента теперь .component—reversed .component__child-element, который, конечно, существует и успешно делает внутренний круг фиолетовым:

Дальнейшие исследования

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

// We're still within .component where $self has been declared.
&__child-element { display: block; width: 15rem; height: 15rem; border-radius: 50%; position: absolute; top: 50%; left: 50%; transform: translate3d(-50%, -50%, 0); background: white; // Flip the color when the parent is reversed #{ $self }--reversed & { background: rebeccapurple; }
}

У нас есть доступ к $self, поскольку корневой контекст предоставляет нам гибкость в работе с модификаторам. Поскольку $self содержит значение .component, а & — .component__element, добавление части -reversed генерирует .component—reversed .component__element, то есть именно то, что нам было нужно.

Более сложный пример

Теперь давайте напишем что-нибудь действительно интересное. Мы разместим три отдельные сетки. Используя $self, я собираюсь изменить цвет элементов сетки, используя селектор sibling в самом элементе сетки.

Давайте рассмотрим Sass, который влияет на цвет:

&__item { // Item styles redacted for brevity // For each direct `.grid sibling`, make this item rebeccapurple #{ $self } + #{ $self } & { background: rebeccapurple; }
}

В результате этот селектор компилирует .grid + .grid .grid__item. Сочетание $self и & позволяет нам генерировать это в контексте элемента, который представляет собой наш &. Это предоставляет нам невероятно много возможностей для поддержания порядка в сложных базах кода.

Заключение

Мы использовали JavaScript для управления с помощью Sass диапазоном в наших компонентах BEM, используя этот небольшой фрагмент в корне: $self: &. Благодаря управлению диапазоном, мы получаем гибкость, позволяющую писать чистый, организованный, управляемый BEM на основе CSS. Надеюсь, этот маленький совет поможет вам улучшить свой Sass.

Автор: Andy Bell

Источник: https://css-tricks.com/

Редакция: Команда webformyself.