@expressivecss/expressive 0.12.0 → 0.13.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 (171) hide show
  1. package/CHANGELOG.md +231 -1
  2. package/README.md +37 -1
  3. package/dist/css/expressive.css +5111 -36
  4. package/dist/css/expressive.css.map +1 -1
  5. package/dist/css/expressive.min.css +1 -1
  6. package/dist/css/expressive.min.css.map +1 -1
  7. package/dist/fonts/material-symbols-outlined.woff2 +0 -0
  8. package/dist/fonts/material-symbols-rounded.woff2 +0 -0
  9. package/dist/fonts/material-symbols-sharp.woff2 +0 -0
  10. package/dist/js/chunks/{chunk-HLDXZ7XB.mjs → chunk-3CLXLPYS.mjs} +2 -2
  11. package/dist/js/chunks/chunk-3DHK5CZU.mjs +105 -0
  12. package/dist/js/chunks/chunk-3DHK5CZU.mjs.map +7 -0
  13. package/dist/js/chunks/chunk-4PWAPV6F.mjs +7 -0
  14. package/dist/js/chunks/chunk-4PWAPV6F.mjs.map +7 -0
  15. package/dist/js/chunks/{chunk-HB6IW7QQ.mjs → chunk-55IA2XUC.mjs} +2 -2
  16. package/dist/js/chunks/chunk-CCWDV5CR.mjs +292 -0
  17. package/dist/js/chunks/chunk-CCWDV5CR.mjs.map +7 -0
  18. package/dist/js/chunks/chunk-DTKR54I6.mjs +270 -0
  19. package/dist/js/chunks/chunk-DTKR54I6.mjs.map +7 -0
  20. package/dist/js/chunks/chunk-GVGT6MIU.mjs +47 -0
  21. package/dist/js/chunks/chunk-GVGT6MIU.mjs.map +7 -0
  22. package/dist/js/chunks/chunk-HKT2S7VM.mjs +48 -0
  23. package/dist/js/chunks/chunk-HKT2S7VM.mjs.map +7 -0
  24. package/dist/js/chunks/chunk-K4CJCRGE.mjs +262 -0
  25. package/dist/js/chunks/chunk-K4CJCRGE.mjs.map +7 -0
  26. package/dist/js/chunks/chunk-LNCCYGPC.mjs +163 -0
  27. package/dist/js/chunks/chunk-LNCCYGPC.mjs.map +7 -0
  28. package/dist/js/chunks/chunk-NQLC3YMW.mjs +1 -0
  29. package/dist/js/chunks/chunk-O5XPWXIQ.mjs +406 -0
  30. package/dist/js/chunks/chunk-O5XPWXIQ.mjs.map +7 -0
  31. package/dist/js/chunks/chunk-P3G3QHII.mjs +166 -0
  32. package/dist/js/chunks/chunk-P3G3QHII.mjs.map +7 -0
  33. package/dist/js/chunks/{chunk-H2WA4XHH.mjs → chunk-PLYXLSH3.mjs} +4 -4
  34. package/dist/js/chunks/chunk-QKWPQK5N.mjs +179 -0
  35. package/dist/js/chunks/chunk-QKWPQK5N.mjs.map +7 -0
  36. package/dist/js/chunks/chunk-RHRQKTTX.mjs +139 -0
  37. package/dist/js/chunks/chunk-RHRQKTTX.mjs.map +7 -0
  38. package/dist/js/chunks/chunk-SPETGK25.mjs +59 -0
  39. package/dist/js/chunks/chunk-SPETGK25.mjs.map +7 -0
  40. package/dist/js/chunks/{chunk-YSBCHYXP.mjs → chunk-U2OTHOTI.mjs} +116 -12
  41. package/dist/js/chunks/chunk-U2OTHOTI.mjs.map +7 -0
  42. package/dist/js/chunks/chunk-VTLBW7QS.mjs +124 -0
  43. package/dist/js/chunks/chunk-VTLBW7QS.mjs.map +7 -0
  44. package/dist/js/chunks/{chunk-RFZZCYVU.mjs → chunk-XGHORT7J.mjs} +2 -2
  45. package/dist/js/chunks/{chunk-HNFSMIBY.mjs → chunk-XH6BEEMY.mjs} +69 -16
  46. package/dist/js/chunks/chunk-XH6BEEMY.mjs.map +7 -0
  47. package/dist/js/expressive.cjs +2209 -32
  48. package/dist/js/expressive.cjs.map +4 -4
  49. package/dist/js/expressive.js +2209 -32
  50. package/dist/js/expressive.js.map +4 -4
  51. package/dist/js/expressive.min.js +10 -10
  52. package/dist/js/expressive.min.js.map +4 -4
  53. package/dist/js/expressive.mjs +68 -20
  54. package/dist/js/expressive.mjs.map +1 -1
  55. package/dist/js/internal/behaviors/forms.mjs +1 -1
  56. package/dist/js/internal/components/autocomplete.mjs +2 -2
  57. package/dist/js/internal/components/barChart.mjs +10 -0
  58. package/dist/js/internal/components/barChart.mjs.map +7 -0
  59. package/dist/js/internal/components/chips.mjs +3 -3
  60. package/dist/js/internal/components/columnChart.mjs +9 -0
  61. package/dist/js/internal/components/columnChart.mjs.map +7 -0
  62. package/dist/js/internal/components/commandPalette.mjs +9 -0
  63. package/dist/js/internal/components/commandPalette.mjs.map +7 -0
  64. package/dist/js/internal/components/datepicker.mjs +3 -3
  65. package/dist/js/internal/components/heatmapChart.mjs +9 -0
  66. package/dist/js/internal/components/heatmapChart.mjs.map +7 -0
  67. package/dist/js/internal/components/index.mjs +65 -17
  68. package/dist/js/internal/components/lineChart.mjs +12 -0
  69. package/dist/js/internal/components/lineChart.mjs.map +7 -0
  70. package/dist/js/internal/components/menu.mjs +1 -1
  71. package/dist/js/internal/components/messageScroller.mjs +9 -0
  72. package/dist/js/internal/components/messageScroller.mjs.map +7 -0
  73. package/dist/js/internal/components/mixedChart.mjs +10 -0
  74. package/dist/js/internal/components/mixedChart.mjs.map +7 -0
  75. package/dist/js/internal/components/pieChart.mjs +9 -0
  76. package/dist/js/internal/components/pieChart.mjs.map +7 -0
  77. package/dist/js/internal/components/questionnaire.mjs +9 -0
  78. package/dist/js/internal/components/questionnaire.mjs.map +7 -0
  79. package/dist/js/internal/components/radarChart.mjs +9 -0
  80. package/dist/js/internal/components/radarChart.mjs.map +7 -0
  81. package/dist/js/internal/components/radialChart.mjs +9 -0
  82. package/dist/js/internal/components/radialChart.mjs.map +7 -0
  83. package/dist/js/internal/components/registry.mjs +23 -11
  84. package/dist/js/internal/components/richTextEditor.mjs +9 -0
  85. package/dist/js/internal/components/richTextEditor.mjs.map +7 -0
  86. package/dist/js/internal/components/select.mjs +2 -2
  87. package/dist/js/modular.mjs +68 -20
  88. package/dist/types/behaviors/forms.d.ts +22 -0
  89. package/dist/types/components/barChart.d.ts +24 -0
  90. package/dist/types/components/columnChart.d.ts +21 -0
  91. package/dist/types/components/commandPalette.d.ts +96 -0
  92. package/dist/types/components/heatmapChart.d.ts +49 -0
  93. package/dist/types/components/index.d.ts +12 -0
  94. package/dist/types/components/lineChart.d.ts +134 -0
  95. package/dist/types/components/menu.d.ts +31 -0
  96. package/dist/types/components/messageScroller.d.ts +111 -0
  97. package/dist/types/components/mixedChart.d.ts +25 -0
  98. package/dist/types/components/pieChart.d.ts +40 -0
  99. package/dist/types/components/questionnaire.d.ts +96 -0
  100. package/dist/types/components/radarChart.d.ts +39 -0
  101. package/dist/types/components/radialChart.d.ts +45 -0
  102. package/dist/types/components/registry.d.ts +44 -0
  103. package/dist/types/components/richTextEditor.d.ts +71 -0
  104. package/dist/types/modular.d.ts +10 -1
  105. package/package.json +4 -1
  106. package/src/sass/README.md +3 -6
  107. package/src/sass/abstracts/_index.scss +1 -1
  108. package/src/sass/abstracts/_mixins.scss +226 -0
  109. package/src/sass/abstracts/_variables.scss +14 -0
  110. package/src/sass/base/_typography.scss +45 -0
  111. package/src/sass/components/_accordion.scss +161 -0
  112. package/src/sass/components/_aura.scss +119 -0
  113. package/src/sass/components/_avatar.scss +86 -0
  114. package/src/sass/components/_bar-chart.scss +285 -0
  115. package/src/sass/components/_bottom-sheet.scss +1 -0
  116. package/src/sass/components/_button-groups.scss +13 -3
  117. package/src/sass/components/_cards.scss +16 -13
  118. package/src/sass/components/_column-chart.scss +134 -0
  119. package/src/sass/components/_command-palette.scss +140 -0
  120. package/src/sass/components/_countdown.scss +89 -0
  121. package/src/sass/components/_data-table.scss +188 -0
  122. package/src/sass/components/_dialog.scss +1 -0
  123. package/src/sass/components/_empty-state.scss +78 -0
  124. package/src/sass/components/_expanding-card.scss +3 -0
  125. package/src/sass/components/_heatmap-chart.scss +156 -0
  126. package/src/sass/components/_index.scss +27 -0
  127. package/src/sass/components/_kpi.scss +162 -0
  128. package/src/sass/components/_line-chart.scss +102 -0
  129. package/src/sass/components/_marquee.scss +111 -0
  130. package/src/sass/components/_menu.scss +10 -0
  131. package/src/sass/components/_message-scroller.scss +66 -0
  132. package/src/sass/components/_message.scss +202 -0
  133. package/src/sass/components/_mixed-chart.scss +157 -0
  134. package/src/sass/components/_navigation-bar.scss +1 -0
  135. package/src/sass/components/_navigation-rail.scss +9 -0
  136. package/src/sass/components/_pie-chart.scss +203 -0
  137. package/src/sass/components/_popover.scss +112 -0
  138. package/src/sass/components/_questionnaire.scss +144 -0
  139. package/src/sass/components/_radar-chart.scss +205 -0
  140. package/src/sass/components/_radial-chart.scss +174 -0
  141. package/src/sass/components/_rich-text-editor.scss +99 -0
  142. package/src/sass/components/_search.scss +1 -0
  143. package/src/sass/components/_side-sheet.scss +1 -0
  144. package/src/sass/components/_skeleton.scss +81 -0
  145. package/src/sass/components/_snackbar.scss +1 -0
  146. package/src/sass/components/_stat.scss +86 -0
  147. package/src/sass/components/_stepper.scss +190 -0
  148. package/src/sass/components/_timeline.scss +114 -0
  149. package/src/sass/components/_toolbar.scss +1 -0
  150. package/src/sass/components/_tooltip.scss +2 -0
  151. package/src/sass/components/_tree.scss +139 -0
  152. package/src/sass/components/forms/_drop-zone.scss +128 -0
  153. package/src/sass/components/forms/_index.scss +2 -0
  154. package/src/sass/components/forms/_input-fields.scss +133 -2
  155. package/src/sass/components/forms/_rating.scss +165 -0
  156. package/src/sass/tokens/_reference.scss +3 -15
  157. package/src/sass/utilities/_index.scss +2 -0
  158. package/src/sass/utilities/_scroll-area.scss +35 -0
  159. package/src/sass/utilities/_shapes.scss +164 -0
  160. package/dist/js/chunks/chunk-G3I5H2U3.mjs +0 -7
  161. package/dist/js/chunks/chunk-G3I5H2U3.mjs.map +0 -7
  162. package/dist/js/chunks/chunk-HNFSMIBY.mjs.map +0 -7
  163. package/dist/js/chunks/chunk-PSSHQWCX.mjs +0 -100
  164. package/dist/js/chunks/chunk-PSSHQWCX.mjs.map +0 -7
  165. package/dist/js/chunks/chunk-WWPAPHMA.mjs +0 -1
  166. package/dist/js/chunks/chunk-YSBCHYXP.mjs.map +0 -7
  167. /package/dist/js/chunks/{chunk-HLDXZ7XB.mjs.map → chunk-3CLXLPYS.mjs.map} +0 -0
  168. /package/dist/js/chunks/{chunk-HB6IW7QQ.mjs.map → chunk-55IA2XUC.mjs.map} +0 -0
  169. /package/dist/js/chunks/{chunk-WWPAPHMA.mjs.map → chunk-NQLC3YMW.mjs.map} +0 -0
  170. /package/dist/js/chunks/{chunk-H2WA4XHH.mjs.map → chunk-PLYXLSH3.mjs.map} +0 -0
  171. /package/dist/js/chunks/{chunk-RFZZCYVU.mjs.map → chunk-XGHORT7J.mjs.map} +0 -0
