@volverjs/style 0.1.25 → 0.1.26

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.
@@ -114,7 +114,7 @@ $vv-dialog: (
114
114
  opacity: var(--opacity-0),
115
115
  element: (
116
116
  wrapper: (
117
- translate: -100%,
117
+ [translate]: calc(-100% * var(--direction, 1)),
118
118
  ),
119
119
  ),
120
120
  ),
@@ -122,7 +122,7 @@ $vv-dialog: (
122
122
  opacity: var(--opacity-0),
123
123
  element: (
124
124
  wrapper: (
125
- translate: 100%,
125
+ [translate]: calc(100% * var(--direction, 1)),
126
126
  ),
127
127
  ),
128
128
  ),
@@ -145,6 +145,24 @@ $vv-dialog: (
145
145
  ),
146
146
  ),
147
147
  ),
148
+ slide-inline-end: (
149
+ active: (
150
+ transition: var(--transition-opacity),
151
+ element: (
152
+ wrapper: (
153
+ transition: var(--transition-transform),
154
+ ),
155
+ ),
156
+ ),
157
+ inert: (
158
+ opacity: var(--opacity-0),
159
+ element: (
160
+ wrapper: (
161
+ [translate]: calc(100% * var(--direction, 1)),
162
+ ),
163
+ ),
164
+ ),
165
+ ),
148
166
  ),
