@volverjs/style 0.1.29 → 0.1.30-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/design-tokens.json +503 -7
  2. package/dist/base.css +1 -1
  3. package/dist/components/vv-alert.css +1 -1
  4. package/dist/components/vv-attachment-group.css +1 -0
  5. package/dist/components/vv-attachment.css +1 -0
  6. package/dist/components/vv-avatar.css +1 -1
  7. package/dist/components/vv-badge.css +1 -1
  8. package/dist/components/vv-breadcrumb.css +1 -1
  9. package/dist/components/vv-bubble-group.css +1 -0
  10. package/dist/components/vv-bubble.css +1 -0
  11. package/dist/components/vv-button.css +1 -1
  12. package/dist/components/vv-calendar.css +1 -0
  13. package/dist/components/vv-checkbox-group.css +1 -1
  14. package/dist/components/vv-checkbox.css +1 -1
  15. package/dist/components/vv-command.css +1 -0
  16. package/dist/components/vv-dialog.css +1 -1
  17. package/dist/components/vv-dropdown-action.css +1 -1
  18. package/dist/components/vv-dropdown-option.css +1 -1
  19. package/dist/components/vv-empty.css +1 -0
  20. package/dist/components/vv-field.css +1 -0
  21. package/dist/components/vv-input-file.css +1 -1
  22. package/dist/components/vv-input-otp.css +1 -0
  23. package/dist/components/vv-input-range.css +1 -1
  24. package/dist/components/vv-input-text.css +1 -1
  25. package/dist/components/vv-item-group.css +1 -0
  26. package/dist/components/vv-item.css +1 -0
  27. package/dist/components/vv-kbd-group.css +1 -0
  28. package/dist/components/vv-kbd.css +1 -0
  29. package/dist/components/vv-marker.css +1 -0
  30. package/dist/components/vv-message-scroller.css +1 -0
  31. package/dist/components/vv-message.css +1 -0
  32. package/dist/components/vv-nav.css +1 -1
  33. package/dist/components/vv-pagination.css +1 -0
  34. package/dist/components/vv-popover.css +1 -0
  35. package/dist/components/vv-progress.css +1 -1
  36. package/dist/components/vv-prose.css +1 -0
  37. package/dist/components/vv-questionnaire.css +1 -0
  38. package/dist/components/vv-radio-group.css +1 -1
  39. package/dist/components/vv-radio.css +1 -1
  40. package/dist/components/vv-select.css +1 -1
  41. package/dist/components/vv-separator.css +1 -0
  42. package/dist/components/vv-sidebar.css +1 -0
  43. package/dist/components/vv-spinner.css +1 -0
  44. package/dist/components/vv-table.css +1 -1
  45. package/dist/components/vv-text.css +1 -1
  46. package/dist/components/vv-textarea.css +1 -1
  47. package/dist/components.css +1 -1
  48. package/dist/export.css +1 -1
  49. package/dist/props/colors.css +1 -1
  50. package/dist/props/input.css +1 -1
  51. package/dist/props.css +1 -1
  52. package/dist/themes/dark/components.css +1 -1
  53. package/dist/themes/dark/props.css +1 -1
  54. package/dist/themes/dark/volver.css +1 -1
  55. package/dist/utilities/colors.css +1 -1
  56. package/dist/utilities/layout.css +1 -1
  57. package/dist/utilities.css +1 -1
  58. package/dist/volver.css +1 -1
  59. package/package.json +49 -1
  60. package/src/_config.scss +23 -0
  61. package/src/_preflight.scss +14 -2
  62. package/src/components/index.scss +28 -0
  63. package/src/components/vv-attachment-group.scss +17 -0
  64. package/src/components/vv-attachment.scss +17 -0
  65. package/src/components/vv-bubble-group.scss +17 -0
  66. package/src/components/vv-bubble.scss +17 -0
  67. package/src/components/vv-calendar.scss +25 -0
  68. package/src/components/vv-command.scss +17 -0
  69. package/src/components/vv-empty.scss +17 -0
  70. package/src/components/vv-field.scss +25 -0
  71. package/src/components/vv-input-otp.scss +17 -0
  72. package/src/components/vv-item-group.scss +17 -0
  73. package/src/components/vv-item.scss +39 -0
  74. package/src/components/vv-kbd-group.scss +17 -0
  75. package/src/components/vv-kbd.scss +17 -0
  76. package/src/components/vv-marker.scss +17 -0
  77. package/src/components/vv-message-scroller.scss +24 -0
  78. package/src/components/vv-message.scss +25 -0
  79. package/src/components/vv-pagination.scss +17 -0
  80. package/src/components/vv-popover.scss +17 -0
  81. package/src/components/vv-prose.scss +17 -0
  82. package/src/components/vv-questionnaire.scss +28 -0
  83. package/src/components/vv-separator.scss +17 -0
  84. package/src/components/vv-sidebar.scss +17 -0
  85. package/src/components/vv-spinner.scss +27 -0
  86. package/src/presets/_outlined-fields.scss +346 -0
  87. package/src/props/colors.scss +6 -0
  88. package/src/settings/_colors.scss +161 -61
  89. package/src/settings/_index.scss +26 -0
  90. package/src/settings/_input.scss +9 -0
  91. package/src/settings/components/_choice.scss +104 -0
  92. package/src/settings/components/_vv-alert.scss +37 -12
  93. package/src/settings/components/_vv-attachment-group.scss +10 -0
  94. package/src/settings/components/_vv-attachment.scss +135 -0
  95. package/src/settings/components/_vv-avatar.scss +17 -7
  96. package/src/settings/components/_vv-badge.scss +21 -13
  97. package/src/settings/components/_vv-breadcrumb.scss +1 -1
  98. package/src/settings/components/_vv-bubble-group.scss +31 -0
  99. package/src/settings/components/_vv-bubble.scss +69 -0
  100. package/src/settings/components/_vv-button.scss +48 -5
  101. package/src/settings/components/_vv-calendar.scss +102 -0
  102. package/src/settings/components/_vv-checkbox-group.scss +33 -2
  103. package/src/settings/components/_vv-checkbox.scss +7 -2
  104. package/src/settings/components/_vv-command.scss +99 -0
  105. package/src/settings/components/_vv-dialog.scss +129 -26
  106. package/src/settings/components/_vv-dropdown-action.scss +2 -1
  107. package/src/settings/components/_vv-dropdown-option.scss +3 -3
  108. package/src/settings/components/_vv-empty.scss +71 -0
  109. package/src/settings/components/_vv-field.scss +111 -0
  110. package/src/settings/components/_vv-input-file.scss +6 -6
  111. package/src/settings/components/_vv-input-otp.scss +55 -0
  112. package/src/settings/components/_vv-input-range.scss +2 -2
  113. package/src/settings/components/_vv-input-text.scss +6 -6
  114. package/src/settings/components/_vv-item-group.scss +68 -0
  115. package/src/settings/components/_vv-item.scss +201 -0
  116. package/src/settings/components/_vv-kbd-group.scss +9 -0
  117. package/src/settings/components/_vv-kbd.scss +20 -0
  118. package/src/settings/components/_vv-marker.scss +32 -0
  119. package/src/settings/components/_vv-message-scroller.scss +65 -0
  120. package/src/settings/components/_vv-message.scss +95 -0
  121. package/src/settings/components/_vv-nav.scss +7 -3
  122. package/src/settings/components/_vv-pagination.scss +78 -0
  123. package/src/settings/components/_vv-popover.scss +53 -0
  124. package/src/settings/components/_vv-progress.scss +4 -4
  125. package/src/settings/components/_vv-prose.scss +274 -0
  126. package/src/settings/components/_vv-questionnaire.scss +77 -0
  127. package/src/settings/components/_vv-radio.scss +6 -2
  128. package/src/settings/components/_vv-select.scss +5 -5
  129. package/src/settings/components/_vv-separator.scss +79 -0
  130. package/src/settings/components/_vv-sidebar.scss +91 -0
  131. package/src/settings/components/_vv-spinner.scss +26 -0
  132. package/src/settings/components/_vv-table.scss +63 -0
  133. package/src/settings/components/_vv-text.scss +2 -2
  134. package/src/settings/components/_vv-textarea.scss +6 -6
  135. package/src/themes/dark/components/_vv-checkbox.scss +27 -0
  136. package/src/themes/dark/components/_vv-radio.scss +27 -0
  137. package/src/themes/dark/components/index.scss +2 -0
  138. package/src/themes/dark/props/_colors.scss +6 -0
  139. package/src/themes/dark/settings/_colors.scss +97 -32
  140. package/src/themes/dark/settings/_index.scss +2 -0
  141. package/src/themes/dark/settings/components/_choice.scss +15 -0
  142. package/src/themes/dark/settings/components/_vv-alert.scss +6 -48
  143. package/src/themes/dark/settings/components/_vv-avatar.scss +1 -16
  144. package/src/themes/dark/settings/components/_vv-badge.scss +1 -32
  145. package/src/themes/dark/settings/components/_vv-button.scss +0 -8
  146. package/src/themes/dark/settings/components/_vv-checkbox.scss +7 -0
  147. package/src/themes/dark/settings/components/_vv-dropdown-action.scss +2 -3
  148. package/src/themes/dark/settings/components/_vv-dropdown-option.scss +2 -19
  149. package/src/themes/dark/settings/components/_vv-nav.scss +2 -15
  150. package/src/themes/dark/settings/components/_vv-radio.scss +7 -0
  151. package/src/tools/_functions.scss +430 -2
  152. package/src/tools/mixin-modules/_bem.scss +5 -39
  153. package/src/tools/mixin-modules/_states.scss +77 -0
  154. package/src/tools/mixin-modules/_theme.scss +135 -121
  155. package/src/utilities/colors.scss +49 -4
  156. package/src/utilities/layout.scss +25 -0