@@ -177,7 +177,7 @@ $_not-label: ".supporting-text, .character-counter, .file-path, .caret, .prefix,
177
177
  // .character-counter, .file-path). So the documented markup names the side
178
178
  // explicitly with .prefix / .suffix, and those rules key on nothing else.
179
179
  > i:is(.prefix, .suffix, :first-of-type, :last-of-type, :last-child),
180
- > :is(.prefix, .suffix) {
180
+ > :is(.prefix, .suffix):not(.icon-button) {
181
181
  position: absolute;
182
182
  top: 16px;
183
183
  display: flex;
@@ -200,7 +200,7 @@ $_not-label: ".supporting-text, .character-counter, .file-path, .caret, .prefix,
200
200
  > i.suffix,
201
201
  > i:last-of-type:not(:first-of-type),
202
202
  > i:last-child:not(:first-child):not(.prefix),
203
- > .suffix {
203
+ > .suffix:not(.icon-button) {
204
204
  right: 12px;
205
205
  // Both icons inherited this from the floating-label rule until they were
206
206
  // excluded from it. .prefix restates it below; .suffix has to as well, or
@@ -208,6 +208,15 @@ $_not-label: ".supporting-text, .character-counter, .file-path, .caret, .prefix,
208
208
  pointer-events: none;
209
209
  }
210
210
 
211
+ // A trailing icon button is a control, so it keeps its 40dp target and its
212
+ // clicks. It centres on the same point as a 24dp trailing icon.
213
+ > .icon-button.suffix {
214
+ position: absolute;
215
+ top: 8px;
216
+ right: 4px;
217
+ z-index: 1;
218
+ }
219
+
211
220
  &:has(> i:first-of-type:not(.suffix):not(:last-child), > i.prefix, > .prefix) {
212
221
  > :is(input, textarea) {
213
222
  padding-left: 52px;
@@ -351,3 +360,125 @@ textarea {
351
360
  }
352
361
  }
353
362
  }
363
+
364
+ // Password visibility toggle. A trailing .icon-button.password-toggle with
365
+ // aria-pressed holds two icons: the first shows while the password is hidden,
366
+ // the second once it is shown. The Forms behavior flips the input's type.
367
+ .password-toggle {
368
+ > #{$icon}:last-child:not(:first-child) {
369
+ display: none;
370
+ }
371
+
372
+ &[aria-pressed="true"] {
373
+ > #{$icon}:first-child:not(:last-child) {
374
+ display: none;
375
+ }
376
+
377
+ > #{$icon}:last-child:not(:first-child) {
378
+ display: inline-block;
379
+ }
380
+ }
381
+ }
382
+
383
+ // One-time code. A single native input drawn as a row of cells, so paste,
384
+ // autofill from autocomplete="one-time-code", undo and the caret all stay the
385
+ // browser's. A monospace face makes every digit 1ch wide; letter-spacing
386
+ // moves each one to the next cell, and the input is clipped after the last.
387
+ //
388
+ // Tokens:
389
+ // --md-comp-otp-length 6
390
+ // --md-comp-otp-cell-width 48px
391
+ // --md-comp-otp-cell-height 56px
392
+ // --md-comp-otp-gap 8px
393
+ // --md-comp-otp-cell-color surface-container-highest
394
+ // --md-comp-otp-indicator-color on-surface-variant / focus: primary
395
+ //
396
+ // :not(.browser-default) matches the weight of the shared text-input rule
397
+ // above, which would otherwise win on width, height and letter-spacing.
398
+ input.otp:not(.browser-default) {
399
+ --md-comp-otp-length: 6;
400
+ --md-comp-otp-cell-width: 48px;
401
+ --md-comp-otp-cell-height: 56px;
402
+ --md-comp-otp-gap: 8px;
403
+ --md-comp-otp-cell-color: var(--md-sys-color-surface-container-highest);
404
+ --md-comp-otp-indicator-color: var(--md-sys-color-on-surface-variant);
405
+ // Derived, not for overriding: the distance between cell starts, the
406
+ // inset that centres a digit in its cell, and the box past the last cell
407
+ // that the caret needs and the clip hides.
408
+ --md-comp-otp-pitch: calc(var(--md-comp-otp-cell-width) + var(--md-comp-otp-gap));
409
+ --md-comp-otp-inset: calc((var(--md-comp-otp-cell-width) - 1ch) / 2);
410
+ --md-comp-otp-overhang: calc(var(--md-comp-otp-gap) + var(--md-comp-otp-inset) + 2px);
411
+
412
+ box-sizing: border-box;
413
+ display: block;
414
+ // Room for the caret after the last digit, clipped back to the cells; the
415
+ // negative margin gives the overhang back to the layout.
416
+ width: calc(var(--md-comp-otp-length) * var(--md-comp-otp-pitch) + var(--md-comp-otp-inset) + 2px);
417
+ height: var(--md-comp-otp-cell-height);
418
+ margin: 0 calc(var(--md-comp-otp-overhang) * -1) 0 0;
419
+ padding: 0 0 0 var(--md-comp-otp-inset);
420
+ border: 0;
421
+ border-radius: 0;
422
+ outline: 0;
423
+ box-shadow: none;
424
+ clip-path: inset(0 var(--md-comp-otp-overhang) 0 0);
425
+ // Digits read left to right in every script.
426
+ direction: ltr;
427
+ font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, "Liberation Mono", monospace;
428
+ font-size: 24px;
429
+ font-variant-numeric: tabular-nums;
430
+ letter-spacing: calc(var(--md-comp-otp-pitch) - 1ch);
431
+ color: var(--md-sys-color-on-surface);
432
+ caret-color: var(--md-sys-color-primary);
433
+ background-color: transparent;
434
+ background-image:
435
+ linear-gradient(
436
+ to right,
437
+ var(--md-comp-otp-indicator-color) var(--md-comp-otp-cell-width),
438
+ transparent 0
439
+ ),
440
+ linear-gradient(
441
+ to right,
442
+ var(--md-comp-otp-cell-color) var(--md-comp-otp-cell-width),
443
+ transparent 0
444
+ );
445
+ background-size:
446
+ var(--md-comp-otp-pitch) 1px,
447
+ var(--md-comp-otp-pitch) 100%;
448
+ background-position: left bottom, left top;
449
+ background-repeat: repeat-x;
450
+
451
+ &::placeholder {
452
+ color: var(--md-sys-color-on-surface-variant);
453
+ opacity: 1;
454
+ }
455
+
456
+ // Every cell's indicator turns primary and thickens while the code has
457
+ // focus; the clip would cut an outline, so this is the focus ring.
458
+ &:focus,
459
+ &:focus-visible {
460
+ --md-comp-otp-indicator-color: var(--md-sys-color-primary);
461
+
462
+ outline: 0;
463
+ background-size:
464
+ var(--md-comp-otp-pitch) 2px,
465
+ var(--md-comp-otp-pitch) 100%;
466
+ }
467
+
468
+ &.invalid,
469
+ &[aria-invalid="true"] {
470
+ --md-comp-otp-indicator-color: var(--md-sys-color-error);
471
+ }
472
+
473
+ &:disabled {
474
+ opacity: 0.38;
475
+ }
476
+
477
+ @media (forced-colors: active) {
478
+ --md-comp-otp-indicator-color: CanvasText;
479
+
480
+ &:focus {
481
+ --md-comp-otp-indicator-color: Highlight;
482
+ }
483
+ }
484
+ }
@@ -0,0 +1,165 @@
1
+ @use "../../abstracts" as *;
2
+
3
+ // Rating. Two forms share the star.
4
+ //
5
+ // Input: a <fieldset class="rating"> with a <legend> and one radio per star,
6
+ // each in a <label> whose visually hidden text ("3 stars") comes before the
7
+ // radio: a span after it is the older input + span radio, which hides the
8
+ // input. The radio itself is drawn as the star, so arrow keys, form
9
+ // submission and the group's name stay native. A star fills when it or a
10
+ // later star is checked; hovering previews.
11
+ //
12
+ // Display: <span class="rating" role="img" aria-label="Rated 4.5 out of 5">
13
+ // with --md-comp-rating-value. The stars fill to that value, halves included.
14
+ //
15
+ // Loaded after radio-buttons: the input rules match the radio rules' weight
16
+ // and win on order.
17
+ //
18
+ // Tokens:
19
+ // --md-comp-rating-size 24px (display 20px)
20
+ // --md-comp-rating-color tertiary
21
+ // --md-comp-rating-empty-color outline-variant
22
+ // --md-comp-rating-value 0 (display)
23
+ // --md-comp-rating-max 5 (display)
24
+
25
+ $_rating-star: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='m12 17.27 6.18 3.73-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z'/%3E%3C/svg%3E");
26
+
27
+ .rating {
28
+ --md-comp-rating-size: 24px;
29
+ --md-comp-rating-color: var(--md-sys-color-tertiary);
30
+ --md-comp-rating-empty-color: var(--md-sys-color-outline-variant);
31
+ }
32
+
33
+ fieldset.rating {
34
+ display: flex;
35
+ flex-wrap: wrap;
36
+ align-items: center;
37
+ margin: 0;
38
+ padding: 0;
39
+ border: 0;
40
+
41
+ > legend {
42
+ margin-block-end: 4px;
43
+ padding: 0;
44
+ color: var(--md-sys-color-on-surface-variant);
45
+ @include typescale("body-medium");
46
+ }
47
+
48
+ > label {
49
+ display: inline-flex;
50
+ gap: 0;
51
+ min-height: 0;
52
+ padding: 4px;
53
+ border-radius: 50%;
54
+ cursor: pointer;
55
+
56
+ &:has(> input:focus-visible) {
57
+ @include focus-ring;
58
+ }
59
+
60
+ &:has(> input:disabled) {
61
+ opacity: 0.38;
62
+ cursor: default;
63
+ }
64
+ }
65
+
66
+ > label > [type="radio"] {
67
+ flex: none;
68
+ width: var(--md-comp-rating-size);
69
+ height: var(--md-comp-rating-size);
70
+ border: 0;
71
+ border-radius: 0;
72
+ background-color: var(--md-comp-rating-empty-color);
73
+ background-image: none;
74
+ box-shadow: none;
75
+ mask: $_rating-star center / 100% no-repeat;
76
+ outline: 0;
77
+ transition: background-color 0.15s ease-out;
78
+
79
+ // The base radio draws its dot in :checked, :hover and the disabled
80
+ // states, some at a higher weight; the star mask would show it.
81
+ &:checked,
82
+ &:hover,
83
+ &:disabled,
84
+ &:disabled:checked {
85
+ border: 0;
86
+ background-image: none;
87
+ box-shadow: none;
88
+ }
89
+ }
90
+
91
+ // Filled: the checked star and every star before it.
92
+ > label:has(> :checked) > [type="radio"],
93
+ > label:has(~ label > :checked) > [type="radio"] {
94
+ background-color: var(--md-comp-rating-color);
95
+ }
96
+
97
+ // Hovering previews the rating under the pointer. Both rules sit under the
98
+ // same :has() so the preview outranks the reset.
99
+ &:has(> label:hover) {
100
+ > label > [type="radio"] {
101
+ background-color: var(--md-comp-rating-empty-color);
102
+ }
103
+
104
+ > label:hover > [type="radio"],
105
+ > label:has(~ label:hover) > [type="radio"] {
106
+ background-color: color-mix(in oklab, var(--md-comp-rating-color) 70%, transparent);
107
+ }
108
+ }
109
+
110
+ @media (prefers-reduced-motion: reduce) {
111
+ > label > [type="radio"] {
112
+ transition: none;
113
+ }
114
+ }
115
+ }
116
+
117
+ .rating:not(fieldset) {
118
+ --md-comp-rating-size: 20px;
119
+ --md-comp-rating-value: 0;
120
+ --md-comp-rating-max: 5;
121
+
122
+ display: inline-block;
123
+ width: calc(var(--md-comp-rating-size) * var(--md-comp-rating-max));
124
+ height: var(--md-comp-rating-size);
125
+ vertical-align: middle;
126
+ background: linear-gradient(
127
+ to right,
128
+ var(--md-comp-rating-color) calc(var(--md-comp-rating-value) / var(--md-comp-rating-max) * 100%),
129
+ var(--md-comp-rating-empty-color) 0
130
+ );
131
+ mask: $_rating-star left center / var(--md-comp-rating-size) repeat-x;
132
+
133
+ &:dir(rtl) {
134
+ background: linear-gradient(
135
+ to left,
136
+ var(--md-comp-rating-color) calc(var(--md-comp-rating-value) / var(--md-comp-rating-max) * 100%),
137
+ var(--md-comp-rating-empty-color) 0
138
+ );
139
+ mask-position: right center;
140
+ }
141
+ }
142
+
143
+ @media (forced-colors: active) {
144
+ fieldset.rating > label > [type="radio"],
145
+ .rating:not(fieldset) {
146
+ forced-color-adjust: none;
147
+ }
148
+
149
+ fieldset.rating > label > [type="radio"] {
150
+ background-color: GrayText;
151
+ }
152
+
153
+ fieldset.rating > label:has(> :checked) > [type="radio"],
154
+ fieldset.rating > label:has(~ label > :checked) > [type="radio"] {
155
+ background-color: Highlight;
156
+ }
157
+
158
+ .rating:not(fieldset) {
159
+ background: linear-gradient(
160
+ to right,
161
+ Highlight calc(var(--md-comp-rating-value) / var(--md-comp-rating-max) * 100%),
162
+ GrayText 0
163
+ );
164
+ }
165
+ }
@@ -24,21 +24,9 @@
24
24
  // against #006495, the one source there is ground truth for - $md-source below
25
25
  // is a branding choice and is free to change without failing it.
26
26
 
27
- // The seed every generated ramp derives from. Chosen for a restaurant POS: the
28
- // error ramp is pinned at hue 27.7deg and both primary and tertiary (source
29
- // +62deg) derive from this, so the seed has to keep both clear of red. Warm
30
- // "appetising" hues (0-60deg) are disqualified outright - they put primary on
31
- // top of error, dEok 0.07-0.12 against ~0.28 here.
32
- //
33
- // Teal wins on *worst case* across both schemes rather than on either one
34
- // alone, which is the metric that matters: evening service runs dark mode in a
35
- // dim room, and that is where a confirm/void mix-up actually happens.
36
- // light primary40 vs error40 dEok 0.278
37
- // dark primary80 vs error80 dEok 0.212 <- best of the candidates
38
- // light tertiary40 vs error40 dEok 0.224
39
- // It also leaves green free for the "settled/paid" role a POS will want and
40
- // Material does not supply.
41
- $md-source: #006A79 !default;
27
+ // Mist Iris is the default seed for the generated ramps. Primary, secondary,
28
+ // tertiary and neutral colors derive from it; the error ramp stays fixed.
29
+ $md-source: #c6a0f6 !default;
42
30
 
43
31
  // tone -> OKLCH lightness. Fitted against the reference ramps; see the test.
