diff options
Diffstat (limited to 'src/bem/_modifier.scss')
| -rw-r--r-- | src/bem/_modifier.scss | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/src/bem/_modifier.scss b/src/bem/_modifier.scss new file mode 100644 index 0000000..e1f9507 --- /dev/null +++ b/src/bem/_modifier.scss | |||
| @@ -0,0 +1,246 @@ | |||
| 1 | //// | ||
| 2 | /// @group BEM | ||
| 3 | /// | ||
| 4 | /// @access public | ||
| 5 | //// | ||
| 6 | |||
| 7 | /// | ||
| 8 | /// Generate a new BEM modifier. | ||
| 9 | /// | ||
| 10 | /// If the parent context is block or element, the modifier will modify said block or element according | ||
| 11 | /// to the BEM naming convention. | ||
| 12 | /// | ||
| 13 | /// If the parent context is a modifier or suffix, then the modifier will depend on said modifier or suffix. | ||
| 14 | /// Depending on $extend, the meaning of this dependency (and the resulting selector) varies: | ||
| 15 | /// If it's false (default), you signalize that the modifier also exists by itself, but it changes its | ||
| 16 | /// behavior when the parent modifier or suffix is set. | ||
| 17 | /// If it's true, you signalize that the modifier extends the parent modifier or suffix and can only be | ||
| 18 | /// used in conjunction with it. | ||
| 19 | /// | ||
| 20 | /// @param {string | list} $name - First element name or list with two items: 1. first element name, 2. bool indicating if the modifier is extending | ||
| 21 | /// @param {string | list} $names - More element names or lists with two items: 1. element name, 2. bool indicating if the modifier is extending | ||
| 22 | /// | ||
| 23 | /// @content | ||
| 24 | /// | ||
| 25 | /// @throw If the element is not preceded by a block, element, modifier or suffix. | ||
| 26 | /// | ||
| 27 | /// @example scss - Modifier that modifies a block or element | ||
| 28 | /// @include iro-bem-component('block') { | ||
| 29 | /// @include iro-bem-modifier('mod') { | ||
| 30 | /// background-color: #eee; | ||
| 31 | /// } | ||
| 32 | /// | ||
| 33 | /// @include iro-bem-element('elem') { | ||
| 34 | /// @include iro-bem-modifier('mod') { | ||
| 35 | /// background-color: #222; | ||
| 36 | /// } | ||
| 37 | /// } | ||
| 38 | /// } | ||
| 39 | /// | ||
| 40 | /// // Generates: | ||
| 41 | /// | ||
| 42 | /// .c-block--mod { | ||
| 43 | /// background-color: #eee; | ||
| 44 | /// } | ||
| 45 | /// | ||
| 46 | /// .c-block__elem--mod { | ||
| 47 | /// background-color: #222; | ||
| 48 | /// } | ||
| 49 | /// | ||
| 50 | /// @example scss - Modifier nested in another modifier, not extending | ||
| 51 | /// @include iro-bem-component('block') { | ||
| 52 | /// @include iro-bem-modifier('mod') { | ||
| 53 | /// background-color: #eee; | ||
| 54 | /// } | ||
| 55 | /// | ||
| 56 | /// @include iro-bem-modifier('dark') { | ||
| 57 | /// /* some definitions */ | ||
| 58 | /// | ||
| 59 | /// @include iro-bem-modifier('mod') { | ||
| 60 | /// background-color: #222; | ||
| 61 | /// } | ||
| 62 | /// } | ||
| 63 | /// } | ||
| 64 | /// | ||
| 65 | /// // Generates: | ||
| 66 | /// | ||
| 67 | /// .c-block--mod { | ||
| 68 | /// background-color: #eee; | ||
| 69 | /// } | ||
| 70 | /// | ||
| 71 | /// .c-block--dark { | ||
| 72 | /// /* some definitions */ | ||
| 73 | /// } | ||
| 74 | /// | ||
| 75 | /// .c-block--dark.c-block--mod { | ||
| 76 | /// background-color: #222; | ||
| 77 | /// } | ||
| 78 | /// | ||
| 79 | /// @example scss - Modifier nested in another modifier, extending | ||
| 80 | /// @include iro-bem-component('block') { | ||
| 81 | /// @include iro-bem-modifier('mod') { | ||
| 82 | /// background-color: #eee; | ||
| 83 | /// } | ||
| 84 | /// | ||
| 85 | /// @include iro-bem-modifier('dark') { | ||
| 86 | /// /* some definitions */ | ||
| 87 | /// | ||
| 88 | /// @include iro-bem-modifier('mod' true) { | ||
| 89 | /// background-color: #222; | ||
| 90 | /// } | ||
| 91 | /// } | ||
| 92 | /// } | ||
| 93 | /// | ||
| 94 | /// // Generates: | ||
| 95 | /// | ||
| 96 | /// .c-block--mod { | ||
| 97 | /// background-color: #eee; | ||
| 98 | /// } | ||
| 99 | /// | ||
| 100 | /// .c-block--dark { | ||
| 101 | /// /* some definitions */ | ||
| 102 | /// } | ||
| 103 | /// | ||
| 104 | /// .c-block--dark--mod { | ||
| 105 | /// background-color: #222; | ||
| 106 | /// } | ||
| 107 | /// | ||
| 108 | @mixin iro-bem-modifier($name, $names...) { | ||
| 109 | $result: iro-bem-modifier($name, $names...); | ||
| 110 | $selector: nth($result, 1); | ||
| 111 | $context: nth($result, 2); | ||
| 112 | |||
| 113 | @include iro-bem-validate( | ||
| 114 | 'modifier', | ||
| 115 | (name: $name, names: $names), | ||
| 116 | $selector, | ||
| 117 | $context | ||
| 118 | ); | ||
| 119 | |||
| 120 | @include iro-context-push($iro-bem-context-id, $context...); | ||
| 121 | @at-root #{$selector} { | ||
| 122 | @content; | ||
| 123 | } | ||
| 124 | @include iro-context-pop($iro-bem-context-id); | ||
| 125 | } | ||
| 126 | |||
| 127 | /// | ||
| 128 | /// Generate a new BEM modifier. Check the respective mixin documentation for more information. | ||
| 129 | /// | ||
| 130 | /// @return {list} A list with two items: 1. selector, 2. context or `null` | ||
| 131 | /// | ||
| 132 | /// @see {mixin} iro-bem-modifier | ||
| 133 | /// | ||
| 134 | @function iro-bem-modifier($name, $names...) { | ||
| 135 | $noop: iro-context-assert-stack-count($iro-bem-context-id, $iro-bem-max-depth); | ||
| 136 | $noop: iro-context-assert-stack-must-contain($iro-bem-context-id, 'block'); | ||
| 137 | |||
| 138 | $parent-context: iro-context-get($iro-bem-context-id, 'block' 'element' 'modifier' 'suffix'); | ||
| 139 | $parent-selector: map-get(nth($parent-context, 2), 'selector'); | ||
| 140 | $selector: (); | ||
| 141 | $parts-data: (); | ||
| 142 | |||
| 143 | @if not iro-selector-suffix-match(&, $parent-selector) { | ||
| 144 | // | ||
| 145 | // The current selector doesn't match the parent selector. | ||
| 146 | // The user manually added a selector between parent context and this modifier call. | ||
| 147 | // This case is forbidden because any outcome semantically wouldn't make sense: | ||
| 148 | // - {b,e,m,s} [manual selector] {b,e,m,s}--modifier | ||
| 149 | // - {b,e,m,s}--modifier [manual selector] | ||
| 150 | // The first case would make the modifier behave like an element. | ||
| 151 | // The second case is unintuitive, the code would be more clear by nesting the manual | ||
| 152 | // selector in the modifier instead. | ||
| 153 | // | ||
| 154 | |||
| 155 | @error 'A modifier must be an immediate child of the parent context'; | ||
| 156 | } | ||
| 157 | |||
| 158 | @each $name in iro-list-prepend($names, $name) { | ||
| 159 | $extend: false; | ||
| 160 | @if type-of($name) == list { | ||
| 161 | $extend: nth($name, 2); | ||
| 162 | $name: nth($name, 1); | ||
| 163 | } | ||
| 164 | |||
| 165 | @if index('block' 'element', nth($parent-context, 1)) or $extend == true { | ||
| 166 | // | ||
| 167 | // Either the parent context is block or element, or a modifier or suffix | ||
| 168 | // is to be extended. The modifier part can simply be appended. | ||
| 169 | // Possible outcomes: | ||
| 170 | // - {b,e,m,s}--modifier | ||
| 171 | // | ||
| 172 | |||
| 173 | $sel: selector-append(&, $iro-bem-modifier-separator + $name); | ||
| 174 | $selector: join($selector, $sel, comma); | ||
| 175 | $parts-data: append($parts-data, ( | ||
| 176 | 'name': $name, | ||
| 177 | 'selector': $sel | ||
| 178 | )); | ||
| 179 | } @else { | ||
| 180 | // | ||
| 181 | // Parent context is modifier, suffix or state and $extend is false. | ||
| 182 | // | ||
| 183 | |||
| 184 | $be-context: iro-context-get($iro-bem-context-id, 'block' 'element'); | ||
| 185 | |||
| 186 | @if nth($be-context, 1) == 'element' { | ||
| 187 | // | ||
| 188 | // Latest context is element. Since element contexts can consist of multiple single | ||
| 189 | // elements, inspect all elements and append its selector with the suffix "--$name". | ||
| 190 | // This has to be done with string comparison since none of Sass selector functions | ||
| 191 | // is of use here. | ||
| 192 | // Possible outcomes: | ||
| 193 | // - {m,s}.{e}--modifier | ||
| 194 | // | ||
| 195 | |||
| 196 | $nsel: (); | ||
| 197 | |||
| 198 | @each $elem-part-data in map-get(nth($be-context, 2), 'parts') { | ||
| 199 | $elem-part-selector: map-get($elem-part-data, 'selector'); | ||
| 200 | |||
| 201 | $sel: (); | ||
| 202 | @each $s in & { | ||
| 203 | @each $ps in $elem-part-selector { | ||
| 204 | @if str-index(inspect($s), inspect($ps) + $iro-bem-modifier-separator) or str-index(inspect($s), inspect($ps) + $iro-bem-suffix-separator) { | ||
| 205 | $sel: join($sel, selector-unify($s, selector-append($ps, $iro-bem-modifier-separator + $name)), comma); | ||
| 206 | } | ||
| 207 | } | ||
| 208 | } | ||
| 209 | @if length($sel) == 0 { | ||
| 210 | @error 'Could not generate modifier selector.'; | ||
| 211 | } | ||
| 212 | |||
| 213 | $nsel: join($nsel, $sel, comma); | ||
| 214 | } | ||
| 215 | |||
| 216 | $selector: join($selector, $nsel, comma); | ||
| 217 | $parts-data: append($parts-data, ( | ||
| 218 | 'name': $name, | ||
| 219 | 'selector': $nsel | ||
| 220 | )); | ||
| 221 | } @else { | ||
| 222 | // | ||
| 223 | // Latest context is block. Just append the modifier part. | ||
| 224 | // Possible outcomes: | ||
| 225 | // - {m,s}.{b}--modifier | ||
| 226 | // | ||
| 227 | |||
| 228 | $block-base-selector: map-get(nth($be-context, 2), 'base-selector'); | ||
| 229 | |||
| 230 | $sel: selector-append(&, $block-base-selector, $iro-bem-modifier-separator + $name); | ||
| 231 | $selector: join($selector, $sel, comma); | ||
| 232 | $parts-data: append($parts-data, ( | ||
| 233 | 'name': $name, | ||
| 234 | 'selector': $sel | ||
| 235 | )); | ||
| 236 | } | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | $context: 'modifier', ( | ||
| 241 | 'parts': $parts-data, | ||
| 242 | 'selector': $selector | ||
| 243 | ); | ||
| 244 | |||
| 245 | @return $selector $context; | ||
| 246 | } | ||
