Sass BEM: avoid modifier duplication when element is inside a modifier - sass

Can I somehow refactor the following code snippet to get rid of double modifier declaration?
.block {
&__element {
rule: value;
}
&--modifier {
rule: value;
}
&--modifier & {
&__element {
rule: value;
}
}
}
Output wanted:
.block {
property: value;
}
.block--modifier {
property: value;
}
.block--modifier .block__element {
property: value;
}

Nesting elements inside modifiers is a known issue. There are a lot of workarounds.
Variable way
Store the block element in a variable.
And use it interpolated when creating a element inside a modifier.
.block {
$block: &;
&__element {
property: value;
}
&--modifier {
property: value;
#{$block}__element {
property: value;
}
}
}
See output below.
Function way
1. Create a function that returns the block element.
It'll get the parent selector and cut the word before -- (which is the block). Looks hacky, but it's the simplest way to go.
#function block() {
$selector: str-slice(inspect(&), 2, -2);
$index: str-index($selector, '--') - 1;
#return str-slice($selector, 0, $index);
}
2. Use the function interpolated.
Which will return the name of the block so you don't have to repeat it.
.block {
property: value;
&--modifier {
property: value;
#{block()}__element {
property: value;
}
}
}
See output below.
Both ways will output to:
.block {
property: value;
}
.block--modifier {
property: value;
}
.block--modifier .block__element {
property: value;
}

You can place the block within the &--modifier selector like this, using the class name of the block rather than & to target it.
.block {
&__element {
rule: value;
}
&--modifier {
rule: value;
.block {
&__element {
rule: value;
}
}
}
}
However, this is possibly not the best BEM solution, you should consider renaming the nested block as an element of the containing block, such as .block__another-element or creating a new block entirely.

You could add & alongside the modifier for a solution similar to Toni's.
.block {
&__element {
rule: value;
}
&--modifier & {
rule: value;
&__element {
rule: value;
}
}
}
This would however require .block to be a root selector and not nested inside any other selector.
Just another possible solution. For most situations though, I would still prefer Toni's solution.

if someone use less, this will help you!
#block:.parent;
#{block}{
backgroung:red;
&--modifier{
backgroung:blue;
}
&__child{
font-size:20px;
#{block}--modifier & {
font-size:40px;
}
}
}

Related

SASS prefix custom element

I try to prefix a custom element that looks like this <myprefix-toggle></myprefix-toggle>. It does not work. However if I add # as if it would be an id it does compile. Why and how can I get around it?
Works
$prefix: "myprefix-";
#{$prefix}toggle {
background: red;
}
Does not work
$prefix: "myprefix-";
{$prefix}toggle {
background: red;
}
The error I get is probably not that related to the real issue...
Error: expected ':' after $prefix in assignment statement
If it's of importance I use gulp-sass to compile the sass to css.
The hashtag and the curly brackets is the Sass syntax for interpolation #{ ... }
$prefix: "myprefix-";
// CSS output
#{$prefix}toggle { ... } // myprefix-toggle { ... }
.#{$prefix}toggle { ... } // .myprefix-toggle { ... }
##{$prefix}toggle { ... } // #myprefix-toggle { ... }

How do i select only first closest parent of current class in sass

I have a hierarchy like this:
.form-group{
.span{
property: value
.form-control{
property: value
select{
}
}
}
}
I want to print css for a selects/dropdowns but only those that have the .form-control class.
.form-group .span select.form-control {
height: 50px
}
Please help me understand how to do this.
.form-group.span.form-control {
height: 50px
}
No deed of select simple ahead through .
Maybe like this ? I'm not sure what you want :
.form-group {
.span {
select {
&.form-control {
height: 50px
}
}
}
}
Change your less/scss like this. Like this it will only target those selects that have form-control class on them.
.form-group{
.span{
property: value
.form-control{
property: value
select.form-control {
// Add your styles here
}
}
}
}

SASS Mixin Rewrite & (ampersand)

I'm trying to write a mixin that will modify the parent selector on output. The idea is that in cases where a mixin is called, the parent selector will need to have a string replacement done on it. I have most of this working, but I can't figure out how to swallow the &.
.test {
#include alt_parent() {
content: 'test';
}
}
The mixin is something like this:
#mixin alt_parent() {
#{str-replace(unquote("#{selector_append(&)}"), "s", "x")} {
#content;
}
}
I have the string replacement working, so that isn't the problem. What I get is this (and I understand why):
.test .text {
content: 'test';
}
What I want is this:
.text {
content: 'test';
}
You have to use the #at-root directive to defeat the automatic inclusion of the selectors represented by &.
http://alwaystwisted.com/articles/2014-03-08-using-sass-33s-at-root-for-piece-of-mind
#mixin alt_parent($parent) {
#at-root {
#{str-replace(unquote("#{selector_append(&)}"), "s", "x")} {
#content;
}
}
}

Is it possible to pass in variables such that it adds to the actual scss variables?

I have a scss code like below
.hello-station {
&-hello123 {
.site-logo__image {
#extend .logos--hello-123;
margin: 27px 0;
}
}
}
Now you can see that the word "hello" is repeated throughout... So is the number.
I will like to create a mixin or function such that the word and the number can be passed along as variables. Is that possible?
Pretty simple actually, Sass/SCSS offers a concatenation syntax:
$word: 'hello';
$number: 123;
.#{$word}-station {
&-#{$word}#{$number} {
.site-logo__image {
#extend .logos--#{$word}-#{$number};
margin: 27px 0;
}
}
}

Updating a SASS variable within an extended class

Is there a way to update a SASS variable within an extended class? For example I have this currently:
.menu-highlight-1 {
$child: 1;
a {
&:nth-child(#{$child}) {
color: red !important;
}
}
}
And I want to update the $child variable to the next element, so in my attempt I've extended the .menu-highlight-1 class and then made a change to the $child variable. However all this does is highlight the same item from the extended class.
.menu-highlight-2 {
#extend .menu-highlight-1;
$child: 2;
}
Is this possible somehow?
The solution for this problem using a SASS mixin. Thanks to #cimmanon and this thread: Updating variables created by lighten/darken functions by modifying the original base color afterwards
#mixin _menuHighlight($child) {
a {
&:nth-child(#{$child}) {
color: red !important;
}
}
}
.menu-highlight-1 {
#include _menuHighlight(1);
}
.menu-highlight-2 {
#include _menuHighlight(2);
}

Resources