44
32
  $tones: (
@@ -6,3 +6,5 @@
6
6
  @forward "spacing"; // .m-3, .pt-2, .px-4, ...
7
7
  @forward "visibility"; // .hide, .hide-on-med-and-down, .show-on-large, ...
8
8
  @forward "helpers"; // alignment, floats, truncate, responsive media
9
+ @forward "scroll-area"; // native scrolling with themed scrollbars
10
+ @forward "shapes"; // .shape-pebble, .shape-scallop-6, ... (masks)
@@ -0,0 +1,35 @@
1
+ @use "../abstracts" as *;
2
+
3
+ // Keep scrolling native; use the current theme for scrollbar colors.
4
+ .scroll-area {
5
+ overflow: auto;
6
+ scrollbar-width: thin;
7
+ scrollbar-color: var(--scroll-area-thumb-color, var(--md-sys-color-on-surface-variant)) var(--scroll-area-track-color, transparent);
8
+ }
9
+
10
+ // Older WebKit engines do not implement the standard scrollbar properties.
11
+ @supports not (scrollbar-color: red blue) {
12
+ @media (forced-colors: none) {
13
+ .scroll-area::-webkit-scrollbar {
14
+ width: 8px;
15
+ height: 8px;
16
+ }
17
+
18
+ .scroll-area::-webkit-scrollbar-thumb {
19
+ background-color: var(--scroll-area-thumb-color, var(--md-sys-color-on-surface-variant));
20
+ border-radius: 999px;
21
+ }
22
+
23
+ .scroll-area::-webkit-scrollbar-track,
24
+ .scroll-area::-webkit-scrollbar-corner {
25
+ background-color: var(--scroll-area-track-color, transparent);
26
+ }
27
+ }
28
+ }
29
+
30
+ @media (forced-colors: active) {
31
+ .scroll-area {
32
+ scrollbar-width: auto;
33
+ scrollbar-color: auto;
34
+ }
35
+ }
@@ -0,0 +1,164 @@
1
+ @use "sass:list";
2
+ @use "sass:math";
3
+ @use "../abstracts" as *;
4
+
5
+ // Shapes. `.shape-<name>` cuts any element to one of twenty rounded shapes:
6
+ // an <img>, a <video>, a button, a card. The set follows M3 Expressive's
7
+ // shape library; the names are ours.
8
+ //
9
+ // <img class="shape-scallop-6" src="..." alt="...">
10
+ // <button class="icon-button filled shape-spark" aria-label="Favorite">
11
+ //
12
+ // Each shape is an SVG path in a 100-unit box, drawn as a mask. A mask scales
13
+ // with the element and keeps the curves smooth at any size, which a
14
+ // `clip-path: polygon()` only does with hundreds of points. The path stretches
15
+ // to the element's box, so the shapes are drawn for a square one; images and
16
+ // videos are made square and cropped with `object-fit: cover`.
17
+ //
18
+ // A mask also clips the element's outline, so a focused shape draws the ring
19
+ // inside its own edge: the mask cuts the 2px gap and `::after` paints the 3px
20
+ // band. `contain: paint` makes the element the band's containing block without
21
+ // touching `position`, which a component (a halfway FAB) may already own. An
22
+ // <img> has no `::after`, so a focusable image shows the gap alone.
23
+
24
+ // Each shape is a polygon whose corners are cut by quadratic curves. The
25
+ // corner takes `$round` of each edge next to it: 0 keeps the point, 0.5 runs
26
+ // the curve from one edge's midpoint to the next, so a polygon rounded at 0.5
27
+ // throughout is a smooth closed curve. `$round` is a list cycled over the
28
+ // points, so a star can round its tips and valleys differently.
29
+
30
+ // One decimal of a 100-unit box is a tenth of a percent - under half a pixel
31
+ // on a 400px image.
32
+ @function _n($v) {
33
+ @return math.div(math.round($v * 10), 10);
34
+ }
35
+
36
+ @function _xy($p) {
37
+ @return "#{_n(list.nth($p, 1))} #{_n(list.nth($p, 2))}";
38
+ }
39
+
40
+ @function _toward($p, $q, $t) {
41
+ @return (list.nth($p, 1) + (list.nth($q, 1) - list.nth($p, 1)) * $t)
42
+ (list.nth($p, 2) + (list.nth($q, 2) - list.nth($p, 2)) * $t);
43
+ }
44
+
45
+ // $count points on a circle round the box centre, clockwise from $start
46
+ // (y points down, so -90deg is the top). $radii is cycled: (40 30) is a star,
47
+ // (40 40 25) puts two points on every lobe of a clover. $sx and $sy stretch
48
+ // the result before $turn rotates it.
49
+ @function _polar($count, $radii, $start: -90deg, $sx: 1, $sy: 1, $turn: 0deg) {
50
+ $pts: ();
51
+ @for $i from 0 through $count - 1 {
52
+ $a: $start + math.div(360deg * $i, $count);
53
+ $r: list.nth($radii, $i % list.length($radii) + 1);
54
+ $x: $r * math.cos($a) * $sx;
55
+ $y: $r * math.sin($a) * $sy;
56
+ $pts: list.append(
57
+ $pts,
58
+ (50 + $x * math.cos($turn) - $y * math.sin($turn))
59
+ (50 + $x * math.sin($turn) + $y * math.cos($turn)),
60
+ comma
61
+ );
62
+ }
63
+ @return $pts;
64
+ }
65
+
66
+ @function _path($pts, $round) {
67
+ $n: list.length($pts);
68
+ $d: "";
69
+ $end: null;
70
+ @for $i from 1 through $n {
71
+ $p: list.nth($pts, $i);
72
+ $r: list.nth($round, ($i - 1) % list.length($round) + 1);
73
+ $move: "L";
74
+ @if $i == 1 { $move: "M"; }
75
+ @if $r == 0 {
76
+ $d: $d + $move + _xy($p);
77
+ $end: _xy($p);
78
+ } @else {
79
+ $prev: list.nth($pts, $n);
80
+ @if $i > 1 { $prev: list.nth($pts, $i - 1); }
81
+ $next: list.nth($pts, 1);
82
+ @if $i < $n { $next: list.nth($pts, $i + 1); }
83
+ $a: _xy(_toward($p, $prev, $r));
84
+ @if $a != $end { $d: $d + $move + $a; }
85
+ $end: _xy(_toward($p, $next, $r));
86
+ $d: $d + "Q" + _xy($p) + " " + $end;
87
+ }
88
+ }
89
+ @return $d + "Z";
90
+ }
91
+
92
+ @function _mask($pts, $round) {
93
+ @return url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' preserveAspectRatio='none'%3E%3Cpath d='#{_path($pts, $round)}'/%3E%3C/svg%3E");
94
+ }
95
+
96
+ // name: (points, rounding). The M3 shape each one follows:
97
+ // pebble oval, bean pill, peak triangle, dart arrow, slice fan, kite diamond,
98
+ // cell clamshell, crest pentagon, facet gem, spark very sunny, seal sunny,
99
+ // scallop-N N-sided cookie, bloom-N N-leaf clover, spike burst, flare soft burst.
100
+ $_shapes: (
101
+ pebble: (_polar(8, 65.5, -90deg, 1, 0.6, -45deg), 0.5),
102
+ bean: (_polar(4, 74.5, -45deg, 1, 0.78, -45deg), 0.5),
103
+ peak: ((50 -6, 108.6 95, -8.6 95), 0.22),
104
+ dart: ((50 -6, 107 99, 50 80, -7 99), (0.3 0.3 0.5 0.3)),
105
+ slice: ((0 0, 100 0, 100 100, 0 100), (0.16 0.84 0.16 0.16)),
106
+ kite: ((50 -6.2, 97.2 50, 50 106.2, 2.8 50), 0.22),
107
+ cell: ((22 11, 78 11, 104 50, 78 89, 22 89, -4 50), 0.3),
108
+ crest: (_polar(5, 53, -90deg), 0.22),
109
+ facet: (_polar(7, 53, -90deg), 0.3),
110
+ spark: (_polar(16, (52 38)), (0.3 0.5)),
111
+ seal: (_polar(16, (52 44)), (0.42 0.5)),
112
+ scallop-4: (_polar(8, (68 32), -45deg), 0.5),
113
+ scallop-6: (_polar(12, (53.9 44.1)), 0.5),
114
+ scallop-7: (_polar(14, (54.2 45.4)), 0.5),
115
+ scallop-9: (_polar(18, (53 46.2)), 0.5),
116
+ scallop-12: (_polar(24, (52 45.8)), 0.5),
117
+ bloom-4: (_polar(16, (54 57 54 38), -67.5deg), (0.5 0.5 0.5 0.25)),
118
+ bloom-8: (_polar(24, (51 51 40), -97.5deg), (0.5 0.5 0.3)),
119
+ spike: (_polar(24, (50 34)), 0),
120
+ flare: (_polar(20, (51.5 36.6)), (0.18 0.3)),
121
+ );
122
+
123
+ %shape {
124
+ contain: paint;
125
+ mask: var(--shape-mask) center / 100% 100% no-repeat;
126
+
127
+ &:is(img, video, canvas) {
128
+ aspect-ratio: 1;
129
+ height: auto;
130
+ object-fit: cover;
131
+ }
132
+
133
+ // The ring: the outline's 3px band and 2px gap, drawn inside the edge.
134
+ &:focus-visible {
135
+ mask:
136
+ var(--shape-mask) center / calc(100% - 10px) calc(100% - 10px) no-repeat,
137
+ var(--shape-mask) center / 100% 100% no-repeat,
138
+ var(--shape-mask) center / calc(100% - 6px) calc(100% - 6px) no-repeat;
139
+ mask-composite: add, exclude;
140
+ }
141
+
142
+ &:focus-visible::after {
143
+ content: "";
144
+ position: absolute;
145
+ inset: 0;
146
+ background-color: var(--md-sys-color-secondary);
147
+ pointer-events: none;
148
+ mask:
149
+ var(--shape-mask) center / 100% 100% no-repeat exclude,
150
+ var(--shape-mask) center / calc(100% - 6px) calc(100% - 6px) no-repeat;
151
+
152
+ @media (forced-colors: active) {
153
+ forced-color-adjust: none;
154
+ background-color: Highlight;
155
+ }
156
+ }
157
+ }
158
+
159
+ @each $name, $shape in $_shapes {
160
+ .shape-#{$name} {
161
+ @extend %shape;
162
+ --shape-mask: #{_mask(list.nth($shape, 1), list.nth($shape, 2))};
163
+ }
164
+ }
@@ -1,7 +0,0 @@
1
- // src/ts/modular.ts
2
- var version = "0.12.0";
3
-
4
- export {
5
- version
6
- };
7
- //# sourceMappingURL=chunk-G3I5H2U3.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/ts/modular.ts"],
4
- "sourcesContent": ["export * from './components';\nexport { Dialogs } from './behaviors/dialogs';\nexport { BottomSheets } from './behaviors/bottomSheets';\nexport { SideSheets } from './behaviors/sideSheets';\nexport { Forms } from './behaviors/forms';\nexport { AutoInit } from './components/registry';\nexport type { AutoInitOptions } from './components/registry';\n\nexport const version = '0.12.0';\n\nexport type * from './components/autocomplete';\nexport type * from './components/appBar';\nexport type * from './components/buttons';\nexport type * from './components/buttonGroup';\nexport type * from './components/cards';\nexport type * from './components/expandingCard';\nexport type * from './components/carousel';\nexport type * from './components/characterCounter';\nexport type * from './components/chips';\nexport type * from './components/datepicker';\nexport type * from './components/menu';\nexport type * from './components/lightbox';\nexport type * from './components/slider';\nexport type * from './components/scrollspy';\nexport type * from './components/select';\nexport type * from './components/navigationRail';\nexport type * from './components/tabs';\nexport type * from './components/timepicker';\nexport type * from './components/snackbar';\nexport type * from './components/tooltip';\n"],
5
- "mappings": ";AAQO,IAAM,UAAU;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/ts/components/registry.ts"],
4
- "sourcesContent": ["import { InitElements, InitElement } from \"../core/component\";\nimport * as Components from \"./index\";\nimport { CARDS_SELECTOR, isCardReveal } from \"./cards\";\nimport { EXPANDING_CARD_SELECTOR } from \"./expandingCard\";\n\n/**\n * The components `AutoInit()` starts, in initialization order, with the\n * selector each one claims.\n *\n * This table is the single source of truth: the options type below and the\n * init calls in `AutoInit()` are both derived from it, so wiring up a new\n * auto-initialized component means adding one line here (plus its export in\n * ./index.ts) - not four hand-synchronized edits that fail silently when one\n * is forgotten.\n *\n * A component that should only ever be constructed explicitly (Snackbar,\n * CharacterCounter, Range, ...) simply stays out of this table.\n */\nexport const AUTO_INIT_COMPONENTS = {\n AppBar: {\n component: Components.AppBar,\n selector:\n \"header.medium, header.large, header:has(> :is(nav:not(.tabs, .navigation-bar, .navigation-rail), .bar) > :is(.search-bar, .searchbar))\",\n },\n Autocomplete: {\n component: Components.Autocomplete,\n selector: \".autocomplete\",\n },\n ButtonGroup: {\n component: Components.ButtonGroup,\n selector: \".button-group\",\n },\n // Shared with Cards.Init() - see CARDS_SELECTOR. This entry used to read\n // '.cards', which no card markup uses.\n Cards: { component: Components.Cards, selector: CARDS_SELECTOR, filter: isCardReveal },\n ExpandingCard: {\n component: Components.ExpandingCard,\n selector: EXPANDING_CARD_SELECTOR,\n },\n Carousel: { component: Components.Carousel, selector: \".carousel\" },\n Chips: { component: Components.Chips, selector: \".chips\" },\n Datepicker: {\n component: Components.Datepicker,\n selector: \".datepicker, .date-picker\",\n },\n Menu: { component: Components.Menu, selector: \".menu-trigger\" },\n Lightbox: { component: Components.Lightbox, selector: \".lightboxed\" },\n ScrollSpy: { component: Components.ScrollSpy, selector: \".scrollspy\" },\n FormSelect: { component: Components.FormSelect, selector: \"select\" },\n NavigationRail: {\n component: Components.NavigationRail,\n selector: \".navigation-rail\",\n },\n Tabs: { component: Components.Tabs, selector: \".tabs\" },\n Timepicker: {\n component: Components.Timepicker,\n selector: \".timepicker, .time-picker\",\n },\n Tooltip: { component: Components.Tooltip, selector: \".tooltipped\" },\n FloatingActionButton: {\n component: Components.FloatingActionButton,\n selector: \".fab-menu\",\n },\n};\n\ntype Registry = typeof AUTO_INIT_COMPONENTS;\n\n/**\n * Per-component options accepted by {@link AutoInit}, derived from the table\n * above - each key takes that component's own options type.\n */\nexport type AutoInitOptions = {\n [K in keyof Registry]?: Partial<\n InstanceType<Registry[K][\"component\"]>[\"options\"]\n >;\n};\n\n/** The shape every entry in the table satisfies; see the cast in AutoInit. */\ntype AutoInitable = {\n init(els: InitElement | InitElements<InitElement>, options: Partial<object>): unknown;\n};\n\ntype AutoInitEntry = {\n component: AutoInitable;\n selector: string;\n filter?: (el: Element) => boolean;\n};\n\n/**\n * Automatically initialize components.\n * @param context Root element to initialize. Defaults to `document.body`.\n * @param options Options for each component.\n */\nexport function AutoInit(\n context: HTMLElement = document.body,\n options?: Partial<AutoInitOptions>,\n) {\n for (const name of Object.keys(AUTO_INIT_COMPONENTS) as (keyof Registry)[]) {\n const { component, selector, filter } = AUTO_INIT_COMPONENTS[\n name\n ] as unknown as AutoInitEntry;\n // `:is()` so that `:not(.no-autoinit)` applies to the whole selector. A\n // bare `${selector}:not(...)` binds the negation to the last alternative\n // only, which would silently break the opt-out for any comma-separated\n // entry (Cards is the first).\n const els = context.querySelectorAll(`:is(${selector}):not(.no-autoinit)`);\n // Each component's static `init` is overloaded; indexing the table widens\n // it to a union TypeScript cannot call, so narrow it once, here.\n if (filter) {\n els.forEach((el) => {\n if (filter(el)) component.init(el, options?.[name] ?? {});\n });\n } else {\n component.init(els, options?.[name] ?? {});\n }\n }\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkBO,IAAM,uBAAuB;AAAA,EAClC,QAAQ;AAAA,IACN,WAAsB;AAAA,IACtB,UACE;AAAA,EACJ;AAAA,EACA,cAAc;AAAA,IACZ,WAAsB;AAAA,IACtB,UAAU;AAAA,EACZ;AAAA,EACA,aAAa;AAAA,IACX,WAAsB;AAAA,IACtB,UAAU;AAAA,EACZ;AAAA;AAAA;AAAA,EAGA,OAAO,EAAE,WAAsB,OAAO,UAAU,gBAAgB,QAAQ,aAAa;AAAA,EACrF,eAAe;AAAA,IACb,WAAsB;AAAA,IACtB,UAAU;AAAA,EACZ;AAAA,EACA,UAAU,EAAE,WAAsB,UAAU,UAAU,YAAY;AAAA,EAClE,OAAO,EAAE,WAAsB,OAAO,UAAU,SAAS;AAAA,EACzD,YAAY;AAAA,IACV,WAAsB;AAAA,IACtB,UAAU;AAAA,EACZ;AAAA,EACA,MAAM,EAAE,WAAsB,MAAM,UAAU,gBAAgB;AAAA,EAC9D,UAAU,EAAE,WAAsB,UAAU,UAAU,cAAc;AAAA,EACpE,WAAW,EAAE,WAAsB,WAAW,UAAU,aAAa;AAAA,EACrE,YAAY,EAAE,WAAsB,YAAY,UAAU,SAAS;AAAA,EACnE,gBAAgB;AAAA,IACd,WAAsB;AAAA,IACtB,UAAU;AAAA,EACZ;AAAA,EACA,MAAM,EAAE,WAAsB,MAAM,UAAU,QAAQ;AAAA,EACtD,YAAY;AAAA,IACV,WAAsB;AAAA,IACtB,UAAU;AAAA,EACZ;AAAA,EACA,SAAS,EAAE,WAAsB,SAAS,UAAU,cAAc;AAAA,EAClE,sBAAsB;AAAA,IACpB,WAAsB;AAAA,IACtB,UAAU;AAAA,EACZ;AACF;AA8BO,SAAS,SACd,UAAuB,SAAS,MAChC,SACA;AACA,aAAW,QAAQ,OAAO,KAAK,oBAAoB,GAAyB;AAC1E,UAAM,EAAE,WAAW,UAAU,OAAO,IAAI,qBACtC,IACF;AAKA,UAAM,MAAM,QAAQ,iBAAiB,OAAO,QAAQ,qBAAqB;AAGzE,QAAI,QAAQ;AACV,UAAI,QAAQ,CAAC,OAAO;AAClB,YAAI,OAAO,EAAE,EAAG,WAAU,KAAK,IAAI,UAAU,IAAI,KAAK,CAAC,CAAC;AAAA,MAC1D,CAAC;AAAA,IACH,OAAO;AACL,gBAAU,KAAK,KAAK,UAAU,IAAI,KAAK,CAAC,CAAC;AAAA,IAC3C;AAAA,EACF;AACF;",
6
- "names": []
7
- }