149
167
  modifier: (
150
168
  small: (
@@ -158,7 +176,7 @@ $vv-dialog: (
158
176
  padding: 0,
159
177
  element: (
160
178
  wrapper: (
161
- border-radius: var(--rounded-0),
179
+ border-radius: var(--rounded-none),
162
180
  max-width: auto,
163
181
  max-height: auto,
164
182
  width: 100%,
@@ -166,5 +184,40 @@ $vv-dialog: (
166
184
  ),
167
185
  ),
168
186
  ),
187
+ // Side panel anchored to the inline end, pair it with the
188
+ // slide-inline-end transition. Full width below sm.
189
+ drawer: (
190
+ padding: 0,
191
+ align-items: stretch,
192
+ justify-content: flex-end,
193
+ element: (
194
+ wrapper: (
195
+ width: 100%,
196
+ max-height: 100dvh,
197
+ border-radius: var(--rounded-none),
198
+ breakpoint: (
199
+ up-sm: (
200
+ width: min(75dvw, 32rem),
201
+ ),
202
+ ),
203
+ ),
204
+ // Flat header and footer: on a full height panel the filled
205
+ // bands read as heavy. The footer keeps its divider because
206
+ // the content above it scrolls.
207
+ header: (
208
+ font-weight: var(--font-bold),
209
+ background: var(--color-transparent),
210
+ border-bottom: 0,
211
+ ),
212
+ // Without the divider the header padding already spaces the
213
+ // title from the first field, so drop the duplicate.
214
+ content: (
215
+ padding-top: 0,
216
+ ),
217
+ footer: (
218
+ background: var(--color-transparent),
219
+ ),
220
+ ),
221
+ ),
169
222
  ),
170
223
  ) !default;
@@ -0,0 +1,189 @@
1
+ $vv-input-range: (
2
+ display: var(--input-display),
3
+ flex: var(--input-flex),
4
+ flex-direction: var(--input-flex-direction),
5
+ margin-bottom: var(--input-gap),
6
+ font-size: var(--input-font-size),
7
+ line-height: var(--input-line-height),
8
+ min-width: var(--input-min-width),
9
+ element: (
10
+ label: (
11
+ _alias: '> label',
12
+ _combinator: '>',
13
+ display: var(--input-label-display),
14
+ font-size: var(--input-label-font-size),
15
+ font-weight: var(--input-label-font-weight),
16
+ color: var(--input-label-color),
17
+ margin: 0 0 var(--input-label-gap) 0,
18
+ white-space: var(--input-label-white-space),
19
+ text-overflow: var(--input-label-text-overflow),
20
+ overflow: var(--input-label-overflow),
21
+ max-width: var(--input-label-max-width),
22
+ ),
23
+ wrapper: (
24
+ display: flex,
25
+ align-items: center,
26
+ gap: var(--spacing-sm),
27
+ min-height: var(--input-min-height),
28
+ ),
29
+ input-before: (
30
+ display: flex,
31
+ align-items: center,
32
+ ),
33
+ input-after: (
34
+ display: flex,
35
+ align-items: center,
36
+ ),
37
+ // Everything reading an --input-range-* token is written as a plain
38
+ // declaration: a var() inside a custom property is substituted where
39
+ // that property is declared, so wrapping these in the generated
40
+ // --vv-input-range-* props would freeze them at the :root values and
41
+ // the fill, the accent overrides and the hover halo would never move.
42
+ input: (
43
+ _alias: 'input[type="range"]',
44
+ -webkit-appearance: none,
45
+ appearance: none,
46
+ flex: 1,
47
+ min-width: 0,
48
+ [height]: var(--input-range-thumb-size),
49
+ margin: 0,
50
+ padding: 0,
51
+ background: transparent,
52
+ cursor: pointer,
53
+ pseudo: (
54
+ -webkit-slider-runnable-track: (
55
+ [height]: var(--input-range-track-height),
56
+ border-radius: var(--rounded-full),
57
+ [background]:
58
+ linear-gradient(
59
+ to right,
60
+ var(--input-range-accent-color)
61
+ var(--input-range-progress),
62
+ var(--input-range-track-color)
63
+ var(--input-range-progress)
64
+ ),
65
+ ),
66
+ -webkit-slider-thumb: (
67
+ -webkit-appearance: none,
68
+ appearance: none,
69
+ [width]: var(--input-range-thumb-size),
70
+ [height]: var(--input-range-thumb-size),
71
+ [margin-top]:
72
+ calc(
73
+ (
74
+ var(--input-range-track-height) -
75
+ var(--input-range-thumb-size)
76
+ ) / 2
77
+ ),
78
+ border: var(--border-2) solid var(--color-surface),
79
+ border-radius: var(--rounded-full),
80
+ [background]: var(--input-range-accent-color),
81
+ [box-shadow]: var(--input-range-thumb-shadow),
82
+ will-change: box-shadow,
83
+ transition: var(--transition-shadow),
84
+ ),
85
+ -moz-range-track: (
86
+ [height]: var(--input-range-track-height),
87
+ border-radius: var(--rounded-full),
88
+ [background]: var(--input-range-track-color),
89
+ ),
90
+ -moz-range-progress: (
91
+ [height]: var(--input-range-track-height),
92
+ border-radius: var(--rounded-full),
93
+ [background]: var(--input-range-accent-color),
94
+ ),
95
+ -moz-range-thumb: (
96
+ [width]: var(--input-range-thumb-size),
97
+ [height]: var(--input-range-thumb-size),
98
+ border: var(--border-2) solid var(--color-surface),
99
+ border-radius: var(--rounded-full),
100
+ [background]: var(--input-range-accent-color),
101
+ [box-shadow]: var(--input-range-thumb-shadow),
102
+ will-change: box-shadow,
103
+ transition: var(--transition-shadow),
104
+ ),
105
+ ),
106
+ state: (
107
+ hover: (
108
+ --input-range-thumb-shadow:
109
+ var(--input-range-thumb-shadow-hover),
110
+ ),
111
+ focus-visible: (
112
+ outline: var(--border-2) solid var(--color-brand),
113
+ outline-offset: var(--spacing-2),
114
+ ),
115
+ disabled: (
116
+ cursor: not-allowed,
117
+ ),
118
+ ),
119
+ ),
120
+ value: (
121
+ display: flex,
122
+ align-items: baseline,
123
+ justify-content: center,
124
+ gap: var(--spacing-2),
125
+ flex: 0 0 auto,
126
+ min-width: var(--spacing-64),
127
+ padding: var(--spacing-4) var(--spacing-sm),
128
+ font-size: var(--text-14),
129
+ font-variant-numeric: tabular-nums,
130
+ color: var(--input-color),
131
+ background-color: var(--input-background-color),
132
+ border: var(--border) solid var(--color-surface-3),
133
+ border-radius: var(--rounded-md),
134
+ text-align: center,
135
+ ),
136
+ unit: (
137
+ color: var(--input-placeholder-color),
138
+ font-size: var(--text-12),
139
+ ),
140
+ hint: (
141
+ _alias: '> small',
142
+ _combinator: '>',
143
+ display: var(--input-hint-display),
144
+ font-size: var(--input-hint-font-size),
145
+ color: var(--input-hint-color),
146
+ padding-top: var(--input-hint-gap),
147
+ padding-inline: var(--spacing-xs),
148
+ white-space: var(--input-hint-white-space),
149
+ text-overflow: var(--input-hint-text-overflow),
150
+ overflow: var(--input-hint-overflow),
151
+ ),
152
+ ),
153
+ modifier: (
154
+ valid: (
155
+ --input-range-accent-color: var(--input-valid-color),
156
+ element: (
157
+ hint: (
158
+ color: var(--input-valid-color),
159
+ ),
160
+ value: (
161
+ border-color: var(--input-valid-color),
162
+ ),
163
+ ),
164
+ ),
165
+ invalid: (
166
+ --input-range-accent-color: var(--input-invalid-color),
167
+ element: (
168
+ hint: (
169
+ color: var(--input-invalid-color),
170
+ ),
171
+ value: (
172
+ border-color: var(--input-invalid-color),
173
+ ),
174
+ ),
175
+ ),
176
+ readonly: (
177
+ --input-range-accent-color: var(--color-word-4),
178
+ opacity: var(--opacity-100),
179
+ cursor: default,
180
+ pointer-events: none,
181
+ ),
182
+ ),
183
+ state: (
184
+ disabled: (
185
+ --input-range-accent-color: var(--color-word-4),
186
+ opacity: var(--opacity-50),
187
+ ),
188
+ ),
189
+ ) !default;
@@ -4,7 +4,7 @@ $vv-tooltip: (
4
4
  position: absolute,
5
5
  inset-inline: 100% auto,
6
6
  top: 50%,
7
- translate: var(--spacing-10) -50%,
7
+ [translate]: calc(var(--spacing-10) * var(--direction, 1)) -50%,
8
8
  z-index: var(--z-tooltip),
9
9
  inline-size: max-content,
10
10
  max-inline-size: 25ch,
@@ -24,7 +24,8 @@ $vv-tooltip: (
24
24
  z-index: -1,
25
25
  inset-inline: 0 auto,
26
26
  inset-block: 50% auto,
27
- translate: -100% -50%,
27
+ [translate]: calc(-100% * var(--direction, 1)) -50%,
28
+ [scale]: var(--direction, 1) 1,
28
29
  border-width: var(--spacing-8),
29
30
  border-style: solid,
30
31
  border-color: transparent var(--color-gray-darken-4) transparent
@@ -38,12 +39,13 @@ $vv-tooltip: (
38
39
  top: (
39
40
  inset-inline: 50% 0,
40
41
  inset-block: auto 100%,
41
- translate: -50% calc(var(--spacing-10) * -1),
42
+ [translate]: calc(-50% * var(--direction, 1))
43
+ calc(var(--spacing-10) * -1),
42
44
  pseudo: (
43
45
  after: (
44
46
  inset-inline: 50% auto,
45
47
  inset-block: 100% auto,
46
- translate: -50%,
48
+ [translate]: calc(-50% * var(--direction, 1)),
47
49
  rotate: -90deg,
48
50
  ),
49
51
  ),
@@ -51,24 +53,24 @@ $vv-tooltip: (
51
53
  bottom: (
52
54
  inset-inline: 50% 0,
53
55
  inset-block: 100% auto,
54
- translate: -50% var(--spacing-10),
56
+ [translate]: calc(-50% * var(--direction, 1)) var(--spacing-10),
55
57
  pseudo: (
56
58
  after: (
57
59
  inset-inline: 50% auto,
58
60
  inset-block: 0 auto,
59
- translate: -50% -100%,
61
+ [translate]: calc(-50% * var(--direction, 1)) -100%,
60
62
  rotate: 90deg,
61
63
  ),
62
64
  ),
63
65
  ),
64
66
  left: (
65
67
  inset-inline: auto 100%,
66
- translate: calc(var(--spacing-10) * -1) -50%,
68
+ [translate]: calc(var(--spacing-10) * -1 * var(--direction, 1)) -50%,
67
69
  pseudo: (
68
70
  after: (
69
71
  position: absolute,
70
72
  inset-inline: auto 0,
71
- translate: 100% -50%,
73
+ [translate]: calc(100% * var(--direction, 1)) -50%,
72
74
  rotate: 180deg,
73
75
  ),
74
76
  ),
@@ -53,7 +53,9 @@ $-pseudo-elements: (
53
53
  -webkit-progress-bar, -webkit-progress-value, -webkit-inner-element,
54
54
  -moz-progress-bar, -moz-progress-value, -moz-inner-element,
55
55
  -webkit-details-marker, -webkit-scrollbar,
56
- -webkit-scrollbar-track, -webkit-scrollbar-thumb
56
+ -webkit-scrollbar-track, -webkit-scrollbar-thumb,
57
+ -webkit-slider-runnable-track, -webkit-slider-thumb,
58
+ -moz-range-track, -moz-range-progress, -moz-range-thumb
57
59
  );
58
60
 
59
61
  // Transform a map into a list of CSS attributes
@@ -81,38 +83,50 @@ $-pseudo-elements: (
81
83
  @each $key, $value in $map {
82
84
  @if not list.index($ignore, $key) {
83
85
  @if meta.type-of($value) == 'map' and ($deep or $key == 'pseudo') {
84
- $original: map.get($original, $key);
85
-
86
- @if not $original {
87
- $original: ();
86
+ // Sass assignment reaches the enclosing scope, so the
87
+ // reference map and the property name of a nested map are
88
+ // kept in their own variables: reusing $original and $key
89
+ // left every key after the first one looking itself up in
90
+ // the wrong map.
91
+ $nested-original: map.get($original, $key);
92
+
93
+ @if not $nested-original {
94
+ $nested-original: ();
88
95
  }
89
96
 
97
+ $nested-key: $key;
98
+
90
99
  @if $postfix != '' {
91
- $key: #{$postfix}-#{$key};
100
+ $nested-key: #{$postfix}-#{$key};
92
101
  }
93
102
 
103
+ // The test is on the map key, not on the postfixed name: a
104
+ // pseudo map nested one level down would otherwise be
105
+ // spread as plain declarations on the parent selector.
94
106
  @if $key == 'pseudo' {
95
107
  @each $pseudo-element, $pseudo-map in $value {
96
108
  @if meta.type-of($pseudo-map) == 'map' {
97
109
  @if list.index($-pseudo-elements, $pseudo-element) {
98
- $original: map.get($original, $pseudo-element);
99
- $key: pseudo-#{$pseudo-element};
110
+ $pseudo-original: (
111
+ map.get($nested-original, $pseudo-element)
112
+ );
113
+ $pseudo-key: pseudo-#{$pseudo-element};
100
114
 
101
115
  @include pseudo-element-placeholder($pseudo-element) {
102
- @if not $original {
116
+ @if not $pseudo-original {
103
117
  @if not map.has-key($pseudo-map, 'content') {
104
118
  content: '';
105
119
  }
106
120
 
107
- $original: ();
121
+ $pseudo-original: ();
108
122
  }
109
123
 
110
124
  @include spread-map-into-attrs(
111
- $original: $original,
125
+ $original: $pseudo-original,
112
126
  $map: $pseudo-map,
113
127
  $prefix: $prefix,
114
128
  $variant: $variant,
115
- $postfix: $key,
129
+ $postfix: $pseudo-key,
116
130
  $deep: true,
117
131
  $use-custom-props: $use-custom-props
118
132
  );
@@ -122,11 +136,11 @@ $-pseudo-elements: (
122
136
  }
123
137
  } @else {
124
138
  @include spread-map-into-attrs(
125
- $original: $original,
139
+ $original: $nested-original,
126
140
  $map: $value,
127
141
  $prefix: $prefix,
128
142
  $variant: $variant,
129
- $postfix: $key,
143
+ $postfix: $nested-key,
130
144
  $deep: true,
131
145
  $use-custom-props: $use-custom-props
132
146
  );
@@ -711,3 +711,120 @@ $-state-suffixes: (
711
711
  }
712
712
  }
713
713
  }
714
+
715
+ // ============================================
716
+ // Control states
717
+ // ============================================
718
+ // A field component is a wrapper element around a native control, so its own
719
+ // `state` maps generate selectors (`.vv-input-text:disabled`) that can never
720
+ // match. This re-applies those placeholders on the control's attribute
721
+ // instead, through `:has()`, block first and then element by element.
722
+
723
+ // `$states` are read from the map's `state` key and must name an attribute of
724
+ // the control: `disabled` and `readonly` for a text field, `disabled` alone
725
+ // where HTML has no readonly (`select`, `input[type="range"]`). Components
726
+ // that spell readonly as a modifier because they freeze the control with the
727
+ // disabled attribute list that modifier in `$except-modifiers`, otherwise the
728
+ // hook would dim a readonly field: `:has()` carries the specificity of its
729
+ // argument, so with `$zero-specificity: false` it outweighs the modifier and
730
+ // source order alone would not settle it.
731
+ @mixin spread-map-into-control-states(
732
+ $map,
733
+ $block,
734
+ $control: 'input',
735
+ $states: ('disabled', 'readonly'),
736
+ $except-modifiers: (),
737
+ $zero-specificity: true
738
+ ) {
739
+ $component-selector: '.#{$block}';
740
+ $exception: '';
741
+
742
+ @each $modifier in $except-modifiers {
743
+ $exception: '#{$exception}:not(#{$component-selector}--#{$modifier})';
744
+ }
745
+
746
+ @each $state in $states {
747
+ $selector: (
748
+ '#{$component-selector}#{$exception}:has(#{$control}[#{$state}])'
749
+ );
750
+
751
+ @include wrap-with-where(
752
+ $selector: $selector,
753
+ $enabled: $zero-specificity
754
+ ) {
755
+ @extend %#{$block}--#{$state} !optional;
756
+ }
757
+
758
+ // element
759
+ @if map.get($map, 'state', $state, 'element') {
760
+ @each $element, $element-map
761
+ in map.get($map, 'state', $state, 'element')
762
+ {
763
+ $element-selector: (
764
+ '#{$selector} #{$component-selector}__#{$element}'
765
+ );
766
+ $alias: map.get($map, 'element', $element, '_alias');
767
+
768
+ @if $alias {
769
+ $element-selector: list.append(
770
+ $element-selector,
771
+ ('#{$selector} #{$alias}')
772
+ );
773
+ }
774
+
775
+ @include wrap-with-where(
776
+ $selector: $element-selector,
777
+ $enabled: $zero-specificity
778
+ ) {
779
+ @extend %#{$block}-state-#{$state}__#{$element} !optional;
780
+ }
781
+
782
+ // element state
783
+ @each $element-state, $state-map
784
+ in map.get($element-map, 'state')
785
+ {
786
+ @if $element-state {
787
+ $element-selector-state: ();
788
+
789
+ @each $item in $element-selector {
790
+ $element-selector-state: list.append(
791
+ $element-selector-state,
792
+ ('#{$item}:#{$element-state}')
793
+ );
794
+ }
795
+
796
+ @include wrap-with-where(
797
+ $selector: $element-selector-state,
798
+ $enabled: $zero-specificity
799
+ ) {
800
+ @extend %#{$block}-state-#{$state}-element-#{$element}--#{$element-state}
801
+ !optional;
802
+ }
803
+ }
804
+ }
805
+
806
+ // element pseudo
807
+ @each $pseudo, $pseudo-map in map.get($element-map, 'pseudo') {
808
+ @if $pseudo {
809
+ $element-selector-pseudo: ();
810
+
811
+ @each $item in $element-selector {
812
+ $element-selector-pseudo: list.append(
813
+ $element-selector-pseudo,
814
+ ('#{$item}::#{$pseudo}')
815
+ );
816
+ }
817
+
818
+ @include wrap-with-where(
819
+ $selector: $element-selector-pseudo,
820
+ $enabled: $zero-specificity
821
+ ) {
822
+ @extend %#{$block}-state-#{$state}-element-#{$element}--#{$pseudo}
823
+ !optional;
824
+ }
825
+ }
826
+ }
827
+ }
828
+ }
829
+ }
830
+ }