@volverjs/style 0.1.27 → 0.1.29

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 (52) hide show
  1. package/README.md +12 -0
  2. package/dist/base.css +1 -1
  3. package/dist/components/vv-accordion.css +1 -1
  4. package/dist/components/vv-alert-group.css +1 -1
  5. package/dist/components/vv-alert.css +1 -1
  6. package/dist/components/vv-avatar.css +1 -1
  7. package/dist/components/vv-badge.css +1 -1
  8. package/dist/components/vv-button-group.css +1 -1
  9. package/dist/components/vv-button.css +1 -1
  10. package/dist/components/vv-card.css +1 -1
  11. package/dist/components/vv-checkbox.css +1 -1
  12. package/dist/components/vv-dialog.css +1 -1
  13. package/dist/components/vv-dropdown-option.css +1 -1
  14. package/dist/components/vv-dropdown.css +1 -1
  15. package/dist/components/vv-input-file.css +1 -1
  16. package/dist/components/vv-input-range.css +1 -1
  17. package/dist/components/vv-input-text.css +1 -1
  18. package/dist/components/vv-nav.css +1 -1
  19. package/dist/components/vv-progress.css +1 -1
  20. package/dist/components/vv-radio.css +1 -1
  21. package/dist/components/vv-select.css +1 -1
  22. package/dist/components/vv-textarea.css +1 -1
  23. package/dist/components.css +1 -1
  24. package/dist/export.css +1 -1
  25. package/dist/props/background.css +1 -1
  26. package/dist/props/input.css +1 -1
  27. package/dist/props/typography.css +1 -1
  28. package/dist/props.css +1 -1
  29. package/dist/reset.css +1 -1
  30. package/dist/utilities/backgrounds.css +1 -1
  31. package/dist/utilities/flexbox.css +1 -1
  32. package/dist/utilities/layout.css +1 -1
  33. package/dist/utilities/sizing.css +1 -1
  34. package/dist/utilities/typography.css +1 -1
  35. package/dist/utilities.css +1 -1
  36. package/dist/volver.css +1 -1
  37. package/package.json +8 -1
  38. package/src/_config.scss +6 -1
  39. package/src/props/index.scss +4 -1
  40. package/src/props/media.scss +21 -6
  41. package/src/settings/_input.scss +8 -0
  42. package/src/settings/components/_vv-accordion.scss +6 -4
  43. package/src/settings/components/_vv-alert.scss +8 -3
  44. package/src/settings/components/_vv-badge.scss +21 -1
  45. package/src/settings/components/_vv-button.scss +12 -5
  46. package/src/settings/components/_vv-checkbox.scss +27 -3
  47. package/src/settings/components/_vv-dropdown-option.scss +4 -1
  48. package/src/settings/components/_vv-input-text.scss +3 -0
  49. package/src/settings/components/_vv-nav.scss +26 -1
  50. package/src/settings/components/_vv-radio.scss +17 -2
  51. package/src/settings/components/_vv-select.scss +5 -0
  52. package/src/utilities/flexbox.scss +27 -0
package/package.json CHANGED
@@ -24,10 +24,17 @@
24
24
  "bugs": {
25
25
  "url": "https://github.com/volverjs/style/issues"
26
26
  },
27
- "version": "0.1.27",
27
+ "version": "0.1.29",
28
28
  "engines": {
29
29
  "node": ">= 20.x"
30
30
  },
31
+ "browserslist": [
32
+ "chrome >= 133",
33
+ "edge >= 133",
34
+ "firefox >= 128",
35
+ "safari >= 16.4",
36
+ "ios_saf >= 16.4"
37
+ ],
31
38
  "type": "module",
32
39
  "main": "dist/volver.css",
33
40
  "style": "dist/volver.css",
package/src/_config.scss CHANGED
@@ -22,7 +22,12 @@ $zero-specificity-for-utilities: true !default;
22
22
  // components prefix
23
23
  $components-prefix: vv !default;
24
24
 
25
- // preflight
25
+ // preflight: bare form controls under `.preflight` take the look of their
26
+ // component through `@extend` of its placeholders, the block-level state
27
+ // placeholders included. A `state.disabled` or `state.readonly` override in a
28
+ // component map therefore reaches every bare disabled or readonly control on
29
+ // the page, not only the ones inside a `.vv-*` block, and only the compiled
30
+ // CSS shows it. Overrides on the `element` maps of a state stay in the block.
26
31
  $preflight: true !default;
27
32
 
28
33
  // components names
@@ -6,8 +6,11 @@
6
6
  @use 'filters';
7
7
  @use 'input';
8
8
  @use 'layout';
9
- @use 'media';
10
9
  @use 'sizing';
11
10
  @use 'spacing';
12
11
  @use 'transitions';
13
12
  @use 'typography';
13
+
14
+ // `media` is left out on purpose: `@custom-media` needs a build step to be
15
+ // resolved, and without one a minifier warns about every rule. A consumer
16
+ // whose pipeline resolves them imports `scss/props/media` on its own.
@@ -1,5 +1,10 @@
1
1
  @use '../context' as *;
2
2
 
3
+ // Opt-in, left out of `props/index.scss`: `@custom-media` is a draft that has
4
+ // to be resolved at build time (postcss-custom-media, or lightningcss with
5
+ // `drafts.customMedia`), and each resolver reads the definitions of the file
6
+ // it is transforming, so import this module where the queries are used.
7
+
3
8
  // motion
4
9
  @custom-media --motion-ok (prefers-reduced-motion: no-preference);
5
10
  @custom-media --motion-not-ok (prefers-reduced-motion: reduce);
@@ -16,7 +21,7 @@
16
21
  @custom-media --os-dark (prefers-color-scheme: dark);
17
22
  @custom-media --os-light (prefers-color-scheme: light);
18
23
 
19
- // constrast
24
+ // contrast
20
25
  @custom-media --high-contrast (prefers-contrast: more);
21
26
  @custom-media --low-contrast (prefers-contrast: less);
22
27
 
@@ -33,17 +38,27 @@
33
38
  @custom-media --pointer (hover) and (pointer: coarse);
34
39
  @custom-media --mouse (hover) and (pointer: fine);
35
40
 
36
- // breakpoints
37
- /* stylelint-disable */
41
+ // breakpoints: the same widths as the `bp-only`, `bp-up` and `bp-down`
42
+ // mixins. A range stops short of the next breakpoint instead of sharing its
43
+ // first pixel, and the last breakpoint has no upper bound.
44
+ // stylelint-disable custom-media-pattern
38
45
  $old-breakpoint: null;
39
46
  $old-breakpoint-value: null;
47
+
40
48
  @each $breakpoint, $breakpoint-value in $breakpoints {
41
49
  @if $old-breakpoint {
42
- @custom-media --#{$old-breakpoint}-only (#{$old-breakpoint-value} <= width <= #{$breakpoint-value});
43
- @custom-media --#{$old-breakpoint}-n-above (width >= #{$breakpoint-value});
44
- @custom-media --#{$old-breakpoint}-n-below (width <= #{$breakpoint-value});
50
+ @custom-media --#{$old-breakpoint}-only (#{$old-breakpoint-value} <= width < #{$breakpoint-value});
51
+ @custom-media --#{$old-breakpoint}-n-below (width < #{$breakpoint-value});
45
52
  }
46
53
 
54
+ @custom-media --#{$breakpoint}-n-above (width >= #{$breakpoint-value});
55
+
47
56
  $old-breakpoint: $breakpoint;
48
57
  $old-breakpoint-value: $breakpoint-value;
49
58
  }
