aboutsummaryrefslogtreecommitdiffstats
path: root/src/bem
diff options
context:
space:
mode:
authorVolpeon <git@volpeon.ink>2025-08-13 12:01:46 +0200
committerVolpeon <git@volpeon.ink>2025-08-13 12:01:46 +0200
commitf0f84513f8efe533b6ee670a6f1a0c074387b2ec (patch)
tree845bc4bacf1bd99acb0dfcc7e4545a36b544d2f8 /src/bem
parentMore fix (diff)
downloadiro-sass-f0f84513f8efe533b6ee670a6f1a0c074387b2ec.tar.gz
iro-sass-f0f84513f8efe533b6ee670a6f1a0c074387b2ec.tar.bz2
iro-sass-f0f84513f8efe533b6ee670a6f1a0c074387b2ec.zip
Make use of SASS modulesHEADmaster
Diffstat (limited to 'src/bem')
-rw-r--r--src/bem/_block.scss222
-rw-r--r--src/bem/_debug.scss15
-rw-r--r--src/bem/_element.scss483
-rw-r--r--src/bem/_functions.scss27
-rw-r--r--src/bem/_modifier.scss227
-rw-r--r--src/bem/_multi.scss102
-rw-r--r--src/bem/_state.scss84
-rw-r--r--src/bem/_suffix.scss95
-rw-r--r--src/bem/_theme.scss57
-rw-r--r--src/bem/_validators.scss137
-rw-r--r--src/bem/_vars.scss28
11 files changed, 746 insertions, 731 deletions
diff --git a/src/bem/_block.scss b/src/bem/_block.scss
index cfa9f33..a4b2a47 100644
--- a/src/bem/_block.scss
+++ b/src/bem/_block.scss
@@ -4,6 +4,10 @@
4/// @access public 4/// @access public
5//// 5////
6 6
7@use 'sass:list';
8@use 'sass:map';
9@use 'sass:meta';
10@use 'sass:selector';
7@use './validators'; 11@use './validators';
8@use './vars'; 12@use './vars';
9@use './functions' as bemfunctions; 13@use './functions' as bemfunctions;
@@ -37,34 +41,34 @@
37/// } 41/// }
38/// 42///
39@mixin block($name, $type: null) { 43@mixin block($name, $type: null) {
40 $result: block($name, $type); 44 $result: block($name, $type);
41 $selector: nth($result, 1); 45 $selector: list.nth($result, 1);
42 $context: nth($result, 2); 46 $context: list.nth($result, 2);
43 47
44 @include validators.validate( 48 @include validators.validate(
45 'block', 49 'block',
46 (name: $name, type: $type), 50 (name: $name, type: $type),
47 $selector, 51 $selector,
48 $context 52 $context
49 ); 53 );
50 54
51 @if $type != null { 55 @if $type != null {
52 vars.$blocks: append(vars.$blocks, $name + '_' + $type); 56 vars.$blocks: list.append(vars.$blocks, $name + '_' + $type);
53 } @else { 57 } @else {
54 vars.$blocks: append(vars.$blocks, $name); 58 vars.$blocks: list.append(vars.$blocks, $name);
55 } 59 }
56 60
57 @include contexts.push(vars.$context-id, $context...); 61 @include contexts.push(vars.$context-id, $context...);
58 @at-root #{$selector} { 62 @at-root #{$selector} {
59 @if $type != null { 63 @if $type != null {
60 @layer #{$type} { 64 @layer #{$type} {
61 @content; 65 @content;
62 } 66 }
63 } @else { 67 } @else {
64 @content; 68 @content;
65 } 69 }
66 } 70 }
67 @include contexts.pop(vars.$context-id); 71 @include contexts.pop(vars.$context-id);
68} 72}
69 73
70/// 74///
@@ -75,47 +79,47 @@
75/// @see {mixin} block 79/// @see {mixin} block
76/// 80///
77@function block($name, $type: null) { 81@function block($name, $type: null) {
78 // 82 //
79 // Possible outcomes: 83 // Possible outcomes:
80 // - ({b,e,m,s}) block 84 // - ({b,e,m,s}) block
81 // 85 //
82 86
83 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); 87 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth);
84 88
85 $selector: null; 89 $selector: null;
86 $base-selector: null; 90 $base-selector: null;
87 91
88 @if $type != null { 92 @if $type != null {
89 $namespace: map-get(vars.$namespaces, $type); 93 $namespace: map.get(vars.$namespaces, $type);
90 94
91 @if not $namespace { 95 @if not $namespace {
92 @error '"#{$type}" is not a valid type.'; 96 @error '"#{$type}" is not a valid type.';
93 } 97 }
94 98
95 $base-selector: selector-parse('.' + $namespace + '-' + $name); 99 $base-selector: selector.parse('.' + $namespace + '-' + $name);
96 100
97 @if $type != 'theme' or & { 101 @if $type != 'theme' or & {
98 $selector: $base-selector; 102 $selector: $base-selector;
99 } @else if not & { 103 } @else if not & {
100 $selector: bemfunctions.theme-selector($name); 104 $selector: bemfunctions.theme-selector($name);
101 } 105 }
102 } @else { 106 } @else {
103 $base-selector: selector-parse('.' + $name); 107 $base-selector: selector.parse('.' + $name);
104 $selector: $base-selector; 108 $selector: $base-selector;
105 } 109 }
106 110
107 @if & { 111 @if & {
108 $selector: selector-nest(&, $selector); 112 $selector: selector.nest(&, $selector);
109 } 113 }
110 114
111 $context: 'block', ( 115 $context: 'block', (
112 'name': $name, 116 'name': $name,
113 'type': $type, 117 'type': $type,
114 'selector': $selector, 118 'selector': $selector,
115 'base-selector': $base-selector 119 'base-selector': $base-selector
116 ); 120 );
117 121
118 @return $selector $context; 122 @return $selector $context;
119} 123}
120 124
121/// 125///
@@ -126,9 +130,9 @@
126/// @content 130/// @content
127/// 131///
128@mixin object($name) { 132@mixin object($name) {
129 @include block($name, 'object') { 133 @include block($name, 'object') {
130 @content; 134 @content;
131 } 135 }
132} 136}
133 137
134/// 138///
@@ -139,7 +143,7 @@
139/// @see {mixin} object 143/// @see {mixin} object
140/// 144///
141@function object($name) { 145@function object($name) {
142 @return block($name, 'object'); 146 @return block($name, 'object');
143} 147}
144 148
145/// 149///
@@ -150,9 +154,9 @@
150/// @content 154/// @content
151/// 155///
152@mixin component($name) { 156@mixin component($name) {
153 @include block($name, 'component') { 157 @include block($name, 'component') {
154 @content; 158 @content;
155 } 159 }
156} 160}
157 161
158/// 162///
@@ -163,7 +167,7 @@
163/// @see {mixin} component 167/// @see {mixin} component
164/// 168///
165@function component($name) { 169@function component($name) {
166 @return block($name, 'component'); 170 @return block($name, 'component');
167} 171}
168 172
169/// 173///
@@ -174,9 +178,9 @@
174/// @content 178/// @content
175/// 179///
176@mixin layout($name) { 180@mixin layout($name) {
177 @include block($name, 'layout') { 181 @include block($name, 'layout') {
178 @content; 182 @content;
179 } 183 }
180} 184}
181 185
182/// 186///
@@ -187,7 +191,7 @@
187/// @see {mixin} layout 191/// @see {mixin} layout
188/// 192///
189@function layout($name) { 193@function layout($name) {
190 @return block($name, 'layout'); 194 @return block($name, 'layout');
191} 195}
192 196
193/// 197///
@@ -198,9 +202,9 @@
198/// @content 202/// @content
199/// 203///
200@mixin utility($name) { 204@mixin utility($name) {
201 @include block($name, 'utility') { 205 @include block($name, 'utility') {
202 @content; 206 @content;
203 } 207 }
204} 208}
205 209
206/// 210///
@@ -211,7 +215,7 @@
211/// @see {mixin} utility 215/// @see {mixin} utility
212/// 216///
213@function utility($name) { 217@function utility($name) {
214 @return block($name, 'utility'); 218 @return block($name, 'utility');
215} 219}
216 220
217/// 221///
@@ -222,9 +226,9 @@
222/// @content 226/// @content
223/// 227///
224@mixin scope($name) { 228@mixin scope($name) {
225 @include block($name, 'scope') { 229 @include block($name, 'scope') {
226 @content; 230 @content;
227 } 231 }
228} 232}
229 233
230/// 234///
@@ -235,7 +239,7 @@
235/// @see {mixin} scope 239/// @see {mixin} scope
236/// 240///
237@function scope($name) { 241@function scope($name) {
238 @return block($name, 'scope'); 242 @return block($name, 'scope');
239} 243}
240 244
241/// 245///
@@ -246,9 +250,9 @@
246/// @content 250/// @content
247/// 251///
248@mixin theme($name) { 252@mixin theme($name) {
249 @include block($name, 'theme') { 253 @include block($name, 'theme') {
250 @content; 254 @content;
251 } 255 }
252} 256}
253 257
254/// 258///
@@ -259,7 +263,7 @@
259/// @see {mixin} theme 263/// @see {mixin} theme
260/// 264///
261@function theme($name) { 265@function theme($name) {
262 @return block($name, 'theme'); 266 @return block($name, 'theme');
263} 267}
264 268
265/// 269///
@@ -270,9 +274,9 @@
270/// @content 274/// @content
271/// 275///
272@mixin js($name) { 276@mixin js($name) {
273 @include block($name, 'js') { 277 @include block($name, 'js') {
274 @content; 278 @content;
275 } 279 }
276} 280}
277 281
278/// 282///
@@ -283,7 +287,7 @@
283/// @see {mixin} js 287/// @see {mixin} js
284/// 288///
285@function js($name) { 289@function js($name) {
286 @return block($name, 'js'); 290 @return block($name, 'js');
287} 291}
288 292
289/// 293///
@@ -294,9 +298,9 @@
294/// @content 298/// @content
295/// 299///
296@mixin qa($name) { 300@mixin qa($name) {
297 @include block($name, 'qa') { 301 @include block($name, 'qa') {
298 @content; 302 @content;
299 } 303 }
300} 304}
301 305
302/// 306///
@@ -307,7 +311,7 @@
307/// @see {mixin} qa 311/// @see {mixin} qa
308/// 312///
309@function qa($name) { 313@function qa($name) {
310 @return block($name, 'qa'); 314 @return block($name, 'qa');
311} 315}
312 316
313/// 317///
@@ -318,9 +322,9 @@
318/// @content 322/// @content
319/// 323///
320@mixin hack($name) { 324@mixin hack($name) {
321 @include block($name, 'hack') { 325 @include block($name, 'hack') {
322 @content; 326 @content;
323 } 327 }
324} 328}
325 329
326/// 330///
@@ -331,7 +335,7 @@
331/// @see {mixin} hack 335/// @see {mixin} hack
332/// 336///
333@function hack($name) { 337@function hack($name) {
334 @return block($name, 'hack'); 338 @return block($name, 'hack');
335} 339}
336 340
337/// 341///
@@ -383,22 +387,22 @@
383/// // Compilation will fail because c-someBlock is defined after c-anotherBlock__elem 387/// // Compilation will fail because c-someBlock is defined after c-anotherBlock__elem
384/// 388///
385@mixin composed-of($block, $blocks...) { 389@mixin composed-of($block, $blocks...) {
386 @each $block in functions.list-prepend($blocks, $block) { 390 @each $block in functions.list-prepend($blocks, $block) {
387 @if type-of($block) == string { 391 @if meta.type-of($block) == string {
388 @if not index(vars.$blocks, $block) { 392 @if not list.index(vars.$blocks, $block) {
389 @error 'Block "#{$block}" does not exist.'; 393 @error 'Block "#{$block}" does not exist.';
390 } 394 }
391 } @else { 395 } @else {
392 $name: nth($block, 1); 396 $name: list.nth($block, 1);
393 $type: nth($block, 2); 397 $type: list.nth($block, 2);
394 398
395 @if not map-get(vars.$namespaces, $type) { 399 @if not map.get(vars.$namespaces, $type) {
396 @error '"#{$type}" is not a valid type.'; 400 @error '"#{$type}" is not a valid type.';
397 } 401 }
398 402
399 @if not index(vars.$blocks, $name + '_' + $type) { 403 @if not list.index(vars.$blocks, $name + '_' + $type) {
400 @error 'Block "#{$name}" does not exist.'; 404 @error 'Block "#{$name}" does not exist.';
401 } 405 }
402 } 406 }
403 } 407 }
404} 408}
diff --git a/src/bem/_debug.scss b/src/bem/_debug.scss
index 8ea0f05..b1f20a7 100644
--- a/src/bem/_debug.scss
+++ b/src/bem/_debug.scss
@@ -4,15 +4,16 @@
4/// @access public 4/// @access public
5//// 5////
6 6
7@use 'sass:map';
7@use './vars'; 8@use './vars';
8 9
9@if vars.$debug { 10@if vars.$debug {
10 @each $type, $color in vars.$debug-colors { 11 @each $type, $color in vars.$debug-colors {
11 $namespace: map-get(vars.$namespaces, $type); 12 $namespace: map.get(vars.$namespaces, $type);
12 13
13 [class^='#{$namespace}-'], 14 [class^='#{$namespace}-'],
14 [class*=' #{$namespace}-'] { 15 [class*=' #{$namespace}-'] {
15 outline: 5px solid $color; 16 outline: 5px solid $color;
16 } 17 }
17 } 18 }
18} 19}
diff --git a/src/bem/_element.scss b/src/bem/_element.scss
index 64862b0..9f108fe 100644
--- a/src/bem/_element.scss
+++ b/src/bem/_element.scss
@@ -4,6 +4,11 @@
4/// @access public 4/// @access public
5//// 5////
6 6
7@use 'sass:list';
8@use 'sass:map';
9@use 'sass:meta';
10@use 'sass:selector';
11@use 'sass:string';
7@use './validators'; 12@use './validators';
8@use './vars'; 13@use './vars';
9@use '../functions'; 14@use '../functions';
@@ -92,22 +97,22 @@
92/// } 97/// }
93/// 98///
94@mixin elem($name, $names...) { 99@mixin elem($name, $names...) {
95 $result: elem($name, $names...); 100 $result: elem($name, $names...);
96 $selector: nth($result, 1); 101 $selector: list.nth($result, 1);
97 $context: nth($result, 2); 102 $context: list.nth($result, 2);
98 103
99 @include validators.validate( 104 @include validators.validate(
100 'element', 105 'element',
101 (name: $name, names: $names), 106 (name: $name, names: $names),
102 $selector, 107 $selector,
103 $context 108 $context
104 ); 109 );
105 110
106 @include contexts.push(vars.$context-id, $context...); 111 @include contexts.push(vars.$context-id, $context...);
107 @at-root #{$selector} { 112 @at-root #{$selector} {
108 @content; 113 @content;
109 } 114 }
110 @include contexts.pop(vars.$context-id); 115 @include contexts.pop(vars.$context-id);
111} 116}
112 117
113/// 118///
@@ -118,99 +123,93 @@
118/// @see {mixin} element 123/// @see {mixin} element
119/// 124///
120@function elem($name, $names...) { 125@function elem($name, $names...) {
121 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); 126 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth);
122 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'block'); 127 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'block');
123 128
124 $parent-context: contexts.get(vars.$context-id, 'block' 'element'); 129 $parent-context: contexts.get(vars.$context-id, 'block' 'element');
125 130
126 $selector: (); 131 $selector: ();
127 $parts-data: (); 132 $parts-data: ();
128 133
129 @if nth($parent-context, 1) == 'element' { 134 @if list.nth($parent-context, 1) == 'element' {
130 @if vars.$element-nesting-policy == 'disallow' { 135 @if vars.$element-nesting-policy == 'disallow' {
131 @error 'Element nesting is forbidden.'; 136 @error 'Element nesting is forbidden.';
132 } 137 }
133 138
134 @if vars.$element-nesting-policy == 'append' { 139 @if vars.$element-nesting-policy == 'append' {
135 $element-selector: map-get(nth($parent-context, 2), 'selector'); 140 $element-selector: map.get(list.nth($parent-context, 2), 'selector');
136 141
137 @if not functions.selector-suffix-match(&, $element-selector) { 142 @if not functions.selector-suffix-match(&, $element-selector) {
138 @error 'A nested element must be an immediate children of the parent element.'; 143 @error 'A nested element must be an immediate children of the parent element.';
139 } 144 }
140 145
141 // 146 //
142 // Possible outcomes: 147 // Possible outcomes:
143 // - {e}__element 148 // - {e}__element
144 // - [manual selector] {e}__element 149 // - [manual selector] {e}__element
145 // 150 //
146 151
147 @each $name in join($name, $names) { 152 @each $name in list.join($name, $names) {
148 $sel: selector-append(&, vars.$element-separator + $name); 153 $sel: selector.append(&, vars.$element-separator + $name);
149 $selector: join($selector, $sel, comma); 154 $selector: list.join($selector, $sel, comma);
150 $parts-data: append( 155 $parts-data: list.append($parts-data, (
151 $parts-data, ( 156 'name': $name,
152 'name': $name, 157 'selector': $sel
153 'selector': $sel 158 ));
154 ) 159 }
155 ); 160 }
156 }
157 }
158 161
159 $parent-context: contexts.get(vars.$context-id, 'block'); 162 $parent-context: contexts.get(vars.$context-id, 'block');
160 } 163 }
161 164
162 @if length($selector) == 0 { 165 @if list.length($selector) == 0 {
163 $parent-selector: map-get(nth($parent-context, 2), 'selector'); 166 $parent-selector: map.get(list.nth($parent-context, 2), 'selector');
164 167
165 @if functions.selector-suffix-match(&, $parent-selector) { 168 @if functions.selector-suffix-match(&, $parent-selector) {
166 // 169 //
167 // Possible outcomes: 170 // Possible outcomes:
168 // - {b}__element 171 // - {b}__element
169 // - [manual selector] {b}__element 172 // - [manual selector] {b}__element
170 // 173 //
171 174
172 @each $name in join($name, $names) { 175 @each $name in list.join($name, $names) {
173 $sel: selector-append(&, vars.$element-separator + $name); 176 $sel: selector.append(&, vars.$element-separator + $name);
174 $selector: join($selector, $sel, comma); 177 $selector: list.join($selector, $sel, comma);
175 $parts-data: append( 178 $parts-data: list.append($parts-data, (
176 $parts-data, ( 179 'name': $name,
177 'name': $name, 180 'selector': $sel
178 'selector': $sel 181 ));
179 ) 182 }
180 ); 183 } @else {
181 } 184 //
182 } @else { 185 // Possible outcomes:
183 // 186 // - {b} [manual selector] {b}__element
184 // Possible outcomes: 187 // - {e,m,s} ([manual selector]) {b}__element
185 // - {b} [manual selector] {b}__element 188 //
186 // - {e,m,s} ([manual selector]) {b}__element
187 //
188 189
189 @if nth($parent-context, 1) != 'block' { 190 @if list.nth($parent-context, 1) != 'block' {
190 $parent-context: contexts.get(vars.$context-id, 'block'); 191 $parent-context: contexts.get(vars.$context-id, 'block');
191 } 192 }
192 193
193 $block-base-selector: map-get(nth($parent-context, 2), 'base-selector'); 194 $block-base-selector: map.get(list.nth($parent-context, 2), 'base-selector');
194 195
195 @each $name in join($name, $names) { 196 @each $name in list.join($name, $names) {
196 $sel: selector-nest(&, selector-append($block-base-selector, vars.$element-separator + $name)); 197 $sel: selector.nest(&, selector.append($block-base-selector, vars.$element-separator + $name));
197 $selector: join($selector, $sel, comma); 198 $selector: list.join($selector, $sel, comma);
198 $parts-data: append( 199 $parts-data: list.append($parts-data, (
199 $parts-data, ( 200 'name': $name,
200 'name': $name, 201 'selector': $sel
201 'selector': $sel 202 ));
202 ) 203 }
203 ); 204 }
204 } 205 }
205 }
206 }
207 206
208 $context: 'element', ( 207 $context: 'element', (
209 'parts': $parts-data, 208 'parts': $parts-data,
210 'selector': $selector 209 'selector': $selector
211 ); 210 );
212 211
213 @return $selector $context; 212 @return $selector $context;
214} 213}
215 214
216/// 215///
@@ -291,22 +290,22 @@
291/// } 290/// }
292/// 291///
293@mixin related-elem($sign, $name, $names...) { 292@mixin related-elem($sign, $name, $names...) {
294 $result: related-elem($sign, $name, $names...); 293 $result: related-elem($sign, $name, $names...);
295 $selector: nth($result, 1); 294 $selector: list.nth($result, 1);
296 $context: nth($result, 2); 295 $context: list.nth($result, 2);
297 296
298 @include validators.validate( 297 @include validators.validate(
299 'related-element', 298 'related-element',
300 (sign: $sign, name: $name, names: $names), 299 (sign: $sign, name: $name, names: $names),
301 $selector, 300 $selector,
302 $context 301 $context
303 ); 302 );
304 303
305 @include contexts.push(vars.$context-id, $context...); 304 @include contexts.push(vars.$context-id, $context...);
306 @at-root #{$selector} { 305 @at-root #{$selector} {
307 @content; 306 @content;
308 } 307 }
309 @include contexts.pop(vars.$context-id); 308 @include contexts.pop(vars.$context-id);
310} 309}
311 310
312/// 311///
@@ -318,44 +317,42 @@
318/// @see {mixin} related-element 317/// @see {mixin} related-element
319/// 318///
320@function related-elem($sign, $name, $names...) { 319@function related-elem($sign, $name, $names...) {
321 // 320 //
322 // Generating this selector is simple: Take the latest block context, use it 321 // Generating this selector is simple: Take the latest block context, use it
323 // to generate the element part, and insert it at the end of the current selector. 322 // to generate the element part, and insert it at the end of the current selector.
324 // Possible outcomes: 323 // Possible outcomes:
325 // - {e} ({m,s}) ([manual selector]) + {e} 324 // - {e} ({m,s}) ([manual selector]) + {e}
326 // - {e} ({m,s}) ([manual selector]) ~ {e} 325 // - {e} ({m,s}) ([manual selector]) ~ {e}
327 // 326 //
328 327
329 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); 328 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth);
330 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'element'); 329 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'element');
331 330
332 @if $sign != '+' and $sign != '~' { 331 @if $sign != '+' and $sign != '~' {
333 @error 'Invalid relationship sign #{inspect($sign)}.'; 332 @error 'Invalid relationship sign #{inspect($sign)}.';
334 } 333 }
335 334
336 $block-context: contexts.get(vars.$context-id, 'block'); 335 $block-context: contexts.get(vars.$context-id, 'block');
337 $block-base-selector: map-get(nth($block-context, 2), 'base-selector'); 336 $block-base-selector: map.get(list.nth($block-context, 2), 'base-selector');
338 337
339 $selector: (); 338 $selector: ();
340 $parts-data: (); 339 $parts-data: ();
341 340
342 @each $name in join($name, $names) { 341 @each $name in list.join($name, $names) {
343 $sel: selector-nest(&, $sign, selector-append($block-base-selector, vars.$element-separator + $name)); 342 $sel: selector.nest(&, $sign, selector.append($block-base-selector, vars.$element-separator + $name));
344 $selector: join($selector, $sel, comma); 343 $selector: list.join($selector, $sel, comma);
345 $parts-data: append( 344 $parts-data: list.append($parts-data, (
346 $parts-data, ( 345 'name': $name,
347 'name': $name, 346 'selector': $sel
348 'selector': $sel 347 ));
349 ) 348 }
350 );
351 }
352 349
353 $context: 'element', ( 350 $context: 'element', (
354 'parts': $parts-data, 351 'parts': $parts-data,
355 'selector': $selector 352 'selector': $selector
356 ); 353 );
357 354
358 @return $selector $context; 355 @return $selector $context;
359} 356}
360 357
361/// 358///
@@ -369,9 +366,9 @@
369/// @content 366/// @content
370/// 367///
371@mixin sibling-elem($name, $names...) { 368@mixin sibling-elem($name, $names...) {
372 @include related-elem('~', $name, $names...) { 369 @include related-elem('~', $name, $names...) {
373 @content; 370 @content;
374 } 371 }
375} 372}
376 373
377/// 374///
@@ -383,7 +380,7 @@
383/// @see {mixin} sibling-element 380/// @see {mixin} sibling-element
384/// 381///
385@function sibling-elem($name, $names...) { 382@function sibling-elem($name, $names...) {
386 @return related-elem('~', $name, $names...); 383 @return related-elem('~', $name, $names...);
387} 384}
388 385
389/// 386///
@@ -397,9 +394,9 @@
397/// @content 394/// @content
398/// 395///
399@mixin next-elem($name, $names...) { 396@mixin next-elem($name, $names...) {
400 @include related-elem('+', $name, $names...) { 397 @include related-elem('+', $name, $names...) {
401 @content; 398 @content;
402 } 399 }
403} 400}
404 401
405/// 402///
@@ -411,7 +408,7 @@
411/// @see {mixin} next-element 408/// @see {mixin} next-element
412/// 409///
413@function next-elem($name, $names...) { 410@function next-elem($name, $names...) {
414 @return related-elem('+', $name, $names...); 411 @return related-elem('+', $name, $names...);
415} 412}
416 413
417/// 414///
@@ -467,22 +464,22 @@
467/// } 464/// }
468/// 465///
469@mixin related-twin-elem($sign) { 466@mixin related-twin-elem($sign) {
470 $result: related-twin-elem($sign); 467 $result: related-twin-elem($sign);
471 $selector: nth($result, 1); 468 $selector: list.nth($result, 1);
472 $context: nth($result, 2); 469 $context: list.nth($result, 2);
473 470
474 @include validators.validate( 471 @include validators.validate(
475 'next-twin-elem', 472 'next-twin-elem',
476 (), 473 (),
477 $selector, 474 $selector,
478 $context 475 $context
479 ); 476 );
480 477
481 @include contexts.push(vars.$context-id, $context...); 478 @include contexts.push(vars.$context-id, $context...);
482 @at-root #{$selector} { 479 @at-root #{$selector} {
483 @content; 480 @content;
484 } 481 }
485 @include contexts.pop(vars.$context-id); 482 @include contexts.pop(vars.$context-id);
486} 483}
487 484
488/// 485///
@@ -494,96 +491,94 @@
494/// @see {mixin} next-twin-elem 491/// @see {mixin} next-twin-elem
495/// 492///
496@function related-twin-elem($sign) { 493@function related-twin-elem($sign) {
497 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); 494 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth);
498 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'element'); 495 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'element');
499 496
500 $element-context: contexts.get(vars.$context-id, 'element'); 497 $element-context: contexts.get(vars.$context-id, 'element');
501 $element-selector: map-get(nth($element-context, 2), 'selector'); 498 $element-selector: map.get(list.nth($element-context, 2), 'selector');
502 499
503 $block-context: contexts.get(vars.$context-id, 'block'); 500 $block-context: contexts.get(vars.$context-id, 'block');
504 $block-base-selector: map-get(nth($block-context, 2), 'base-selector'); 501 $block-base-selector: map.get(list.nth($block-context, 2), 'base-selector');
505 502
506 $selector: (); 503 $selector: ();
507 $parts-data: (); 504 $parts-data: ();
508 505
509 // 506 //
510 // To determine the twin for each element, iterate the sub-selectors from the current selector 507 // To determine the twin for each element, iterate the sub-selectors from the current selector
511 // and check if it contains the currently inspected element. This has to be done with string 508 // and check if it contains the currently inspected element. This has to be done with string
512 // comparison since none of Sass selector functions is of use here. 509 // comparison since none of Sass selector functions is of use here.
513 // Finally, the current twin will be appended to the extracted sub-selector as a successor 510 // Finally, the current twin will be appended to the extracted sub-selector as a successor
514 // element. 511 // element.
515 // 512 //
516 @each $part-data in map-get(nth($element-context, 2), 'parts') { 513 @each $part-data in map.get(list.nth($element-context, 2), 'parts') {
517 $part-selector: map-get($part-data, 'selector'); 514 $part-selector: map.get($part-data, 'selector');
518 $part-name: map-get($part-data, 'name'); 515 $part-name: map.get($part-data, 'name');
519 516
520 $sel: (); 517 $sel: ();
521 @if functions.selector-suffix-match(&, $element-selector) { 518 @if functions.selector-suffix-match(&, $element-selector) {
522 // 519 //
523 // This mixin is included in the selector the last element mixin created. 520 // This mixin is included in the selector the last element mixin created.
524 // Possible outcomes: 521 // Possible outcomes:
525 // - {e} + {e} 522 // - {e} + {e}
526 // - [manual selector] {e} + {e} 523 // - [manual selector] {e} + {e}
527 // 524 //
528 525
529 @each $s in & { 526 @each $s in & {
530 @each $ps in $part-selector { 527 @each $ps in $part-selector {
531 @if nth($s, -1) == nth($ps, -1) { 528 @if list.nth($s, -1) == list.nth($ps, -1) {
532 $sel-ent: selector-nest($s, $sign, selector-append($block-base-selector, vars.$element-separator + $part-name)); 529 $sel-ent: selector.nest($s, $sign, selector.append($block-base-selector, vars.$element-separator + $part-name));
533 $sel: join($sel, $sel-ent, comma); 530 $sel: list.join($sel, $sel-ent, comma);
534 } 531 }
535 } 532 }
536 } 533 }
537 } @else { 534 } @else {
538 // 535 //
539 // This mixin is NOT included in the selector the last element mixin created. 536 // This mixin is NOT included in the selector the last element mixin created.
540 // Possible outcomes: 537 // Possible outcomes:
541 // - {e} {m,s} + {e} 538 // - {e} {m,s} + {e}
542 // - {e} [manual selector] + {e} 539 // - {e} [manual selector] + {e}
543 // - {e} {m,s} [manual selector] + {e} 540 // - {e} {m,s} [manual selector] + {e}
544 // 541 //
545 542
546 @each $s in & { 543 @each $s in & {
547 @each $ps in $part-selector { 544 @each $ps in $part-selector {
548 @if str-index(inspect($s), inspect($ps)) { 545 @if string.index(meta.inspect($s), meta.inspect($ps)) {
549 $char-index: str-length(inspect($ps)) + 1; 546 $char-index: string.length(meta.inspect($ps)) + 1;
550 $match: index(' ' ':' ',', str-slice(inspect($s), $char-index, $char-index)) != null; 547 $match: list.index(' ' ':' ',', string.slice(meta.inspect($s), $char-index, $char-index)) != null;
551 548
552 @if not $match { 549 @if not $match {
553 @each $separator in vars.$element-separator vars.$modifier-separator vars.$suffix-separator { 550 @each $separator in vars.$element-separator vars.$modifier-separator vars.$suffix-separator {
554 @if str-slice(inspect($s), $char-index, $char-index + str-length($separator) - 1) == $separator { 551 @if string.slice(meta.inspect($s), $char-index, $char-index + string.length($separator) - 1) == $separator {
555 $match: true; 552 $match: true;
556 } 553 }
557 } 554 }
558 } 555 }
559 556
560 @if $match { 557 @if $match {
561 $sel-ent: selector-nest($s, '+', selector-append($block-base-selector, vars.$element-separator + $part-name)); 558 $sel-ent: selector.nest($s, '+', selector.append($block-base-selector, vars.$element-separator + $part-name));
562 $sel: join($sel, $sel-ent, comma); 559 $sel: list.join($sel, $sel-ent, comma);
563 } 560 }
564 } 561 }
565 } 562 }
566 } 563 }
567 } 564 }
568 @if length($sel) != length($part-selector) { 565 @if list.length($sel) != list.length($part-selector) {
569 @error 'Could not generate twin element selector.'; 566 @error 'Could not generate twin element selector.';
570 } 567 }
571 568
572 $selector: join($selector, $sel, comma); 569 $selector: list.join($selector, $sel, comma);
573 $parts-data: append( 570 $parts-data: list.append($parts-data, (
574 $parts-data, ( 571 'name': $part-name,
575 'name': $part-name, 572 'selector': $sel
576 'selector': $sel 573 ));
577 ) 574 }
578 );
579 }
580 575
581 $context: 'element', ( 576 $context: 'element', (
582 'parts': $parts-data, 577 'parts': $parts-data,
583 'selector': $selector 578 'selector': $selector
584 ); 579 );
585 580
586 @return $selector $context; 581 @return $selector $context;
587} 582}
588 583
589/// 584///
@@ -594,9 +589,9 @@
594/// @content 589/// @content
595/// 590///
596@mixin sibling-twin-element { 591@mixin sibling-twin-element {
597 @include related-twin-elem('~') { 592 @include related-twin-elem('~') {
598 @content; 593 @content;
599 } 594 }
600} 595}
601 596
602/// 597///
@@ -608,7 +603,7 @@
608/// @see {mixin} sibling-twin-element 603/// @see {mixin} sibling-twin-element
609/// 604///
610@function sibling-twin-elem() { 605@function sibling-twin-elem() {
611 @return related-twin-elem('~'); 606 @return related-twin-elem('~');
612} 607}
613 608
614/// 609///
@@ -619,9 +614,9 @@
619/// @content 614/// @content
620/// 615///
621@mixin next-twin-elem { 616@mixin next-twin-elem {
622 @include related-twin-elem('+') { 617 @include related-twin-elem('+') {
623 @content; 618 @content;
624 } 619 }
625} 620}
626 621
627/// 622///
@@ -633,5 +628,5 @@
633/// @see {mixin} next-twin-elem 628/// @see {mixin} next-twin-elem
634/// 629///
635@function next-twin-elem() { 630@function next-twin-elem() {
636 @return related-twin-elem('+'); 631 @return related-twin-elem('+');
637} 632}
diff --git a/src/bem/_functions.scss b/src/bem/_functions.scss
index b7bd5ec..7f52b93 100644
--- a/src/bem/_functions.scss
+++ b/src/bem/_functions.scss
@@ -4,25 +4,28 @@
4/// @access public 4/// @access public
5//// 5////
6 6
7@use 'sass:list';
8@use 'sass:map';
9@use 'sass:selector';
7@use './vars'; 10@use './vars';
8 11
9/// 12///
10/// @access private 13/// @access private
11/// 14///
12@function theme-selector($name, $names...) { 15@function theme-selector($name, $names...) {
13 $namespace: map-get(vars.$namespaces, 'theme'); 16 $namespace: map.get(vars.$namespaces, 'theme');
14 $selector: null; 17 $selector: null;
15 18
16 @each $name in join($name, $names) { 19 @each $name in list.join($name, $names) {
17 $sel: '.' + $namespace + '-' + $name; 20 $sel: '.' + $namespace + '-' + $name;
18 21
19 @if $selector == null { 22 @if $selector == null {
20 $selector: join(selector-parse($sel), selector-parse('[class*=\' t-\'] ' + $sel), comma); 23 $selector: list.join(selector.parse($sel), selector.parse('[class*=\' t-\'] ' + $sel), comma);
21 $selector: join($selector, selector-parse('[class^=\'t-\'] ' + $sel), comma); 24 $selector: list.join($selector, selector.parse('[class^=\'t-\'] ' + $sel), comma);
22 } @else { 25 } @else {
23 $selector: selector-nest($selector, $sel); 26 $selector: selector.nest($selector, $sel);
24 } 27 }
25 } 28 }
26 29
27 @return $selector; 30 @return $selector;
28} 31}
diff --git a/src/bem/_modifier.scss b/src/bem/_modifier.scss
index 07267fe..10e2826 100644
--- a/src/bem/_modifier.scss
+++ b/src/bem/_modifier.scss
@@ -4,6 +4,11 @@
4/// @access public 4/// @access public
5//// 5////
6 6
7@use 'sass:list';
8@use 'sass:map';
9@use 'sass:meta';
10@use 'sass:selector';
11@use 'sass:string';
7@use './validators'; 12@use './validators';
8@use './vars'; 13@use './vars';
9@use '../functions'; 14@use '../functions';
@@ -111,22 +116,22 @@
111/// } 116/// }
112/// 117///
113@mixin modifier($name, $names...) { 118@mixin modifier($name, $names...) {
114 $result: modifier($name, $names...); 119 $result: modifier($name, $names...);
115 $selector: nth($result, 1); 120 $selector: list.nth($result, 1);
116 $context: nth($result, 2); 121 $context: list.nth($result, 2);
117 122
118 @include validators.validate( 123 @include validators.validate(
119 'modifier', 124 'modifier',
120 (name: $name, names: $names), 125 (name: $name, names: $names),
121 $selector, 126 $selector,
122 $context 127 $context
123 ); 128 );
124 129
125 @include contexts.push(vars.$context-id, $context...); 130 @include contexts.push(vars.$context-id, $context...);
126 @at-root #{$selector} { 131 @at-root #{$selector} {
127 @content; 132 @content;
128 } 133 }
129 @include contexts.pop(vars.$context-id); 134 @include contexts.pop(vars.$context-id);
130} 135}
131 136
132/// 137///
@@ -137,121 +142,115 @@
137/// @see {mixin} modifier 142/// @see {mixin} modifier
138/// 143///
139@function modifier($name, $names...) { 144@function modifier($name, $names...) {
140 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); 145 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth);
141 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'block'); 146 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'block');
142 147
143 $parent-context: contexts.get(vars.$context-id, 'block' 'element' 'modifier' 'suffix' 'state'); 148 $parent-context: contexts.get(vars.$context-id, 'block' 'element' 'modifier' 'suffix' 'state');
144 $parent-selector: map-get(nth($parent-context, 2), 'selector'); 149 $parent-selector: map.get(list.nth($parent-context, 2), 'selector');
145 $selector: (); 150 $selector: ();
146 $parts-data: (); 151 $parts-data: ();
147 152
148 @if not functions.selector-suffix-match(&, $parent-selector) { 153 @if not functions.selector-suffix-match(&, $parent-selector) {
149 // 154 //
150 // The current selector doesn't match the parent selector. 155 // The current selector doesn't match the parent selector.
151 // The user manually added a selector between parent context and this modifier call. 156 // The user manually added a selector between parent context and this modifier call.
152 // This case is forbidden because any outcome semantically wouldn't make sense: 157 // This case is forbidden because any outcome semantically wouldn't make sense:
153 // - {b,e,m,s} [manual selector] {b,e,m,s}--modifier 158 // - {b,e,m,s} [manual selector] {b,e,m,s}--modifier
154 // - {b,e,m,s}--modifier [manual selector] 159 // - {b,e,m,s}--modifier [manual selector]
155 // The first case would make the modifier behave like an element. 160 // The first case would make the modifier behave like an element.
156 // The second case is unintuitive, the code would be more clear by nesting the manual 161 // The second case is unintuitive, the code would be more clear by nesting the manual
157 // selector in the modifier instead. 162 // selector in the modifier instead.
158 // 163 //
159 164
160 @error 'A modifier must be an immediate child of the parent context'; 165 @error 'A modifier must be an immediate child of the parent context';
161 } 166 }
162 167
163 @each $name in functions.list-prepend($names, $name) { 168 @each $name in functions.list-prepend($names, $name) {
164 $extend: false; 169 $extend: false;
165 @if type-of($name) == list { 170 @if meta.type-of($name) == list {
166 $extend: nth($name, 2); 171 $extend: list.nth($name, 2);
167 $name: nth($name, 1); 172 $name: list.nth($name, 1);
168 } 173 }
169 174
170 @if index('block' 'element', nth($parent-context, 1)) or $extend == true { 175 @if list.index('block' 'element', list.nth($parent-context, 1)) or $extend == true {
171 // 176 //
172 // Either the parent context is block or element, or a modifier or suffix 177 // Either the parent context is block or element, or a modifier or suffix
173 // is to be extended. The modifier part can simply be appended. 178 // is to be extended. The modifier part can simply be appended.
174 // Possible outcomes: 179 // Possible outcomes:
175 // - {b,e,m,s}--modifier 180 // - {b,e,m,s}--modifier
176 // 181 //
177 182
178 $sel: selector-append(&, vars.$modifier-separator + $name); 183 $sel: selector.append(&, vars.$modifier-separator + $name);
179 $selector: join($selector, $sel, comma); 184 $selector: list.join($selector, $sel, comma);
180 $parts-data: append( 185 $parts-data: list.append($parts-data, (
181 $parts-data, ( 186 'name': $name,
182 'name': $name, 187 'selector': $sel
183 'selector': $sel 188 ));
184 ) 189 } @else {
185 ); 190 //
186 } @else { 191 // Parent context is modifier, suffix or state and $extend is false.
187 // 192 //
188 // Parent context is modifier, suffix or state and $extend is false.
189 //
190 193
191 $be-context: contexts.get(vars.$context-id, 'block' 'element'); 194 $be-context: contexts.get(vars.$context-id, 'block' 'element');
192 195
193 @if nth($be-context, 1) == 'element' { 196 @if list.nth($be-context, 1) == 'element' {
194 // 197 //
195 // Latest context is element. Since element contexts can consist of multiple single 198 // Latest context is element. Since element contexts can consist of multiple single
196 // elements, inspect all elements and append its selector with the suffix "--$name". 199 // elements, inspect all elements and append its selector with the suffix "--$name".
197 // This has to be done with string comparison since none of Sass selector functions 200 // This has to be done with string comparison since none of Sass selector functions
198 // is of use here. 201 // is of use here.
199 // Possible outcomes: 202 // Possible outcomes:
200 // - {m,s}.{e}--modifier 203 // - {m,s}.{e}--modifier
201 // 204 //
202 205
203 $nsel: (); 206 $nsel: ();
204 207
205 @each $elem-part-data in map-get(nth($be-context, 2), 'parts') { 208 @each $elem-part-data in map.get(list.nth($be-context, 2), 'parts') {
206 $elem-part-selector: map-get($elem-part-data, 'selector'); 209 $elem-part-selector: map.get($elem-part-data, 'selector');
207 210
208 $sel: (); 211 $sel: ();
209 @each $s in & { 212 @each $s in & {
210 @each $ps in $elem-part-selector { 213 @each $ps in $elem-part-selector {
211 @if str-index(inspect($s), inspect($ps) + vars.$modifier-separator) or str-index(inspect($s), inspect($ps) + vars.$suffix-separator) { 214 @if string.index(meta.inspect($s), meta.inspect($ps) + vars.$modifier-separator) or string.index(meta.inspect($s), meta.inspect($ps) + vars.$suffix-separator) {
212 $sel: join($sel, selector-unify($s, selector-append($ps, vars.$modifier-separator + $name)), comma); 215 $sel: list.join($sel, selector.unify($s, selector.append($ps, vars.$modifier-separator + $name)), comma);
213 } 216 }
214 } 217 }
215 } 218 }
216 @if length($sel) == 0 { 219 @if list.length($sel) == 0 {
217 @error 'Could not generate modifier selector.'; 220 @error 'Could not generate modifier selector.';
218 } 221 }
219 222
220 $nsel: join($nsel, $sel, comma); 223 $nsel: list.join($nsel, $sel, comma);
221 } 224 }
222 225
223 $selector: join($selector, $nsel, comma); 226 $selector: list.join($selector, $nsel, comma);
224 $parts-data: append( 227 $parts-data: list.append($parts-data, (
225 $parts-data, ( 228 'name': $name,
226 'name': $name, 229 'selector': $nsel
227 'selector': $nsel 230 ));
228 ) 231 } @else {
229 ); 232 //
230 } @else { 233 // Latest context is block. Just append the modifier part.
231 // 234 // Possible outcomes:
232 // Latest context is block. Just append the modifier part. 235 // - {m,s}.{b}--modifier
233 // Possible outcomes: 236 //
234 // - {m,s}.{b}--modifier
235 //
236 237
237 $block-base-selector: map-get(nth($be-context, 2), 'base-selector'); 238 $block-base-selector: map.get(list.nth($be-context, 2), 'base-selector');
238 239
239 $sel: selector-append(&, $block-base-selector, vars.$modifier-separator + $name); 240 $sel: selector.append(&, $block-base-selector, vars.$modifier-separator + $name);
240 $selector: join($selector, $sel, comma); 241 $selector: list.join($selector, $sel, comma);
241 $parts-data: append( 242 $parts-data: list.append($parts-data, (
242 $parts-data, ( 243 'name': $name,
243 'name': $name, 244 'selector': $sel
244 'selector': $sel 245 ));
245 ) 246 }
246 ); 247 }
247 } 248 }
248 }
249 }
250 249
251 $context: 'modifier', ( 250 $context: 'modifier', (
252 'parts': $parts-data, 251 'parts': $parts-data,
253 'selector': $selector 252 'selector': $selector
254 ); 253 );
255 254
256 @return $selector $context; 255 @return $selector $context;
257} 256}
diff --git a/src/bem/_multi.scss b/src/bem/_multi.scss
index 1de5cdc..c0beeeb 100644
--- a/src/bem/_multi.scss
+++ b/src/bem/_multi.scss
@@ -4,6 +4,10 @@
4/// @access public 4/// @access public
5//// 5////
6 6
7@use 'sass:list';
8@use 'sass:meta';
9@use 'sass:selector';
10@use 'sass:string';
7@use '../functions'; 11@use '../functions';
8@use '../contexts'; 12@use '../contexts';
9@use './block'; 13@use './block';
@@ -81,67 +85,67 @@
81/// } 85/// }
82/// 86///
83@mixin multi($first, $others...) { 87@mixin multi($first, $others...) {
84 @include contexts.assert-stack-count(vars.$context-id, vars.$max-depth); 88 @include contexts.assert-stack-count(vars.$context-id, vars.$max-depth);
85 89
86 @each $entity in functions.list-prepend($others, $first) { 90 @each $entity in functions.list-prepend($others, $first) {
87 $is-manual-selector: false; 91 $is-manual-selector: false;
88 92
89 @if type-of($entity) == string { 93 @if meta.type-of($entity) == string {
90 @if find-bem-function($entity) == null { 94 @if find-bem-function($entity) == null {
91 $is-manual-selector: true; 95 $is-manual-selector: true;
92 } 96 }
93 } 97 }
94 98
95 @if $is-manual-selector { 99 @if $is-manual-selector {
96 $sel: if(&, selector-nest(&, $entity), selector-parse($entity)); 100 $sel: if(&, selector.nest(&, $entity), selector.parse($entity));
97 101
98 @at-root #{$sel} { 102 @at-root #{$sel} {
99 @content; 103 @content;
100 } 104 }
101 } @else { 105 } @else {
102 $entity-func-id: null; 106 $entity-func-id: null;
103 107
104 @if type-of($entity) == list { 108 @if meta.type-of($entity) == list {
105 $entity-func-id: nth($entity, 1); 109 $entity-func-id: list.nth($entity, 1);
106 $entity: functions.list-slice($entity, 2); 110 $entity: functions.list-slice($entity, 2);
107 } @else { 111 } @else {
108 $entity-func-id: $entity; 112 $entity-func-id: $entity;
109 $entity: (); 113 $entity: ();
110 } 114 }
111 115
112 @if str-slice($entity-func-id, str-length($entity-func-id)) == ':' { 116 @if string.slice($entity-func-id, string.length($entity-func-id)) == ':' {
113 $entity-func-id: unquote(str-slice($entity-func-id, 1, str-length($entity-func-id) - 1)); 117 $entity-func-id: string.unquote(string.slice($entity-func-id, 1, string.length($entity-func-id) - 1));
114 } 118 }
115 119
116 $sel-func: find-bem-function($entity-func-id); 120 $sel-func: find-bem-function($entity-func-id);
117 121
118 @if $sel-func == null { 122 @if $sel-func == null {
119 @error 'Function "#{inspect($entity-func-id)}" was not found.'; 123 @error 'Function "#{inspect($entity-func-id)}" was not found.';
120 } 124 }
121 125
122 $entity-result: call($sel-func, $entity...); 126 $entity-result: meta.call($sel-func, $entity...);
123 $entity-result-selector: nth($entity-result, 1); 127 $entity-result-selector: list.nth($entity-result, 1);
124 $entity-result-context: nth($entity-result, 2); 128 $entity-result-context: list.nth($entity-result, 2);
125 129
126 @if $entity-result-context != null { 130 @if $entity-result-context != null {
127 @include contexts.push(vars.$context-id, $entity-result-context...); 131 @include contexts.push(vars.$context-id, $entity-result-context...);
128 } 132 }
129 @at-root #{$entity-result-selector} { 133 @at-root #{$entity-result-selector} {
130 @content; 134 @content;
131 } 135 }
132 @if $entity-result-context != null { 136 @if $entity-result-context != null {
133 @include contexts.pop(vars.$context-id); 137 @include contexts.pop(vars.$context-id);
134 } 138 }
135 } 139 }
136 } 140 }
137} 141}
138 142
139@function find-bem-function($name) { 143@function find-bem-function($name) {
140 @each $module in (block element modifier state suffix theme) { 144 @each $module in (block element modifier state suffix theme) {
141 @if function-exists($name, $module) { 145 @if meta.function-exists($name, $module) {
142 @return get-function($name, $module: $module); 146 @return meta.get-function($name, $module: $module);
143 } 147 }
144 } 148 }
145 149
146 @return null; 150 @return null;
147} 151}
diff --git a/src/bem/_state.scss b/src/bem/_state.scss
index 41bacee..bd0efb1 100644
--- a/src/bem/_state.scss
+++ b/src/bem/_state.scss
@@ -4,6 +4,8 @@
4/// @access public 4/// @access public
5//// 5////
6 6
7@use 'sass:list';
8@use 'sass:selector';
7@use './validators'; 9@use './validators';
8@use './vars'; 10@use './vars';
9@use '../contexts'; 11@use '../contexts';
@@ -56,22 +58,22 @@
56/// } 58/// }
57/// 59///
58@mixin state($prefix, $state, $states...) { 60@mixin state($prefix, $state, $states...) {
59 $result: state($prefix, $state, $states...); 61 $result: state($prefix, $state, $states...);
60 $selector: nth($result, 1); 62 $selector: list.nth($result, 1);
61 $context: nth($result, 2); 63 $context: list.nth($result, 2);
62 64
63 @include validators.validate( 65 @include validators.validate(
64 'state', 66 'state',
65 (prefix: $prefix, state: $state, states: $states), 67 (prefix: $prefix, state: $state, states: $states),
66 $selector, 68 $selector,
67 $context 69 $context
68 ); 70 );
69 71
70 @include contexts.push(vars.$context-id, $context...); 72 @include contexts.push(vars.$context-id, $context...);
71 @at-root #{$selector} { 73 @at-root #{$selector} {
72 @content; 74 @content;
73 } 75 }
74 @include contexts.pop(vars.$context-id); 76 @include contexts.pop(vars.$context-id);
75} 77}
76 78
77/// 79///
@@ -82,29 +84,27 @@
82/// @see {mixin} has 84/// @see {mixin} has
83/// 85///
84@function state($prefix, $state, $states...) { 86@function state($prefix, $state, $states...) {
85 $selector: (); 87 $selector: ();
86 $parts-data: (); 88 $parts-data: ();
87 89
88 @each $state in join($state, $states) { 90 @each $state in list.join($state, $states) {
89 $sel: selector-parse('.#{$prefix}-#{$state}'); 91 $sel: selector.parse('.#{$prefix}-#{$state}');
90 @if & { 92 @if & {
91 $sel: selector-append(&, $sel); 93 $sel: selector.append(&, $sel);
92 } 94 }
93 $selector: join($selector, $sel, comma); 95 $selector: list.join($selector, $sel, comma);
94 $parts-data: append( 96 $parts-data: list.append($parts-data, (
95 $parts-data, ( 97 'name': $state,
96 'name': $state, 98 'selector': $sel
97 'selector': $sel 99 ));
98 ) 100 }
99 );
100 }
101 101
102 $context: 'state', ( 102 $context: 'state', (
103 'parts': $parts-data, 103 'parts': $parts-data,
104 'selector': $selector 104 'selector': $selector
105 ); 105 );
106 106
107 @return $selector $context; 107 @return $selector $context;
108} 108}
109 109
110/// 110///
@@ -113,9 +113,9 @@
113/// It's a shorthand for state('is', $state, $states...). 113/// It's a shorthand for state('is', $state, $states...).
114/// 114///
115@mixin is($state, $states...) { 115@mixin is($state, $states...) {
116 @include state('is', $state, $states...) { 116 @include state('is', $state, $states...) {
117 @content; 117 @content;
118 } 118 }
119} 119}
120 120
121/// 121///
@@ -126,7 +126,7 @@
126/// @see {mixin} is 126/// @see {mixin} is
127/// 127///
128@function is($state, $states...) { 128@function is($state, $states...) {
129 @return state('is', $state, $states...); 129 @return state('is', $state, $states...);
130} 130}
131 131
132/// 132///
@@ -135,9 +135,9 @@
135/// It's a shorthand for state('has', $state, $states...). 135/// It's a shorthand for state('has', $state, $states...).
136/// 136///
137@mixin has($state, $states...) { 137@mixin has($state, $states...) {
138 @include state('has', $state, $states...) { 138 @include state('has', $state, $states...) {
139 @content; 139 @content;
140 } 140 }
141} 141}
142 142
143/// 143///
@@ -148,5 +148,5 @@
148/// @see {mixin} has 148/// @see {mixin} has
149/// 149///
150@function has($state, $states...) { 150@function has($state, $states...) {
151 @return state('has', $state, $states...); 151 @return state('has', $state, $states...);
152} 152}
diff --git a/src/bem/_suffix.scss b/src/bem/_suffix.scss
index 2ddb54d..93e4066 100644
--- a/src/bem/_suffix.scss
+++ b/src/bem/_suffix.scss
@@ -4,6 +4,9 @@
4/// @access public 4/// @access public
5//// 5////
6 6
7@use 'sass:list';
8@use 'sass:map';
9@use 'sass:selector';
7@use './validators'; 10@use './validators';
8@use './vars'; 11@use './vars';
9@use '../functions'; 12@use '../functions';
@@ -54,22 +57,22 @@
54/// } 57/// }
55/// 58///
56@mixin suffix($name) { 59@mixin suffix($name) {
57 $result: suffix($name); 60 $result: suffix($name);
58 $selector: nth($result, 1); 61 $selector: list.nth($result, 1);
59 $context: nth($result, 2); 62 $context: list.nth($result, 2);
60 63
61 @include validators.validate( 64 @include validators.validate(
62 'suffix', 65 'suffix',
63 (name: $name), 66 (name: $name),
64 $selector, 67 $selector,
65 $context 68 $context
66 ); 69 );
67 70
68 @include contexts.push(vars.$context-id, $context...); 71 @include contexts.push(vars.$context-id, $context...);
69 @at-root #{$selector} { 72 @at-root #{$selector} {
70 @content; 73 @content;
71 } 74 }
72 @include contexts.pop(vars.$context-id); 75 @include contexts.pop(vars.$context-id);
73} 76}
74 77
75/// 78///
@@ -80,44 +83,44 @@
80/// @see {mixin} suffix 83/// @see {mixin} suffix
81/// 84///
82@function suffix($name) { 85@function suffix($name) {
83 // 86 //
84 // Suffixes can be used on block, element and modifier. 87 // Suffixes can be used on block, element and modifier.
85 // 88 //
86 89
87 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth); 90 $noop: contexts.assert-stack-count(vars.$context-id, vars.$max-depth);
88 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'block'); 91 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'block');
89 $noop: contexts.assert-stack-must-not-contain(vars.$context-id, 'suffix'); 92 $noop: contexts.assert-stack-must-not-contain(vars.$context-id, 'suffix');
90 93
91 $parent-context: contexts.get(vars.$context-id, 'block' 'element' 'modifier'); 94 $parent-context: contexts.get(vars.$context-id, 'block' 'element' 'modifier');
92 $parent-selector: map-get(nth($parent-context, 2), 'selector'); 95 $parent-selector: map.get(list.nth($parent-context, 2), 'selector');
93 96
94 @if not functions.selector-suffix-match(&, $parent-selector) { 97 @if not functions.selector-suffix-match(&, $parent-selector) {
95 // 98 //
96 // The current selector doesn't match the parent selector. 99 // The current selector doesn't match the parent selector.
97 // The user manually added a selector between parent context and this suffix call. 100 // The user manually added a selector between parent context and this suffix call.
98 // This case is forbidden because any outcome semantically wouldn't make sense: 101 // This case is forbidden because any outcome semantically wouldn't make sense:
99 // - {b,e,m} [manual selector] {b,e,m}@suffix 102 // - {b,e,m} [manual selector] {b,e,m}@suffix
100 // - {b,e,m}@suffix [manual selector] 103 // - {b,e,m}@suffix [manual selector]
101 // The first case would make the modifier behave like an element. 104 // The first case would make the modifier behave like an element.
102 // The second case is unintuitive, the code would be more clear by nesting the manual 105 // The second case is unintuitive, the code would be more clear by nesting the manual
103 // selector in the suffix instead. 106 // selector in the suffix instead.
104 // 107 //
105 108
106 @error 'A suffix must be an immediate child of the parent context'; 109 @error 'A suffix must be an immediate child of the parent context';
107 } 110 }
108 111
109 // 112 //
110 // The suffix part can simply be appended. 113 // The suffix part can simply be appended.
111 // Possible outcomes: 114 // Possible outcomes:
112 // - {b,e,m}@suffix 115 // - {b,e,m}@suffix
113 // 116 //
114 117
115 $selector: selector-append(&, vars.$suffix-separator + $name); 118 $selector: selector.append(&, vars.$suffix-separator + $name);
116 119
117 $context: 'suffix', ( 120 $context: 'suffix', (
118 'name': $name, 121 'name': $name,
119 'selector': $selector 122 'selector': $selector
120 ); 123 );
121 124
122 @return $selector $context; 125 @return $selector $context;
123} 126}
diff --git a/src/bem/_theme.scss b/src/bem/_theme.scss
index ff1ba49..535cc81 100644
--- a/src/bem/_theme.scss
+++ b/src/bem/_theme.scss
@@ -4,6 +4,9 @@
4/// @access public 4/// @access public
5//// 5////
6 6
7@use 'sass:list';
8@use 'sass:map';
9@use 'sass:selector';
7@use './functions'; 10@use './functions';
8@use './validators'; 11@use './validators';
9@use './vars'; 12@use './vars';
@@ -18,22 +21,22 @@
18/// @content 21/// @content
19/// 22///
20@mixin at-theme($name, $names...) { 23@mixin at-theme($name, $names...) {
21 $result: at-theme($name, $names...); 24 $result: at-theme($name, $names...);
22 $selector: nth($result, 1); 25 $selector: list.nth($result, 1);
23 $context: nth($result, 2); 26 $context: list.nth($result, 2);
24 27
25 @include validators.validate( 28 @include validators.validate(
26 'at-theme', 29 'at-theme',
27 (name: $name, names: $names), 30 (name: $name, names: $names),
28 $selector, 31 $selector,
29 $context 32 $context
30 ); 33 );
31 34
32 @include contexts.push(vars.$context-id, $context...); 35 @include contexts.push(vars.$context-id, $context...);
33 @at-root #{$selector} { 36 @at-root #{$selector} {
34 @content; 37 @content;
35 } 38 }
36 @include contexts.pop(vars.$context-id); 39 @include contexts.pop(vars.$context-id);
37} 40}
38 41
39/// 42///
@@ -45,22 +48,22 @@
45/// @see {mixin} at-theme 48/// @see {mixin} at-theme
46/// 49///
47@function at-theme($name, $names...) { 50@function at-theme($name, $names...) {
48 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'block'); 51 $noop: contexts.assert-stack-must-contain(vars.$context-id, 'block');
49 52
50 $parent-context: contexts.get(vars.$context-id, 'block'); 53 $parent-context: contexts.get(vars.$context-id, 'block');
51 $parent-selector: map-get(nth($parent-context, 2), 'selector'); 54 $parent-selector: map.get(list.nth($parent-context, 2), 'selector');
52 55
53 //@if not functions.selector-suffix-match(&, $parent-selector) { 56 //@if not functions.selector-suffix-match(&, $parent-selector) {
54 // @error 'An at-theme rule must be an immediate child of a block'; 57 // @error 'An at-theme rule must be an immediate child of a block';
55 //} 58 //}
56 59
57 $selector: functions.theme-selector($name, $names...); 60 $selector: functions.theme-selector($name, $names...);
58 $selector: selector-nest($selector, &); 61 $selector: selector.nest($selector, &);
59 62
60 $context: 'at-theme', ( 63 $context: 'at-theme', (
61 'name': join($name, $names), 64 'name': list.join($name, $names),
62 'selector': $selector 65 'selector': $selector
63 ); 66 );
64 67
65 @return $selector $context; 68 @return $selector $context;
66} 69}
diff --git a/src/bem/_validators.scss b/src/bem/_validators.scss
index 042e15e..bc3c9b7 100644
--- a/src/bem/_validators.scss
+++ b/src/bem/_validators.scss
@@ -16,6 +16,9 @@
16/// @access public 16/// @access public
17//// 17////
18 18
19@use 'sass:list';
20@use 'sass:map';
21@use 'sass:meta';
19@use './vars'; 22@use './vars';
20@use '../functions'; 23@use '../functions';
21@use '../contexts'; 24@use '../contexts';
@@ -46,7 +49,7 @@ $validators: ();
46/// @param {string} $func-names - Other function names. 49/// @param {string} $func-names - Other function names.
47/// 50///
48@mixin add($func-name, $func-names...) { 51@mixin add($func-name, $func-names...) {
49 $noop: add($func-name, $func-names...); 52 $noop: add($func-name, $func-names...);
50} 53}
51 54
52/// 55///
@@ -55,11 +58,11 @@ $validators: ();
55/// @see {mixin} add 58/// @see {mixin} add
56/// 59///
57@function add($func-name, $func-names...) { 60@function add($func-name, $func-names...) {
58 @each $fn-name in join($func-name, $func-names) { 61 @each $fn-name in list.join($func-name, $func-names) {
59 $fn: get-function($fn-name); 62 $fn: meta.get-function($fn-name);
60 $validators: map-merge($validators, ($fn-name: $fn)); 63 $validators: map.merge($validators, ($fn-name: $fn));
61 } 64 }
62 @return null; 65 @return null;
63} 66}
64 67
65/// 68///
@@ -69,7 +72,7 @@ $validators: ();
69/// @param {string} $func-names - Other function names. 72/// @param {string} $func-names - Other function names.
70/// 73///
71@mixin remove($func-name, $func-names...) { 74@mixin remove($func-name, $func-names...) {
72 $noop: remove($func-name, $func-names...); 75 $noop: remove($func-name, $func-names...);
73} 76}
74 77
75/// 78///
@@ -78,20 +81,20 @@ $validators: ();
78/// @see {mixin} remove 81/// @see {mixin} remove
79/// 82///
80@function remove($func-name, $func-names...) { 83@function remove($func-name, $func-names...) {
81 $validators: map-remove($validators, $func-name, $func-names...); 84 $validators: map.remove($validators, $func-name, $func-names...);
82 @return null; 85 @return null;
83} 86}
84 87
85/// 88///
86/// @access private 89/// @access private
87/// 90///
88@mixin validate($type, $args, $selector, $context) { 91@mixin validate($type, $args, $selector, $context) {
89 @each $id, $fn in $validators { 92 @each $id, $fn in $validators {
90 $result: call($fn, $type, $args, $selector, $context); 93 $result: meta.call($fn, $type, $args, $selector, $context);
91 @if not nth($result, 1) { 94 @if not list.nth($result, 1) {
92 @error 'A BEM validator rejected this mixin usage due to the following reason: #{nth($result, 2)}'; 95 @error 'A BEM validator rejected this mixin usage due to the following reason: #{nth($result, 2)}';
93 } 96 }
94 } 97 }
95} 98}
96 99
97// 100//
@@ -105,76 +108,76 @@ $validators: ();
105/// namespace used. 108/// namespace used.
106/// 109///
107@function enforce-namespace-order($type, $args, $selector, $context) { 110@function enforce-namespace-order($type, $args, $selector, $context) {
108 @if not global-variable-exists(namespace-order, vars) { 111 @if not meta.global-variable-exists(namespace-order, vars) {
109 vars.$namespace-order: map-keys(vars.$namespaces); 112 vars.$namespace-order: map.keys(vars.$namespaces);
110 } 113 }
111 @if not global-variable-exists(cur-namespace-index, vars) { 114 @if not meta.global-variable-exists(cur-namespace-index, vars) {
112 vars.$cur-namespace-index: 1; 115 vars.$cur-namespace-index: 1;
113 } 116 }
114 117
115 @if $type == 'block' { 118 @if $type == 'block' {
116 $block-type: map-get($args, 'type'); 119 $block-type: map.get($args, 'type');
117 120
118 @if $block-type != null { 121 @if $block-type != null {
119 $ns-index: index(vars.$namespace-order, $block-type); 122 $ns-index: list.index(vars.$namespace-order, $block-type);
120 123
121 @if $ns-index != null { 124 @if $ns-index != null {
122 @if $ns-index < vars.$cur-namespace-index { 125 @if $ns-index < vars.$cur-namespace-index {
123 @return false 'Namespace "#{$block-type}" comes before current namespace #{nth(vars.$namespace-order, vars.$cur-namespace-index)}'; 126 @return false 'Namespace "#{$block-type}" comes before current namespace #{nth(vars.$namespace-order, vars.$cur-namespace-index)}';
124 } 127 }
125 128
126 vars.$cur-namespace-index: $ns-index; 129 vars.$cur-namespace-index: $ns-index;
127 } 130 }
128 } 131 }
129 } 132 }
130 133
131 @return true ''; 134 @return true '';
132} 135}
133 136
134/// 137///
135/// A validator that makes all BEM entities immutable. 138/// A validator that makes all BEM entities immutable.
136/// 139///
137@function immutable-entities($type, $args, $selector, $context) { 140@function immutable-entities($type, $args, $selector, $context) {
138 @if not global-variable-exists(generated-selectors, vars) { 141 @if not meta.global-variable-exists(generated-selectors, vars) {
139 vars.$generated-selectors: (); 142 vars.$generated-selectors: ();
140 } 143 }
141 144
142 $block-name: null; 145 $block-name: null;
143 $block-type: null; 146 $block-type: null;
144 $block-id: null; 147 $block-id: null;
145 148
146 @if $type == 'block' { 149 @if $type == 'block' {
147 $block-name: map-get($args, 'name'); 150 $block-name: map.get($args, 'name');
148 $block-type: map-get($args, 'type'); 151 $block-type: map.get($args, 'type');
149 } @else { 152 } @else {
150 $block-context: contexts.get(vars.$context-id, 'block'); 153 $block-context: contexts.get(vars.$context-id, 'block');
151 $block-name: map-get(nth($block-context, 2), 'name'); 154 $block-name: map.get(list.nth($block-context, 2), 'name');
152 $block-type: map-get(nth($block-context, 2), 'type'); 155 $block-type: map.get(list.nth($block-context, 2), 'type');
153 } 156 }
154 157
155 @if $block-type != null { 158 @if $block-type != null {
156 $block-id: $block-name + '_' + $block-type; 159 $block-id: $block-name + '_' + $block-type;
157 } @else { 160 } @else {
158 $block-id: $block-name; 161 $block-id: $block-name;
159 } 162 }
160 163
161 @if $type == 'block' { 164 @if $type == 'block' {
162 @if map-has-key(vars.$generated-selectors, $block-id) { 165 @if map.has-key(vars.$generated-selectors, $block-id) {
163 @return false 'Entity "#{$type}" with arguments [ #{functions.map-print($args)} ] was already defined.'; 166 @return false 'Entity "#{$type}" with arguments [ #{functions.map-print($args)} ] was already defined.';
164 } 167 }
165 168
166 vars.$generated-selectors: map-merge(vars.$generated-selectors, ($block-id: ())); 169 vars.$generated-selectors: map.merge(vars.$generated-selectors, ($block-id: ()));
167 } @else { 170 } @else {
168 $selectors: map-get(vars.$generated-selectors, $block-id); 171 $selectors: map.get(vars.$generated-selectors, $block-id);
169 172
170 @if index($selectors, $selector) { 173 @if list.index($selectors, $selector) {
171 @return false 'Entity "#{$type}" with arguments [ #{functions.map-print($args)} ] was already defined.'; 174 @return false 'Entity "#{$type}" with arguments [ #{functions.map-print($args)} ] was already defined.';
172 } 175 }
173 176
174 $selectors: append($selectors, $selector); 177 $selectors: list.append($selectors, $selector);
175 178
176 vars.$generated-selectors: map-merge(vars.$generated-selectors, ($block-id: $selectors)); 179 vars.$generated-selectors: map.merge(vars.$generated-selectors, ($block-id: $selectors));
177 } 180 }
178 181
179 @return true ''; 182 @return true '';
180} 183}
diff --git a/src/bem/_vars.scss b/src/bem/_vars.scss
index 3d0f92a..823bf0a 100644
--- a/src/bem/_vars.scss
+++ b/src/bem/_vars.scss
@@ -41,15 +41,15 @@ $suffix-separator: '\\@' !default;
41/// @type map 41/// @type map
42/// 42///
43$namespaces: ( 43$namespaces: (
44 object: 'o', 44 object: 'o',
45 component: 'c', 45 component: 'c',
46 layout: 'l', 46 layout: 'l',
47 scope: 's', 47 scope: 's',
48 theme: 't', 48 theme: 't',
49 utility: 'u', 49 utility: 'u',
50 js: 'js', 50 js: 'js',
51 qa: 'qa', 51 qa: 'qa',
52 hack: '_' 52 hack: '_'
53) !default; 53) !default;
54 54
55/// 55///
@@ -100,9 +100,9 @@ $debug: false !default;
100/// @type map 100/// @type map
101/// 101///
102$debug-colors: ( 102$debug-colors: (
103 object: #ffa500, 103 object: #ffa500,
104 component: #00f, 104 component: #00f,
105 layout: #ff0, 105 layout: #ff0,
106 utility: #008000, 106 utility: #008000,
107 hack: #f00 107 hack: #f00
108) !default; 108) !default;