Skip to content

Instantly share code, notes, and snippets.

@elado
Last active February 29, 2020 15:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elado/af17ed255333dc34375876a4decd827e to your computer and use it in GitHub Desktop.
Save elado/af17ed255333dc34375876a4decd827e to your computer and use it in GitHub Desktop.
Advanced BEM SASS Mixins
@mixin reset {
&, * {
margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline;
box-sizing: border-box; min-height: 0; min-width: 0;
ul { list-style: none; }
button { width: auto; height: auto; border: 0; background-color: transparent; cursor: pointer; outline: none; line-height: 1; }
}
}
$bem-use-namespace: false !default;
$bem-reset: false !default;
$__current-block-stack: () !global;
@for $i from 1 through 1000 {
$__current-block-stack: append($__current-block-stack, null) !global;
}
$__current-block-stack-index: 0 !global;
@function current-block-config() {
@return nth($__current-block-stack, $__current-block-stack-index);
}
@function current-block-name() { @return map-get(current-block-config(), 'block-name'); }
@function current-block-promote() { @return map-get(current-block-config(), 'promote'); }
@function current-block-namespace() { @return map-get(current-block-config(), 'namespace'); }
@mixin block($block-name, $namespace: $bem-use-namespace, $reset: $bem-reset, $promote: true) {
$__current-block-stack-index: $__current-block-stack-index + 1 !global;
@if $__current-block-stack-index > 1 {
$promote: false;
}
$config: (
'block-name': $block-name,
'promote': $promote,
'namespace': $namespace
);
$__current-block-stack: set-nth($list: $__current-block-stack, $n: $__current-block-stack-index, $value: $config) !global;
// apply reset only for root level
@if $__current-block-stack-index == 1 and $reset {
#{block-selector($block-name, $namespace: $namespace, $promote: false)} {
@include reset;
}
}
#{block-selector($block-name, $namespace: $namespace, $promote: $promote)} {
@content;
}
$__current-block-stack-index: $__current-block-stack-index - 1 !global;
}
@mixin block-child($child-name, $promote: false, $pseudo: null) {
@if type-of($child-name) != 'list' {
$child-name: ($child-name);
}
$sel: '';
@each $child-name-item in $child-name {
$current-selector: block-child-selector(
$block-name: current-block-name(),
$block-namespace: current-block-namespace(),
$child-name: $child-name-item,
$block-promote: current-block-promote(),
$child-promote: $promote,
$pseudo: $pseudo
);
$sel: '#{$sel}#{$current-selector},';
}
#{$sel} {
@content;
}
}
@mixin block-override($args...) {
@include block($args...) { @content; }
}
@mixin blocks-override($block-names, $promote: true) {
@each $block-name in $block-names {
@include block($block-name, $promote: $promote) {
@content;
}
}
}
@mixin block-mod($mod-name, $mod-value: null, $promote: false, $pseudo: null, $op: 'or') {
@if type-of($mod-name) == 'string' {
$tmp: (null);
$tmp: set-nth($tmp, 1, ($mod-name, $mod-value));
$mod-name: $tmp;
}
$sel: null;
@each $mod-item in $mod-name {
$mod-name-item: nth($mod-item, 1);
$mod-value-item: null;
@if length($mod-item) > 1 {
$mod-value-item: nth($mod-item, 2);
}
$current-selector: block-mod-selector(
$block-name: current-block-name(),
$block-namespace: current-block-namespace(),
$mod-name: $mod-name-item,
$mod-value: $mod-value-item,
$block-promote: current-block-promote(),
$mod-promote: $promote,
$pseudo: $pseudo
);
$sel: '#{$sel}#{&}#{$current-selector}';
@if $op != 'and' {
$sel: '#{$sel},';
}
}
@at-root #{$sel} {
@content;
}
}
@function block-selector($block-name, $namespace: null, $promote: false) {
$sel: '.';
@if $namespace { $sel: '#{$sel}#{$namespace}-'; }
$sel: '#{$sel}#{$block-name}';
@if $promote { $sel: '#{$sel}[class]'; }
@return $sel;
}
@function block-child-selector($block-name, $block-namespace, $child-name, $child-promote: false, $block-promote: true, $pseudo: null) {
$sel: '#{block-selector($block-name, $block-namespace, false)}__#{$child-name}';
@if $child-promote { $sel: '#{$sel}[class]'; }
@if $pseudo { $sel: '#{$sel}#{$pseudo}'; }
@return $sel;
}
@function block-mod-selector($block-name, $block-namespace, $mod-name, $mod-value: null, $mod-promote: false, $block-promote: true, $pseudo: null) {
$sel: '#{block-selector($block-name, $block-namespace, false)}--#{$mod-name}';
@if $mod-value {
$sel: '#{$sel}--#{$mod-value}';
}
@if $mod-promote { $sel: '#{$sel}[class]'; }
@if $pseudo { $sel: '#{$sel}#{$pseudo}'; }
@return $sel;
}
@function current-block-child-selector($child-name) {
@return block-child-selector(current-block-name(), current-block-namespace(), $child-name, $block-promote: false);
}
@function current-block-mod-selector($child-name) {
@return block-mod-selector(current-block-name(), current-block-namespace(), $child-name, $block-promote: false);
}
@mixin b($args...) {
@include block($args...) { @content; }
}
@mixin m($args...) {
@include block-mod($args...) { @content; }
}
@mixin c($args...) {
@include block-child($args...) { @content; }
}
$bem-use-namespace: 'ns';
$bem-reset: true;
// .ns-InfoPane[class]
@include b('InfoPane') {
border: 1px solid red;
// .ns-InfoPane[class] .ns-InfoPane__header
@include c('header') {
background: gray;
}
// .ns-InfoPane[class] .ns-InfoPane__main
@include c('main') {
line-height: 1.4em;
}
// .ns-InfoPane[class].ns-InfoPane--minimized .ns-InfoPane__main {
@include m('minimized') {
@include c('main') {
height: 20px; overflow: hidden;
}
}
}
// .ns-Message[class]
@include b('Message') {
// .ns-Message[class] .ns-Message--direction--outgoing
@include m('direction', 'outgoing') {
}
}
// .ns-MessageList[class]
@include b('MessageList') {
a: 1;
// .ns-MessageList[class] .ns-Message
@include b('Message') {
b: 2;
// .ns-MessageList[class] .ns-Message .ns-UserAvatar
@include b('UserAvatar') {
c: 3;
}
@include m('direction', 'outgoing') {
// .ns-MessageList[class] .ns-Message .ns-Message--direction-outgoing .ns-UserAvatar
@include b('UserAvatar') {
d: 4;
}
}
}
}
.ns-InfoPane, .ns-InfoPane * {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
box-sizing: border-box;
min-height: 0;
min-width: 0;
}
.ns-InfoPane ul, .ns-InfoPane * ul {
list-style: none;
}
.ns-InfoPane button, .ns-InfoPane * button {
width: auto;
height: auto;
border: 0;
background-color: transparent;
cursor: pointer;
outline: none;
line-height: 1;
}
.ns-InfoPane[class] {
border: 1px solid red;
}
.ns-InfoPane[class] .ns-InfoPane__header {
background: gray;
}
.ns-InfoPane[class] .ns-InfoPane__main {
line-height: 1.4em;
}
.ns-InfoPane[class].ns-InfoPane--minimized .ns-InfoPane__main {
height: 20px;
overflow: hidden;
}
.ns-Message, .ns-Message * {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
box-sizing: border-box;
min-height: 0;
min-width: 0;
}
.ns-Message ul, .ns-Message * ul {
list-style: none;
}
.ns-Message button, .ns-Message * button {
width: auto;
height: auto;
border: 0;
background-color: transparent;
cursor: pointer;
outline: none;
line-height: 1;
}
.ns-MessageList, .ns-MessageList * {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
box-sizing: border-box;
min-height: 0;
min-width: 0;
}
.ns-MessageList ul, .ns-MessageList * ul {
list-style: none;
}
.ns-MessageList button, .ns-MessageList * button {
width: auto;
height: auto;
border: 0;
background-color: transparent;
cursor: pointer;
outline: none;
line-height: 1;
}
.ns-MessageList[class] {
a: 1;
}
.ns-MessageList[class] .ns-Message {
b: 2;
}
.ns-MessageList[class] .ns-Message .ns-UserAvatar {
c: 3;
}
.ns-MessageList[class] .ns-Message.ns-Message--direction--outgoing .ns-UserAvatar {
d: 4;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment