ATTENTION!
I keep this Gist for archival reasons, however I strongly recommend against using it. As I discovered after several weeks in production usage, these BEM mixins cause unexpected, unfixable and hard-to-debug selectors in some cases (especially when nested in some ways).
This is a utility with three simple Sass mixins for writing BEM as DRY as possible, heavily inspired by Hugo Giraudel's article on CSS Tricks.
It exposes three Sass mixins: block
, element
and modifier
.
A block
must always be at the top level. It can include any of the three provided mixins.
@include block ('alert') {
color: black;
}
// becomes
.alert {
color: black;
}
An element
mixin
-
can be placed inside a
block
@include block ('alert') { color: black; @include element ('icon') { background-image: url('info.svg'); } } // becomes .alert { color: black; } .alert__icon { background-image: url('info.svg'); }
-
can be placed inside a block
modifier
@include block ('alert') { @include modifier ('warn') { @include element ('icon') { background-image: url('warn.svg'); } } } // becomes .alert--warn__icon { background-image: url('warn.svg'); }
-
can take multiple parameters to represent multiple elements
@include block ('alert') { @include element (icon, message) { padding: 1em; } } // becomes .alert__icon, .alert__message { padding: 1em; }
-
can (with care!) be placed inside other elements. It's not the BEM way to nest element selectors, however this is allowed for special cases that cannot be expressed through BEM, for example handling pseudo classes:
@include block ('alert') { @include element ('close') { &:focus + { @include element ('icon') { background-image: url('info-closing.svg'); } } } } // becomes .alert__close:focus + .alert__icon { background-image: url("info-closing.svg"); }
A modifier
mixin
-
can be placed inside a
block
@include block ('alert') { color: black; @include modifier ('inverse') { color: white; background-color: black; } } // becomes .alert { color: black; } .alert--inverse { color: white; background-color: black; }
-
can be placed inside an
element
@include block ('alert') { @include element ('icon') { background-image: url('info.svg'); @include modifier ('hidden') { display: none; } } } // becomes .alert__icon { background-image: url('info.svg'); } .alert__icon--hidden { display: none; }
-
can be placed inside other
modifier
mixins to represent modifier combinations@include block ('alert') { color: black; @include modifier ('inverse') { color: white; background-color: black; } @include modifier ('warn') { background-color: yellow; @include modifier ('inverse') { color: yellow; background-color: black; } } } // becomes .alert { color: black; } .alert--inverse { color: white; background-color: black; } .alert--warn { background-color: yellow; } .alert--warn.alert--inverse { color: yellow; background-color: black; }
-
can take multiple parameters to represent modifier combinations
@include block ('alert') { color: black; @include modifier ('inverse') { color: white; background-color: black; } @include modifier ('warn') { background-color: yellow; } @include modifier ('warn', 'inverse') { color: yellow; background-color: black; } } // becomes .alert { color: black; } .alert--inverse { color: white; background-color: black; } .alert--warn { background-color: yellow; } .alert--warn.alert--inverse { color: yellow; background-color: black; }
-
can negate modifiers by prepending a
!
@include block ('alert') { color: black; @include modifier ('error', '!active') { color: pink; } } // becomes .alert { color: black; } .alert--error:not(.alert--active) { color: pink; }