59
+
60
+ @if $old-breakpoint {
61
+ @custom-media --#{$old-breakpoint}-only (width >= #{$old-breakpoint-value});
62
+ @custom-media --#{$old-breakpoint}-n-below (width >= 0);
63
+ }
64
+ // stylelint-enable custom-media-pattern
@@ -59,6 +59,14 @@ $input: (
59
59
  translate: 0 -0.8em,
60
60
  ),
61
61
  ),
62
+ // Block size of a checkbox, a radio or the pill of a switch: the side of
63
+ // the square, and the figure the offset that keeps the control on the
64
+ // first line of a wrapping label is derived from. It was written twice per
65
+ // component, once as `min-width` and once as `height`, and the switch
66
+ // spelled a third value.
67
+ control: (
68
+ size: var(--spacing-20),
69
+ ),
62
70
  range: (
63
71
  accent-color: var(--color-brand),
64
72
  track-color: var(--color-surface-3),
@@ -1,6 +1,8 @@
1
1
  $vv-accordion: (
2
- --accordion-marker-size:
3
- calc(var(--spacing-sm) + var(--leading-normal) * 1em),
2
+ // `1lh` is resolved where the token is read, on the summary, so the reserved
3
+ // space follows the line box of the summary rather than a line height
4
+ // spelled out a second time here.
5
+ --accordion-marker-size: calc(var(--spacing-sm) + 1lh),
4
6
  --accordion-padding-top: var(--spacing-14),
5
7
  --accordion-padding-right: var(--spacing-14),
6
8
  --accordion-padding-bottom: var(--spacing-14),
@@ -57,8 +59,8 @@ $vv-accordion: (
57
59
  position: absolute,
58
60
  [top]: var(--accordion-padding-top),
59
61
  [left]: var(--accordion-padding-left),
60
- width: calc(var(--leading-normal) * 1em),
61
- height: calc(var(--leading-normal) * 1em),
62
+ width: 1lh,
63
+ height: 1lh,
62
64
  background-image: var(--bg-chevron),
63
65
  background-position: center,
64
66
  background-size: 1em,
@@ -8,7 +8,7 @@ $vv-alert: (
8
8
  border-width: var(--border),
9
9
  display: flex,
10
10
  flex-wrap: wrap,
11
- align-items: center,
11
+ align-items: flex-start,
12
12
  gap: var(--spacing-sm),
13
13
  min-width: 0,
14
14
  overflow: hidden,
@@ -16,13 +16,13 @@ $vv-alert: (
16
16
  element: (
17
17
  header: (
18
18
  display: flex,
19
- align-items: center,
19
+ align-items: flex-start,
20
20
  gap: var(--spacing-sm),
21
21
  line-height: var(--leading-normal),
22
22
  color: var(--color-word),
23
23
  overflow: hidden,
24
24
  text-overflow: ellipsis,
25
- flex-shrink: 0,
25
+ min-width: 0,
26
26
  ),
27
27
  title: (
28
28
  min-width: 0,
@@ -53,7 +53,12 @@ $vv-alert: (
53
53
  border-radius: var(--rounded),
54
54
  ),
55
55
  icon: (
56
+ --alert-icon-size: 1.1em,
57
+ [inline-size]: var(--alert-icon-size),
58
+ block-size: auto,
59
+ aspect-ratio: 1,
56
60
  flex-shrink: 0,
61
+ [translate]: 0 calc((1lh - var(--alert-icon-size)) / 2),
57
62
  ),
58
63
  close-mask: (
59
64
  display: none,
@@ -38,10 +38,26 @@ $vv-badge: (
38
38
  ),
39
39
  ),
40
40
  element: (
41
+ icon: (
42
+ // A descendant selector, not a child one: the icon of an action
43
+ // badge sits inside the button. `svg:not(svg *)` leaves the parts
44
+ // of a composite icon to the icon itself.
45
+ _alias: ':where(svg:not(svg *), [data-icon])',
46
+ aspect-ratio: 1,
47
+ inline-size: 1lh,
48
+ block-size: auto,
49
+ flex-shrink: 0,
50
+ background-position: center,
51
+ background-repeat: no-repeat,
52
+ background-size: contain,
53
+ ),
41
54
  button: (
42
55
  _alias: '> button',
43
56
  _combinator: '>',
44
57
  cursor: pointer,
58
+ display: inline-flex,
59
+ align-items: center,
60
+ justify-content: center,
45
61
  padding: 0 0.5em,
46
62
  margin: 0 -0.5em,
47
63
  min-height: 2em,
@@ -58,7 +74,11 @@ $vv-badge: (
58
74
  ),
59
75
  modifier: (
60
76
  action: (
61
- gap: calc(0.5ch + 0.5em),
77
+ // The same gap as a badge that holds its icon directly. The button
78
+ // widens its hit area toward the text with a negative margin and
79
+ // puts the drawing back with an equal padding, so the extra 0.5em
80
+ // this gap used to carry only pushed the icon away from the label.
81
+ gap: 0.5ch,
62
82
  ),
63
83
  sm: (
64
84
  --badge-font-size: 0.6rem,
@@ -49,7 +49,14 @@ $vv-button: (
49
49
  inline-size: 1.1em,
50
50
  margin: 0,
51
51
  flex-shrink: 0,
52
- [min-height]: calc(1em * var(--button-line-height)),
52
+ // The box is 1.1em wide and at least one line tall, so it is not
53
+ // square. An `<svg>` centres itself in it, an icon painted as a
54
+ // background does not: these three are inert for the first and
55
+ // place the second. The `_alias` claims to take both.
56
+ background-position: center,
57
+ background-repeat: no-repeat,
58
+ background-size: contain,
59
+ [min-height]: 1lh,
53
60
  ),
54
61
  label: (
55
62
  white-space: nowrap,
@@ -259,8 +266,8 @@ $vv-button: (
259
266
  element: (
260
267
  icon: (
261
268
  margin-inline: -0.125ch,
262
- block-size: calc(1em * var(--leading-snug)),
263
- inline-size: calc(1em * var(--leading-snug)),
269
+ block-size: 1lh,
270
+ inline-size: 1lh,
264
271
  ),
265
272
  ),
266
273
  ),
@@ -298,8 +305,8 @@ $vv-button: (
298
305
  element: (
299
306
  icon: (
300
307
  margin-inline: -0.125ch,
301
- block-size: calc(1em * var(--leading-snug)),
302
- inline-size: calc(1em * var(--leading-snug)),
308
+ block-size: 1lh,
309
+ inline-size: 1lh,
303
310
  ),
304
311
  ),
305
312
  ),
@@ -24,8 +24,17 @@ $vv-checkbox: (
24
24
  input: (
25
25
  _alias: "> input[type='checkbox']",
26
26
  border: var(--spacing-2) solid var(--input-color),
27
- min-width: var(--spacing-20),
28
- height: var(--spacing-20),
27
+ // Plain declarations, so the size is read on the control: the
28
+ // `switch` modifier overrides the token, and a `var()` inside a
29
+ // generated custom property would have been substituted at the
30
+ // root and frozen at the default.
31
+ [min-width]: var(--input-control-size),
32
+ [height]: var(--input-control-size),
33
+ // The control belongs to the first line of the label, not to the
34
+ // centre of a label that wraps. `align-self` rather than
35
+ // `align-items` on the block, so the hint keeps its own alignment.
36
+ align-self: flex-start,
37
+ [translate]: 0 calc((1lh - var(--input-control-size)) / 2),
29
38
  margin: 0 var(--input-label-gap) 0 0,
30
39
  background-position: center,
31
40
  background-size: var(--text-12) auto,
@@ -45,6 +54,19 @@ $vv-checkbox: (
45
54
  ),
46
55
  ),
47
56
  ),
57
+ label: (
58
+ // Without an element the label is an anonymous flex item, and one
59
+ // whose max-content width does not fit starts a new flex line:
60
+ // a long label dropped below the control instead of sitting next
61
+ // to it. `@volverjs/ui-vue` renders the span this reaches.
62
+ // Only a span without a class of its own, for plain markup: a
63
+ // badge or an icon written next to the label is a span too, and
64
+ // `flex: 1` would stretch it. Anything carrying a class takes
65
+ // `vv-checkbox__label` to be the label.
66
+ _alias: '> span:not([class])',
67
+ flex: 1,
68
+ min-width: 0,
69
+ ),
48
70
  hint: (
49
71
  _alias: '> small',
50
72
  _combinator: '>',
@@ -68,9 +90,11 @@ $vv-checkbox: (
68
90
  switch: (
69
91
  element: (
70
92
  input: (
93
+ --input-control-size: var(--spacing-24),
71
94
  border: 0,
95
+ // Not a plain declaration: this one reads no token of the
96
+ // control, so it keeps its generated custom property.
72
97
  min-width: var(--spacing-44),
73
- height: var(--spacing-24),
74
98
  border-radius: var(--spacing-12),
75
99
  background-color: var(--color-word-5),
76
100
  position: relative,
@@ -3,7 +3,10 @@ $vv-dropdown-option: (
3
3
  min-width: 0,
4
4
  overflow: hidden,
5
5
  gap: var(--spacing-sm),
6
- align-items: flex-end,
6
+ // The hint is smaller than the label: `baseline` is what keeps the two
7
+ // sitting on the same line of text. `flex-end` aligned their bottom edges
8
+ // instead, which drifts with the descenders of the font.
9
+ align-items: baseline,
7
10
  padding: var(--spacing-sm),
8
11
  state: (
9
12
  disabled: (
@@ -25,6 +25,9 @@ $vv-input-text: (
25
25
  display: flex,
26
26
  align-items: center,
27
27
  flex: 1,
28
+ // The wrapper clips its content: the `::after` bars run along the
29
+ // bottom edge and the input never paints outside the box. The
30
+ // `vv-select` wrapper does not clip, on purpose: see its map.
28
31
  overflow: hidden,
29
32
  background-color: var(--input-background-color),
30
33
  will-change: background-color,
@@ -21,6 +21,7 @@ $vv-nav: (
21
21
  _alias: 'li > :is(a, button)',
22
22
  display: flex,
23
23
  align-items: center,
24
+ gap: 0.5ch,
24
25
  will-change: background-color color,
25
26
  transition: var(--transition-all),
26
27
  color: var(--color-word-2),
@@ -40,6 +41,23 @@ $vv-nav: (
40
41
  ),
41
42
  ),
42
43
  ),
44
+ icon: (
45
+ _alias: 'li > :is(a, button) > :where(svg, [data-icon])',
46
+ --nav-icon-size: 1.1em,
47
+ // `align-self` and not `align-items` on the item: the label of a
48
+ // voice can wrap, and the icon belongs to its first line, while
49
+ // everything else in the item (a counter badge, a chevron) stays
50
+ // centred on the whole voice.
51
+ align-self: flex-start,
52
+ flex-shrink: 0,
53
+ [inline-size]: var(--nav-icon-size),
54
+ block-size: auto,
55
+ aspect-ratio: 1,
56
+ background-position: center,
57
+ background-repeat: no-repeat,
58
+ background-size: contain,
59
+ [translate]: 0 calc((1lh - var(--nav-icon-size)) / 2),
60
+ ),
43
61
  separator: (
44
62
  _alias: 'li[role="separator"]',
45
63
  display: block,
@@ -134,7 +152,14 @@ $vv-nav: (
134
152
  flex: 1 1 0%,
135
153
  ),
136
154
  item-label: (
137
- display: block,
155
+ // Still a flex container: as a block it puts the icon of a
156
+ // voice back on the baseline, where `align-self` is inert and
157
+ // the `gap` between the icon and the label is dropped, and the
158
+ // first line offset the icon carries becomes a nudge of its
159
+ // own. `justify-content` centres the icon and the label
160
+ // together, `text-align` the lines of a label that wraps.
161
+ display: flex,
162
+ justify-content: center,
138
163
  width: 100%,
139
164
  text-align: center,
140
165
  ),
@@ -42,13 +42,28 @@ $vv-radio: (
42
42
  ),
43
43
  ),
44
44
  element: (
45
+ label: (
46
+ // See `vv-checkbox`: an anonymous flex item cannot be kept beside
47
+ // the control once the label wraps.
48
+ // Only a span without a class of its own, for plain markup: a
49
+ // badge or an icon written next to the label is a span too, and
50
+ // `flex: 1` would stretch it. Anything carrying a class takes
51
+ // `vv-radio__label` to be the label.
52
+ _alias: '> span:not([class])',
53
+ flex: 1,
54
+ min-width: 0,
55
+ ),
45
56
  input: (
46
57
  _alias: "> input[type='radio']",
47
58
  border-width: var(--spacing-2),
48
59
  border-style: solid,
49
60
  border-color: currentcolor,
50
- min-width: var(--spacing-20),
51
- height: var(--spacing-20),
61
+ // Plain declarations for the same reason as `vv-checkbox`: the
62
+ // size is read on the control, not at the root.
63
+ [min-width]: var(--input-control-size),
64
+ [height]: var(--input-control-size),
65
+ align-self: flex-start,
66
+ [translate]: 0 calc((1lh - var(--input-control-size)) / 2),
52
67
  border-radius: 50%,
53
68
  margin: 0 var(--input-label-gap) 0 0,
54
69
  background-color: transparent,
@@ -26,6 +26,11 @@ $vv-select: (
26
26
  display: flex,
27
27
  align-items: center,
28
28
  flex: 1,
29
+ // No `overflow: hidden` here, unlike the wrapper of `vv-input-text`,
30
+ // `vv-textarea` and `vv-input-file`: this wrapper also hosts the
31
+ // dropdown of a combobox, which a clip would cut off whenever the
32
+ // positioning strategy is not `fixed`. The ellipsis of a long value
33
+ // is clipped by the `value` element instead.
29
34
  background-color: var(--input-background-color),
30
35
  will-change: background-color,
31
36
  transition: var(--transition-colors),
@@ -136,4 +136,31 @@
136
136
  );
137
137
  }
138
138
 
139
+ // Optically aligns an icon with the first line of a wrapping label instead of
140
+ // with the centre of the whole block. `align-self: center` centres the icon on
141
+ // the flex line, which is the whole block once the label wraps, and
142
+ // `align-self: flex-start` drops the half leading of the first line. The offset
143
+ // is half the difference between the line box and the icon, so it holds at any
144
+ // font size and any icon size. The offset itself is never zero: on a label that
145
+ // does not wrap it puts the icon exactly where `align-self: center` would, since
146
+ // the first line is then the whole block, which is why the class is safe to
147
+ // apply unconditionally and needs no breakpoint variant.
148
+ // https://ishadeed.com/article/aligning-list-icons/
149
+ // `--icon-size` is the rendered size of the icon and defaults to the `1em` an
150
+ // inline SVG carries. `transform` and not `translate`, because components of
151
+ // the library move their own elements with `translate` and the two would
152
+ // overwrite each other.
153
+ %self-first-line {
154
+ flex-shrink: 0;
155
+ align-self: flex-start;
156
+ transform: translateY(calc((1lh - var(--icon-size, 1em)) / 2));
157
+ }
158
+
159
+ @include wrap-with-where(
160
+ $selector: '.self-first-line',
161
+ $enabled: $zero-specificity-for-utilities
162
+ ) {
163
+ @extend %self-first-line;
164
+ }
165
+
139
166
  }