@@ -1,9 +1,11 @@
1
1
  @use 'sass:map';
2
2
  @use 'sass:list';
3
3
  @use 'sass:meta';
4
+ @use 'sass:selector';
4
5
  @use 'props' as *;
5
6
  @use 'attrs' as *;
6
7
  @use 'helpers' as *;
8
+ @use 'states' as *;
7
9
 
8
10
  // ============================================
9
11
  // Theme component utilities
@@ -103,23 +105,15 @@
103
105
  );
104
106
  }
105
107
 
106
- // Process modifiers recursively
107
- @if map.has-key($theme-map, 'modifier') {
108
- @include _theme-spread-modifiers(
109
- $map: map.get($theme-map, 'modifier'),
110
- $prefix: $component-name,
111
- $block: $selector,
112
- $bps: $bps,
113
- $zero-specificity: $zero-specificity
114
- );
115
- }
116
-
117
- // Process elements
108
+ // Elements, states, then modifiers: the order of the light walk, so
109
+ // that at equal specificity a modifier wins over a state of the block
110
+ // in the dark theme as it does in the light one
118
111
  @if map.has-key($theme-map, 'element') {
119
112
  @include _theme-spread-elements(
120
113
  $map: map.get($theme-map, 'element'),
121
114
  $prefix: $component-name,
122
115
  $block: $selector,
116
+ $base-map: $base-map,
123
117
  $bps: $bps,
124
118
  $zero-specificity: $zero-specificity
125
119
  );
@@ -132,6 +126,19 @@
132
126
  $prefix: $component-name,
133
127
  $block: $selector,
134
128
  $parent-selector: $selector,
129
+ $base-map: $base-map,
130
+ $bps: $bps,
131
+ $zero-specificity: $zero-specificity
132
+ );
133
+ }
134
+
135
+ // Process modifiers recursively
136
+ @if map.has-key($theme-map, 'modifier') {
137
+ @include _theme-spread-modifiers(
138
+ $map: map.get($theme-map, 'modifier'),
139
+ $prefix: $component-name,
140
+ $block: $selector,
141
+ $base-map: $base-map,
135
142
  $bps: $bps,
136
143
  $zero-specificity: $zero-specificity
137
144
  );
@@ -144,6 +151,7 @@
144
151
  $map: $light-reset-map,
145
152
  $prefix: $component-name,
146
153
  $block: $selector,
154
+ $base-map: $base-map,
147
155
  $bps: $bps,
148
156
  $zero-specificity: $zero-specificity
149
157
  );
@@ -152,8 +160,8 @@
152
160
  }
153
161
  }
154
162
 
155
- // Helper to generate all theme rules (block, modifiers, elements, states)
156
- @mixin _theme-generate-all($map, $prefix, $block, $bps, $zero-specificity: true) {
163
+ // Helper to generate all theme rules (block, elements, states, modifiers)
164
+ @mixin _theme-generate-all($map, $prefix, $block, $bps, $base-map: (), $zero-specificity: true) {
157
165
  // Block styles
158
166
  @include wrap-with-where($selector: $block, $enabled: $zero-specificity) {
159
167
  @include spread-map-into-attrs(
@@ -164,23 +172,13 @@
164
172
  );
165
173
  }
166
174
 
167
- // Process modifiers
168
- @if map.has-key($map, 'modifier') {
169
- @include _theme-spread-modifiers(
170
- $map: map.get($map, 'modifier'),
171
- $prefix: $prefix,
172
- $block: $block,
173
- $bps: $bps,
174
- $zero-specificity: $zero-specificity
175
- );
176
- }
177
-
178
- // Process elements
175
+ // Elements, states, then modifiers, as above
179
176
  @if map.has-key($map, 'element') {
180
177
  @include _theme-spread-elements(
181
178
  $map: map.get($map, 'element'),
182
179
  $prefix: $prefix,
183
180
  $block: $block,
181
+ $base-map: $base-map,
184
182
  $bps: $bps,
185
183
  $zero-specificity: $zero-specificity
186
184
  );
@@ -193,6 +191,19 @@
193
191
  $prefix: $prefix,
194
192
  $block: $block,
195
193
  $parent-selector: $block,
194
+ $base-map: $base-map,
195
+ $bps: $bps,
196
+ $zero-specificity: $zero-specificity
197
+ );
198
+ }
199
+
200
+ // Process modifiers
201
+ @if map.has-key($map, 'modifier') {
202
+ @include _theme-spread-modifiers(
203
+ $map: map.get($map, 'modifier'),
204
+ $prefix: $prefix,
205
+ $block: $block,
206
+ $base-map: $base-map,
196
207
  $bps: $bps,
197
208
  $zero-specificity: $zero-specificity
198
209
  );
@@ -203,8 +214,39 @@
203
214
  // Private helper mixins for theme spreading
204
215
  // ============================================
205
216
 
217
+ // Every selector of a list with a suffix appended, one entry at a time.
218
+ // Appended to the list as a whole, the suffix would reach its last entry
219
+ // alone and leave the others bare (see AGENTS.md).
220
+ // @param {String|List} $selectors - Selector list
221
+ // @param {String} $suffix - Compound suffix (a class, an attribute, a pseudo-class)
222
+ // @return {List} Comma separated list of selector strings
223
+ @function -theme-suffix($selectors, $suffix) {
224
+ $result: ();
225
+
226
+ @each $item in selector.parse($selectors) {
227
+ $result: list.append($result, '#{$item}#{$suffix}', $separator: comma);
228
+ }
229
+
230
+ @return $result;
231
+ }
232
+
233
+ // Every selector of a list with a descendant nested under it, one entry at a
234
+ // time, the way the light walk nests an element or an alias under a modifier
235
+ // @param {String|List} $parents - Selector list
236
+ // @param {String} $child - Descendant, possibly starting with a combinator
237
+ // @return {List} Comma separated list of selector strings
238
+ @function -theme-nest($parents, $child) {
239
+ $result: ();
240
+
241
+ @each $item in selector.parse($parents) {
242
+ $result: list.append($result, '#{selector.nest($item, $child)}', $separator: comma);
243
+ }
244
+
245
+ @return $result;
246
+ }
247
+
206
248
  // Spread modifiers for theme (no @extend)
207
- @mixin _theme-spread-modifiers($map, $prefix, $block, $parent-selector: null, $bps: (xxs: 0), $zero-specificity: true) {
249
+ @mixin _theme-spread-modifiers($map, $prefix, $block, $parent-selector: null, $base-map: (), $bps: (xxs: 0), $zero-specificity: true) {
208
250
  @each $mod-name, $mod-value in $map {
209
251
  $mod-selector: '#{$block}#{$block}--#{$mod-name}';
210
252
 
@@ -229,6 +271,7 @@
229
271
  $prefix: $prefix,
230
272
  $block: $block,
231
273
  $parent-selector: $mod-selector,
274
+ $base-map: $base-map,
232
275
  $bps: $bps,
233
276
  $zero-specificity: $zero-specificity
234
277
  );
@@ -241,6 +284,7 @@
241
284
  $prefix: $prefix,
242
285
  $block: $block,
243
286
  $parent-selector: $mod-selector,
287
+ $base-map: $base-map,
244
288
  $bps: $bps,
245
289
  $zero-specificity: $zero-specificity
246
290
  );
@@ -253,6 +297,7 @@
253
297
  $prefix: $prefix,
254
298
  $block: $block,
255
299
  $parent-selector: $mod-selector,
300
+ $base-map: $base-map,
256
301
  $bps: $bps,
257
302
  $zero-specificity: $zero-specificity
258
303
  );
@@ -261,17 +306,36 @@
261
306
  }
262
307
 
263
308
  // Spread elements for theme (no @extend)
264
- @mixin _theme-spread-elements($map, $prefix, $block, $parent-selector: null, $bps: (xxs: 0), $zero-specificity: true) {
309
+ // The `_alias` of an element lives in the base map, not in the theme map, so
310
+ // `$base-map` is threaded down to find it: without it a dark override reached
311
+ // `.block__element` alone and missed the plain markup the alias is there for.
312
+ // Under a parent (a modifier, a state) the element and its alias are nested
313
+ // under every selector of the parent, like the light walk does.
314
+ @mixin _theme-spread-elements($map, $prefix, $block, $parent-selector: null, $base-map: (), $bps: (xxs: 0), $zero-specificity: true) {
265
315
  @each $el-name, $el-value in $map {
266
316
  $el-selector: '#{$block}__#{$el-name}';
267
- $full-selector: $el-selector;
317
+ $alias: map.get($el-value, '_alias') or map.get($base-map, 'element', $el-name, '_alias');
318
+ $selectors: $el-selector;
319
+ $alias-selectors: null;
268
320
 
269
321
  @if $parent-selector {
270
- $full-selector: '#{$parent-selector} #{$el-selector}';
322
+ $selectors: -theme-nest($parent-selector, $el-selector);
323
+
324
+ @if $alias {
325
+ $alias-selectors: -theme-nest($parent-selector, $alias);
326
+ }
327
+ } @else if $alias {
328
+ $alias-selectors: '#{$block} #{$alias}';
329
+ }
330
+
331
+ $rule-selectors: $selectors;
332
+
333
+ @if $alias-selectors {
334
+ $rule-selectors: list.join($selectors, $alias-selectors, $separator: comma);
271
335
  }
272
336
 
273
337
  // Element base styles
274
- @include wrap-with-where($selector: $full-selector, $enabled: $zero-specificity) {
338
+ @include wrap-with-where($selector: $rule-selectors, $enabled: $zero-specificity) {
275
339
  @include spread-map-into-attrs(
276
340
  $map: $el-value,
277
341
  $prefix: $prefix,
@@ -280,13 +344,17 @@
280
344
  );
281
345
  }
282
346
 
283
- // States inside element
347
+ // States inside element: on the element itself, wherever it sits.
348
+ // They used to take the parent as their base, which put the state of
349
+ // an element under a modifier on the modifier block instead.
284
350
  @if map.has-key($el-value, 'state') {
285
351
  @include _theme-spread-states(
286
352
  $map: map.get($el-value, 'state'),
287
353
  $prefix: $prefix,
288
354
  $block: $el-selector,
289
- $parent-selector: $parent-selector,
355
+ $parent-selector: $selectors,
356
+ $alias-selector: $alias-selectors,
357
+ $base-map: $base-map,
290
358
  $bps: $bps,
291
359
  $zero-specificity: $zero-specificity
292
360
  );
@@ -295,16 +363,13 @@
295
363
  }
296
364
 
297
365
  // Spread states for theme (no @extend)
298
- @mixin _theme-spread-states($map, $prefix, $block, $parent-selector: null, $bps: (xxs: 0), $zero-specificity: true) {
299
- $state-list: (
300
- active, focus, hover, focus-within, target, visited, focus-visible,
301
- disabled, checked, checked-within, multiple, selected, readonly,
302
- empty, placeholder-shown, dirty, valid, invalid, indeterminate,
303
- first-child, last-child, open, close, determinate, pressed, current
304
- );
305
-
366
+ // `$parent-selector` is the selector the state compounds onto (the block, a
367
+ // modifier, an element, possibly a list of them) and `$alias-selector` the
368
+ // alias of an element, which takes the `.state` class and the suffixes like
369
+ // the light walk gives it, but not the `--state` modifier.
370
+ @mixin _theme-spread-states($map, $prefix, $block, $parent-selector: null, $alias-selector: null, $base-map: (), $bps: (xxs: 0), $zero-specificity: true) {
306
371
  @each $state-name, $state-value in $map {
307
- @if list.index($state-list, $state-name) {
372
+ @if list.index($valid-states, $state-name) {
308
373
  $base: $block;
309
374
 
310
375
  @if $parent-selector {
@@ -313,11 +378,16 @@
313
378
 
314
379
  // BEM modifier style: .block--state
315
380
  // Class style: .block.state
316
- $state-selectors: (
317
- '#{$base}#{$block}--#{$state-name}',
318
- '#{$base}.#{$state-name}'
381
+ $state-selectors: list.join(
382
+ -theme-suffix($base, '#{$block}--#{$state-name}'),
383
+ -theme-suffix($base, '.#{$state-name}'),
384
+ $separator: comma
319
385
  );
320
386
 
387
+ @if $alias-selector {
388
+ $state-selectors: list.join($state-selectors, -theme-suffix($alias-selector, '.#{$state-name}'), $separator: comma);
389
+ }
390
+
321
391
  @include wrap-with-where($selector: $state-selectors, $enabled: $zero-specificity) {
322
392
  @include spread-map-into-attrs(
323
393
  $map: $state-value,
@@ -327,81 +397,22 @@
327
397
  );
328
398
  }
329
399
 
330
- // Pseudo-class style based on state type
331
- @if $state-name == 'disabled' or $state-name == 'readonly' or $state-name == 'multiple' {
332
- @include wrap-with-where($selector: '#{$base}[#{$state-name}]', $enabled: $zero-specificity) {
333
- @include spread-map-into-attrs(
334
- $map: $state-value,
335
- $prefix: $prefix,
336
- $use-custom-props: false,
337
- $ignore: (modifier, element, state, breakpoint)
338
- );
339
- }
340
- } @else if $state-name == 'focus-visible' {
341
- @include wrap-with-where($selector: '#{$base}:not(:active):not([disabled]):not([tabindex="-1"]):not([aria-disabled="true"]):#{$state-name}', $enabled: $zero-specificity) {
342
- @include spread-map-into-attrs(
343
- $map: $state-value,
344
- $prefix: $prefix,
345
- $use-custom-props: false,
346
- $ignore: (modifier, element, state, breakpoint)
347
- );
348
- }
349
- } @else if $state-name == 'selected' {
350
- @include wrap-with-where($selector: '#{$base}[aria-selected="true"]', $enabled: $zero-specificity) {
351
- @include spread-map-into-attrs(
352
- $map: $state-value,
353
- $prefix: $prefix,
354
- $use-custom-props: false,
355
- $ignore: (modifier, element, state, breakpoint)
356
- );
357
- }
358
- } @else if $state-name == 'pressed' {
359
- @include wrap-with-where($selector: '#{$base}[aria-pressed="true"]', $enabled: $zero-specificity) {
360
- @include spread-map-into-attrs(
361
- $map: $state-value,
362
- $prefix: $prefix,
363
- $use-custom-props: false,
364
- $ignore: (modifier, element, state, breakpoint)
365
- );
366
- }
367
- } @else if $state-name == 'checked-within' {
368
- @include wrap-with-where($selector: '#{$base}:has(input:checked)', $enabled: $zero-specificity) {
369
- @include spread-map-into-attrs(
370
- $map: $state-value,
371
- $prefix: $prefix,
372
- $use-custom-props: false,
373
- $ignore: (modifier, element, state, breakpoint)
374
- );
375
- }
376
- } @else if $state-name == 'open' {
377
- @include wrap-with-where($selector: '#{$base}[open]', $enabled: $zero-specificity) {
378
- @include spread-map-into-attrs(
379
- $map: $state-value,
380
- $prefix: $prefix,
381
- $use-custom-props: false,
382
- $ignore: (modifier, element, state, breakpoint)
383
- );
384
- }
385
- } @else if $state-name == 'close' {
386
- @include wrap-with-where($selector: '#{$base}:not([open])', $enabled: $zero-specificity) {
387
- @include spread-map-into-attrs(
388
- $map: $state-value,
389
- $prefix: $prefix,
390
- $use-custom-props: false,
391
- $ignore: (modifier, element, state, breakpoint)
392
- );
393
- }
394
- } @else if $state-name == 'dirty' {
395
- @include wrap-with-where($selector: '#{$base}:not(:has(*:placeholder-shown))', $enabled: $zero-specificity) {
396
- @include spread-map-into-attrs(
397
- $map: $state-value,
398
- $prefix: $prefix,
399
- $use-custom-props: false,
400
- $ignore: (modifier, element, state, breakpoint)
401
- );
400
+ // The selectors the state reads from the DOM, from the table the
401
+ // light walk uses too. One rule for all of them, so a state with a
402
+ // single suffix comes out exactly as it did when this walk had its own
403
+ // chain of branches. "current" has none and stops at the classes above.
404
+ $suffix-selectors: ();
405
+
406
+ @each $suffix in state-suffixes($state-name) {
407
+ $suffix-selectors: list.join($suffix-selectors, -theme-suffix($base, $suffix), $separator: comma);
408
+
409
+ @if $alias-selector {
410
+ $suffix-selectors: list.join($suffix-selectors, -theme-suffix($alias-selector, $suffix), $separator: comma);
402
411
  }
403
- } @else if $state-name != 'current' {
404
- @include wrap-with-where($selector: '#{$base}:not([disabled]):#{$state-name}', $enabled: $zero-specificity) {
412
+ }
413
+
414
+ @if list.length($suffix-selectors) > 0 {
415
+ @include wrap-with-where($selector: $suffix-selectors, $enabled: $zero-specificity) {
405
416
  @include spread-map-into-attrs(
406
417
  $map: $state-value,
407
418
  $prefix: $prefix,
@@ -411,13 +422,16 @@
411
422
  }
412
423
  }
413
424
 
414
- // Elements inside state
425
+ // Elements inside state, under every selector the state matches:
426
+ // the classes and what it reads from the DOM. They used to hang
427
+ // under `.block--state` alone.
415
428
  @if map.has-key($state-value, 'element') {
416
429
  @include _theme-spread-elements(
417
430
  $map: map.get($state-value, 'element'),
418
431
  $prefix: $prefix,
419
432
  $block: $block,
420
- $parent-selector: '#{$base}#{$block}--#{$state-name}',
433
+ $parent-selector: list.join($state-selectors, $suffix-selectors, $separator: comma),
434
+ $base-map: $base-map,
421
435
  $bps: $bps,
422
436
  $zero-specificity: $zero-specificity
423
437
  );
@@ -1,9 +1,54 @@
1
+ @use 'sass:map';
2
+ @use 'sass:meta';
3
+ @use 'sass:string';
1
4
  @use '../context' as *;
2
5
 
6
+ // The colors map for one group of utilities. A cover only exists to hide the
7
+ // emboss of vv-button and gets no class. A contrast text and a readable role
8
+ // are text colors: they are left out of the fills ($text: false) and kept
9
+ // without a value for the `text-` classes ($text: true), which then read the
10
+ // token with no fallback, since the expression they would repeat reads the
11
+ // tokens of the props anyway. A graphic role is kept the same way in both.
12
+ @function -utility-colors($colors, $text) {
13
+ $result: ();
14
+
15
+ @each $key, $value in $colors {
16
+ @if meta.type-of($value) == 'map' {
17
+ $shades: ();
18
+
19
+ @each $shade, $color in $value {
20
+ $shade-name: '#{$shade}';
21
+
22
+ @if string.index($shade-name, 'readable') or string.index($shade-name, 'contrast') {
23
+ @if $text {
24
+ $shades: map.set($shades, $shade, null);
25
+ }
26
+ } @else if string.index($shade-name, 'graphic') {
27
+ $shades: map.set($shades, $shade, null);
28
+ } @else if not string.index($shade-name, 'cover') {
29
+ $shades: map.set($shades, $shade, $color);
30
+ }
31
+ }
32
+
33
+ $result: map.set($result, $key, $shades);
34
+ } @else {
35
+ $result: map.set($result, $key, $value);
36
+ }
37
+ }
38
+
39
+ @return $result;
40
+ }
41
+
42
+ // --color-tint is only the source the neutrals take their hue from, so it
43
+ // gets no utilities
44
+ $-colors: map.remove($colors, tint);
45
+ $-texts: -utility-colors($-colors, $text: true);
46
+ $-fills: -utility-colors($-colors, $text: false);
47
+
3
48
  @include layer(utilities, $use-css-layers, $layer-prefix) {
4
49
  // background-color
5
50
  @include spread-map-into-utilities(
6
- $map: $colors,
51
+ $map: $-fills,
7
52
  $class: bg,
8
53
  $attribute: background-color,
9
54
  $prop: color,
@@ -12,7 +57,7 @@
12
57
 
13
58
  // color
14
59
  @include spread-map-into-utilities(
15
- $map: $colors,
60
+ $map: $-texts,
16
61
  $class: text,
17
62
  $attribute: color,
18
63
  $prop: color,
@@ -21,7 +66,7 @@
21
66
 
22
67
  // text-decoration-color
23
68
  @include spread-map-into-utilities(
24
- $map: $colors,
69
+ $map: $-fills,
25
70
  $class: decoration,
26
71
  $attribute: text-decoration-color,
27
72
  $prop: color,
@@ -30,7 +75,7 @@
30
75
 
31
76
  // border-color
32
77
  @include spread-map-into-utilities(
33
- $map: $colors,
78
+ $map: $-fills,
34
79
  $class: border,
35
80
  $attribute: border-color,
36
81
  $prop: color,
@@ -115,6 +115,31 @@
115
115
  }
116
116
  }
117
117
 
118
+ // scrollbar: the native scrollbar through the standard properties, which
119
+ // is all a scrolling panel needs; no component draws a custom one
120
+ %scrollbar-thin {
121
+ scrollbar-width: thin;
122
+ scrollbar-color: var(--color-word-5) transparent;
123
+ }
124
+
125
+ @include wrap-with-where(
126
+ $selector: '.scrollbar-thin',
127
+ $enabled: $zero-specificity-for-utilities
128
+ ) {
129
+ @extend %scrollbar-thin;
130
+ }
131
+
132
+ %scrollbar-none {
133
+ scrollbar-width: none;
134
+ }
135
+
136
+ @include wrap-with-where(
137
+ $selector: '.scrollbar-none',
138
+ $enabled: $zero-specificity-for-utilities
139
+ ) {
140
+ @extend %scrollbar-none;
141
+ }
142
+
118
143
  // position
119
144
  @include spread-map-into-utilities(
120
145
  $map: $position,