@happyvertical/smrt-ui 0.55.16 → 0.55.18

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 (166) hide show
  1. package/AGENTS.md +14 -0
  2. package/README.md +75 -0
  3. package/dist/components/calendar/Calendar.svelte +59 -56
  4. package/dist/components/calendar/CalendarView.svelte +80 -77
  5. package/dist/components/calendar/DayView.svelte +39 -36
  6. package/dist/components/chat/MessageBubble.svelte +26 -23
  7. package/dist/components/chat/ReactionPicker.svelte +12 -9
  8. package/dist/components/chat/TypingIndicator.svelte +12 -9
  9. package/dist/components/data/CollectionList.svelte +11 -8
  10. package/dist/components/data/CollectionToolbar.svelte +5 -2
  11. package/dist/components/data/DataTable.svelte +74 -59
  12. package/dist/components/data/ListSortSelect.svelte +8 -5
  13. package/dist/components/data/SortableHeader.svelte +10 -7
  14. package/dist/components/display/ConfidenceBadge.svelte +11 -8
  15. package/dist/components/display/CurrencyDisplay.svelte +7 -4
  16. package/dist/components/display/DateDisplay.svelte +6 -3
  17. package/dist/components/display/Icon.svelte +4 -1
  18. package/dist/components/display/StatusBadge.svelte +9 -6
  19. package/dist/components/feedback/Alert.svelte +12 -8
  20. package/dist/components/feedback/ConfirmDialog.svelte +13 -10
  21. package/dist/components/feedback/LoadingOverlay.svelte +37 -34
  22. package/dist/components/feedback/Meter.svelte +4 -1
  23. package/dist/components/feedback/Modal.svelte +26 -21
  24. package/dist/components/feedback/PhoneSheet.svelte +6 -3
  25. package/dist/components/feedback/Progress.svelte +8 -5
  26. package/dist/components/feedback/ProgressBar.svelte +13 -10
  27. package/dist/components/feedback/Spinner.svelte +4 -1
  28. package/dist/components/feedback/ToastViewport.svelte +18 -15
  29. package/dist/components/feedback/WorkingStrip.svelte +6 -3
  30. package/dist/components/forms/CameraCapture.svelte +8 -5
  31. package/dist/components/forms/Checkbox.svelte +11 -8
  32. package/dist/components/forms/Combobox.svelte +5 -2
  33. package/dist/components/forms/DatePicker.svelte +7 -2
  34. package/dist/components/forms/DictationButton.svelte +17 -14
  35. package/dist/components/forms/DictationStatus.svelte +12 -9
  36. package/dist/components/forms/ErrorSummary.svelte +4 -1
  37. package/dist/components/forms/FieldLabel.svelte +9 -6
  38. package/dist/components/forms/Fieldset.svelte +4 -1
  39. package/dist/components/forms/FilePicker.svelte +5 -2
  40. package/dist/components/forms/Form.svelte +1 -1
  41. package/dist/components/forms/FormActionBar.svelte +4 -1
  42. package/dist/components/forms/FormGroup.svelte +6 -3
  43. package/dist/components/forms/Input.svelte +22 -15
  44. package/dist/components/forms/InputGroup.svelte +6 -3
  45. package/dist/components/forms/Listbox.svelte +5 -2
  46. package/dist/components/forms/MultiSelect.svelte +4 -1
  47. package/dist/components/forms/Radio.svelte +9 -6
  48. package/dist/components/forms/RadioGroup.svelte +5 -2
  49. package/dist/components/forms/RangeSlider.svelte +5 -2
  50. package/dist/components/forms/RelationInput.svelte +7 -4
  51. package/dist/components/forms/SearchInput.svelte +7 -4
  52. package/dist/components/forms/SegmentedControl.svelte +6 -3
  53. package/dist/components/forms/Select.svelte +20 -13
  54. package/dist/components/forms/SignaturePad.svelte +8 -5
  55. package/dist/components/forms/Slider.svelte +5 -2
  56. package/dist/components/forms/StagedControlReview.svelte +15 -12
  57. package/dist/components/forms/Switch.svelte +8 -5
  58. package/dist/components/forms/TagsInput.svelte +5 -2
  59. package/dist/components/forms/Textarea.svelte +20 -13
  60. package/dist/components/forms/TimePicker.svelte +7 -2
  61. package/dist/components/forms/Toggle.svelte +14 -11
  62. package/dist/components/forms/ToggleButton.svelte +5 -2
  63. package/dist/components/layout/ActionGroup.svelte +4 -1
  64. package/dist/components/layout/Container.svelte +4 -1
  65. package/dist/components/layout/EmptyState.svelte +11 -8
  66. package/dist/components/layout/Footer.svelte +4 -1
  67. package/dist/components/layout/Grid.svelte +13 -10
  68. package/dist/components/layout/Header.svelte +4 -1
  69. package/dist/components/layout/Masthead.svelte +4 -1
  70. package/dist/components/layout/PageHeader.svelte +12 -9
  71. package/dist/components/layout/PageLayout.svelte +3 -0
  72. package/dist/components/layout/SummaryCard.svelte +9 -6
  73. package/dist/components/memberships/MembershipCard.svelte +5 -2
  74. package/dist/components/memberships/MembershipList.svelte +5 -2
  75. package/dist/components/nav/FilterChips.svelte +13 -10
  76. package/dist/components/nav/Tabs.svelte +75 -41
  77. package/dist/components/permissions/PermissionPicker.svelte +5 -2
  78. package/dist/components/roles/RoleBadge.svelte +8 -5
  79. package/dist/components/roles/RoleSelector.svelte +22 -19
  80. package/dist/components/theme/ThemeProvider.svelte +7 -4
  81. package/dist/components/ui/Accordion.svelte +4 -1
  82. package/dist/components/ui/AccordionItem.svelte +5 -2
  83. package/dist/components/ui/Avatar.svelte +10 -7
  84. package/dist/components/ui/Badge.svelte +6 -2
  85. package/dist/components/ui/Button.svelte +49 -37
  86. package/dist/components/ui/Card.svelte +18 -14
  87. package/dist/components/ui/Chip.svelte +41 -14
  88. package/dist/components/ui/Disclosure.svelte +5 -2
  89. package/dist/components/ui/Dropdown.svelte +33 -25
  90. package/dist/components/ui/IconToggle.svelte +12 -9
  91. package/dist/components/ui/IconToggle.svelte.d.ts +1 -1
  92. package/dist/components/ui/IconToggle.svelte.d.ts.map +1 -1
  93. package/dist/components/ui/IconToggleGroup.svelte +8 -5
  94. package/dist/components/ui/Pagination.svelte +11 -8
  95. package/dist/components/ui/Popover.svelte +9 -6
  96. package/dist/components/ui/Skeleton.svelte +10 -7
  97. package/dist/components/ui/Tooltip.svelte +13 -9
  98. package/dist/components/ui/Tree.svelte +12 -9
  99. package/dist/styles/layers.css +8 -0
  100. package/dist/styles/layers.d.ts +9 -0
  101. package/dist/styles/layers.d.ts.map +1 -0
  102. package/dist/styles/layers.js +8 -0
  103. package/dist/styles/tokens.css +27 -69
  104. package/dist/styles/unstyled.css +65 -0
  105. package/dist/svelte/playground/BaseControlsPreview.svelte +3 -0
  106. package/dist/svelte/playground/CalendarPreview.svelte +5 -2
  107. package/dist/svelte/playground/CapturePreview.svelte +3 -0
  108. package/dist/svelte/playground/CodeSelectorsPreview.svelte +3 -0
  109. package/dist/svelte/playground/CollectionsPreview.svelte +3 -0
  110. package/dist/svelte/playground/DataTablePreview.svelte +3 -0
  111. package/dist/svelte/playground/FeedbackPreview.svelte +3 -0
  112. package/dist/svelte/playground/InteractiveControlsPreview.svelte +3 -0
  113. package/dist/svelte/playground/StylelessPreview.svelte +110 -0
  114. package/dist/svelte/playground/StylelessPreview.svelte.d.ts +4 -0
  115. package/dist/svelte/playground/StylelessPreview.svelte.d.ts.map +1 -0
  116. package/dist/svelte/playground.d.ts.map +1 -1
  117. package/dist/svelte/playground.js +10 -0
  118. package/dist/test-support/setup.js +9 -0
  119. package/dist/theme/ThemeProvider.svelte +3 -0
  120. package/dist/themes/CUSTOM_THEME_GUIDE.md +40 -0
  121. package/dist/themes/README.md +151 -0
  122. package/dist/themes/ThemeProvider.svelte +181 -12
  123. package/dist/themes/ThemeProvider.svelte.d.ts +7 -2
  124. package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -1
  125. package/dist/themes/components/ColorSchemeToggle.svelte +17 -14
  126. package/dist/themes/components/ThemeSwitcher.svelte +14 -11
  127. package/dist/themes/create-theme.d.ts +22 -1
  128. package/dist/themes/create-theme.d.ts.map +1 -1
  129. package/dist/themes/create-theme.js +100 -9
  130. package/dist/themes/css-generator.d.ts.map +1 -1
  131. package/dist/themes/css-generator.js +20 -20
  132. package/dist/themes/derive.d.ts +31 -0
  133. package/dist/themes/derive.d.ts.map +1 -0
  134. package/dist/themes/derive.js +183 -0
  135. package/dist/themes/glass/index.d.ts.map +1 -1
  136. package/dist/themes/glass/index.js +127 -14
  137. package/dist/themes/happyvertical/index.d.ts.map +1 -1
  138. package/dist/themes/happyvertical/index.js +101 -20
  139. package/dist/themes/index.d.ts +2 -2
  140. package/dist/themes/index.d.ts.map +1 -1
  141. package/dist/themes/index.js +1 -1
  142. package/dist/themes/material/index.d.ts.map +1 -1
  143. package/dist/themes/material/index.js +129 -28
  144. package/dist/themes/required-tokens.generated.json +201 -0
  145. package/dist/themes/shared.d.ts +35 -0
  146. package/dist/themes/shared.d.ts.map +1 -1
  147. package/dist/themes/shared.js +32 -0
  148. package/dist/themes/smrt/index.d.ts +3 -0
  149. package/dist/themes/smrt/index.d.ts.map +1 -1
  150. package/dist/themes/smrt/index.js +108 -26
  151. package/dist/themes/studio/index.d.ts.map +1 -1
  152. package/dist/themes/studio/index.js +116 -40
  153. package/dist/themes/styles/base.css +229 -1
  154. package/dist/themes/styles/glass.css +107 -20
  155. package/dist/themes/styles/happyvertical.css +111 -15
  156. package/dist/themes/styles/material.css +127 -35
  157. package/dist/themes/styles/smrt.css +126 -31
  158. package/dist/themes/styles/studio.css +132 -37
  159. package/dist/themes/theme-script.d.ts +3 -1
  160. package/dist/themes/theme-script.d.ts.map +1 -1
  161. package/dist/themes/theme-script.js +7 -2
  162. package/dist/themes/types.d.ts +118 -6
  163. package/dist/themes/types.d.ts.map +1 -1
  164. package/dist/utils/theme/typography.d.ts +21 -21
  165. package/dist/utils/theme/typography.js +21 -21
  166. package/package.json +8 -2
@@ -129,12 +129,12 @@ function handleClick(event: MouseEvent) {
129
129
  aria-disabled={isDisabled}
130
130
  aria-busy={loading}
131
131
  onclick={handleClick as HTMLAnchorAttributes['onclick']}
132
- {...linkProps()}
132
+ {...linkProps()} data-smrt-component="Button"
133
133
  >
134
134
  {#if loading}
135
- <span class="spinner" aria-hidden="true"></span>
135
+ <span class="spinner" aria-hidden="true" data-smrt-part="spinner"></span>
136
136
  {/if}
137
- <span class="content" class:loading>
137
+ <span class="content" class:loading data-smrt-part="label">
138
138
  {@render children?.()}
139
139
  </span>
140
140
  </a>
@@ -147,33 +147,65 @@ function handleClick(event: MouseEvent) {
147
147
  class:full-width={fullWidth}
148
148
  class:loading
149
149
  onclick={handleClick}
150
- {...rest}
150
+ {...rest} data-smrt-component="Button"
151
151
  >
152
152
  {#if loading}
153
- <span class="spinner" aria-hidden="true"></span>
153
+ <span class="spinner" aria-hidden="true" data-smrt-part="spinner"></span>
154
154
  {/if}
155
- <span class="content" class:loading>
155
+ <span class="content" class:loading data-smrt-part="label">
156
156
  {@render children?.()}
157
157
  </span>
158
158
  </button>
159
159
  {/if}
160
160
 
161
161
  <style>
162
+ @layer smrt.reset, smrt.structure, smrt.theme;
163
+ @layer smrt.theme {
162
164
  .button {
163
165
  display: inline-flex;
164
166
  align-items: center;
165
167
  justify-content: center;
166
168
  gap: var(--smrt-spacing-2);
167
- font-weight: var(--smrt-typography-body-medium-weight);
168
- border-radius: var(--smrt-radius-medium);
169
+ position: relative;
170
+ font-weight: var(--smrt-control-weight);
171
+ border-radius: var(--smrt-control-radius);
169
172
  transition: all var(--smrt-duration-short2) var(--smrt-easing-standard);
170
173
  cursor: pointer;
171
174
  text-decoration: none;
172
175
  border: 1px solid transparent;
173
176
  }
174
177
 
178
+ /* One state layer for every variant: currentColor mixed at the theme's
179
+ state opacity. Per-variant tweaks use --smrt-button-state-* names. */
180
+ .button::after {
181
+ content: '';
182
+ position: absolute;
183
+ inset: 0;
184
+ border-radius: inherit;
185
+ background: transparent;
186
+ pointer-events: none;
187
+ transition: background-color var(--smrt-duration-short2) var(--smrt-easing-standard);
188
+ }
189
+
190
+ .button:hover:not(:disabled):not(.disabled)::after {
191
+ background: color-mix(
192
+ in srgb,
193
+ currentColor calc(var(--smrt-state-hover) * 100%),
194
+ transparent
195
+ );
196
+ }
197
+
198
+ .button:active:not(:disabled):not(.disabled)::after {
199
+ background: color-mix(
200
+ in srgb,
201
+ currentColor calc(var(--smrt-state-pressed) * 100%),
202
+ transparent
203
+ );
204
+ }
205
+
175
206
  /* Sizes */
176
207
  .sm {
208
+ border-radius: var(--smrt-control-radius-small);
177
209
  padding: var(--smrt-spacing-2) var(--smrt-spacing-3);
178
210
  font-size: var(--smrt-typography-body-medium-size);
179
211
  }
@@ -194,47 +226,25 @@ function handleClick(event: MouseEvent) {
194
226
  color: var(--smrt-color-on-primary);
195
227
  }
196
228
 
197
- /* M3 hover state layer: 8% of the on-color blended over the base, so the
198
- button visibly darkens on hover instead of repeating its base color. */
199
- .primary:hover:not(:disabled):not(.disabled) {
200
- background: color-mix(
201
- in srgb,
202
- var(--smrt-color-on-primary) 8%,
203
- var(--smrt-color-primary)
204
- );
205
- }
206
229
 
207
230
  .secondary {
208
- background: var(--smrt-color-surface);
231
+ background: var(--smrt-surface-control);
209
232
  color: var(--smrt-color-primary);
210
233
  border-color: var(--smrt-color-primary);
211
234
  }
212
235
 
213
- .secondary:hover:not(:disabled):not(.disabled) {
214
- background: var(--smrt-color-primary-container);
215
- }
216
236
 
217
237
  .ghost {
218
238
  background: transparent;
219
239
  color: var(--smrt-color-primary);
220
240
  }
221
241
 
222
- .ghost:hover:not(:disabled):not(.disabled) {
223
- background: var(--smrt-color-primary-container);
224
- }
225
242
 
226
243
  .danger {
227
244
  background: var(--smrt-color-error);
228
245
  color: var(--smrt-color-on-error);
229
246
  }
230
247
 
231
- .danger:hover:not(:disabled):not(.disabled) {
232
- background: color-mix(
233
- in srgb,
234
- var(--smrt-color-on-error) 8%,
235
- var(--smrt-color-error)
236
- );
237
- }
238
248
 
239
249
  /* Full width */
240
250
  .full-width {
@@ -255,7 +265,7 @@ function handleClick(event: MouseEvent) {
255
265
  height: 1em;
256
266
  border: 2px solid currentColor;
257
267
  border-right-color: transparent;
258
- border-radius: var(--smrt-radius-full, 9999px);
268
+ border-radius: var(--smrt-radius-full);
259
269
  animation: spin 0.75s linear infinite;
260
270
  }
261
271
 
@@ -287,15 +297,15 @@ function handleClick(event: MouseEvent) {
287
297
  /* Disabled state */
288
298
  .button:disabled,
289
299
  .button.disabled {
290
- opacity: 0.5;
300
+ opacity: var(--smrt-state-disabled);
291
301
  cursor: not-allowed;
292
302
  pointer-events: none;
293
303
  }
294
304
 
295
305
  /* Focus state */
296
306
  .button:focus-visible {
297
- outline: 2px solid var(--smrt-color-primary);
298
- outline-offset: 2px;
307
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
308
+ outline-offset: var(--smrt-focus-ring-offset);
299
309
  }
300
310
 
301
311
  /* Phones: every size is at least a 44px touch target. */
@@ -312,7 +322,9 @@ function handleClick(event: MouseEvent) {
312
322
  }
313
323
  }
314
324
 
315
- .button[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min, 48px); }
325
+ .button[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
316
326
  .button[data-density='comfortable'] { --smrt-control-target-min: 0px; }
317
- .button { min-height: var(--smrt-control-target-min, 0px); min-width: var(--smrt-control-target-min, 0px); }
327
+ .button { min-height: max(var(--smrt-control-height), var(--smrt-control-target-min, 0px)); min-width: var(--smrt-control-target-min, 0px); }
328
+ .button.sm { min-height: max(var(--smrt-control-height-small), var(--smrt-control-target-min, 0px)); }
329
+ }
318
330
  </style>
@@ -32,26 +32,28 @@ const {
32
32
  <div
33
33
  class="card {variant} padding-{padding}"
34
34
  class:hoverable
35
- {...rest}
35
+ {...rest} data-smrt-component="Card"
36
36
  >
37
37
  {#if header}
38
- <div class="card-header">
38
+ <div class="card-header" data-smrt-part="header">
39
39
  {@render header()}
40
40
  </div>
41
41
  {/if}
42
42
 
43
- <div class="card-content">
43
+ <div class="card-content" data-smrt-part="body">
44
44
  {@render children?.()}
45
45
  </div>
46
46
 
47
47
  {#if footer}
48
- <div class="card-footer">
48
+ <div class="card-footer" data-smrt-part="footer">
49
49
  {@render footer()}
50
50
  </div>
51
51
  {/if}
52
52
  </div>
53
53
 
54
54
  <style>
55
+ @layer smrt.reset, smrt.structure, smrt.theme;
56
+ @layer smrt.theme {
55
57
  /*
56
58
  * Themeable look (app-level custom properties; defaults keep the stock look):
57
59
  * --smrt-card-border edge of default and elevated cards
@@ -61,29 +63,30 @@ const {
61
63
  * The `outlined` variant always draws its outline.
62
64
  */
63
65
  .card {
64
- background: var(--smrt-card-background, var(--smrt-color-surface));
66
+ background: var(--smrt-card-background, var(--smrt-surface-panel));
65
67
  border-radius: var(--smrt-radius-medium);
66
68
  transition: all var(--smrt-duration-short2) var(--smrt-easing-standard);
67
69
  }
68
70
 
69
71
  /* Variants */
70
72
  .default {
71
- border: var(--smrt-card-border, 1px solid var(--smrt-color-outline-variant));
72
- box-shadow: var(--smrt-card-shadow, none);
73
+ border: var(--smrt-card-border, var(--smrt-hairline));
74
+ box-shadow: var(--smrt-card-shadow, var(--smrt-inset-highlight));
73
75
  }
74
76
 
75
77
  .outlined {
76
- border: 2px solid var(--smrt-color-outline);
78
+ border: var(--smrt-hairline-strong);
79
+ box-shadow: var(--smrt-inset-highlight);
77
80
  }
78
81
 
79
82
  .elevated {
80
- border: var(--smrt-card-border, 1px solid var(--smrt-color-outline-variant));
81
- box-shadow: var(--smrt-elevation-2);
83
+ border: var(--smrt-card-border, var(--smrt-hairline));
84
+ box-shadow: var(--smrt-inset-highlight), var(--smrt-elevation-2);
82
85
  }
83
86
 
84
87
  /* Hoverable state */
85
88
  .hoverable:hover {
86
- box-shadow: var(--smrt-elevation-3);
89
+ box-shadow: var(--smrt-inset-highlight), var(--smrt-elevation-3);
87
90
  transform: translateY(-2px);
88
91
  }
89
92
 
@@ -107,7 +110,7 @@ const {
107
110
  /* Header */
108
111
  .card-header {
109
112
  padding: var(--smrt-spacing-6);
110
- border-bottom: var(--smrt-card-divider, 1px solid var(--smrt-color-outline-variant));
113
+ border-bottom: var(--smrt-card-divider, var(--smrt-hairline));
111
114
  }
112
115
 
113
116
  .padding-sm .card-header {
@@ -121,8 +124,8 @@ const {
121
124
  /* Footer */
122
125
  .card-footer {
123
126
  padding: var(--smrt-spacing-6);
124
- border-top: var(--smrt-card-divider, 1px solid var(--smrt-color-outline-variant));
125
- background: var(--smrt-color-surface-container-low);
127
+ border-top: var(--smrt-card-divider, var(--smrt-hairline));
128
+ background: var(--smrt-surface-sunken);
126
129
  border-bottom-left-radius: var(--smrt-radius-medium);
127
130
  border-bottom-right-radius: var(--smrt-radius-medium);
128
131
  }
@@ -134,4 +137,5 @@ const {
134
137
  .padding-lg .card-footer {
135
138
  padding: var(--smrt-spacing-8);
136
139
  }
140
+ }
137
141
  </style>
@@ -49,7 +49,7 @@ const {
49
49
  class:selected
50
50
  class:disabled
51
51
  class:selectable
52
- class:closeable
52
+ class:closeable data-smrt-component="Chip"
53
53
  >
54
54
  {#if selectable}
55
55
  <button
@@ -93,23 +93,49 @@ const {
93
93
  </span>
94
94
 
95
95
  <style>
96
+ @layer smrt.reset, smrt.structure, smrt.theme;
97
+ @layer smrt.theme {
96
98
  .chip {
99
+ position: relative;
97
100
  display: inline-flex;
98
101
  align-items: center;
99
- gap: var(--smrt-spacing-1, 4px);
100
- border-radius: var(--smrt-radius-full, 9999px);
101
- background: var(--smrt-color-surface-container-high);
102
+ gap: var(--smrt-spacing-1);
103
+ border-radius: var(--smrt-control-radius);
104
+ background: var(--smrt-surface-control);
102
105
  color: var(--smrt-color-on-surface);
103
- border: 1px solid var(--smrt-color-outline-variant, #cac4d0);
106
+ border: var(--smrt-hairline);
107
+ font-weight: var(--smrt-control-weight);
104
108
  max-width: 100%;
105
109
  }
106
110
 
111
+ /* State layer: currentColor mixed at the theme's state opacity. */
112
+ .chip::after {
113
+ content: '';
114
+ position: absolute;
115
+ inset: 0;
116
+ border-radius: inherit;
117
+ background: transparent;
118
+ pointer-events: none;
119
+ }
120
+
121
+ .chip.selectable:hover::after {
122
+ background: color-mix(in srgb, currentColor calc(var(--smrt-state-hover) * 100%), transparent);
123
+ }
124
+
125
+ .chip.selectable:active::after {
126
+ background: color-mix(in srgb, currentColor calc(var(--smrt-state-pressed) * 100%), transparent);
127
+ }
128
+
107
129
  .chip.sm {
108
- padding: 0 var(--smrt-spacing-1, 4px);
130
+ padding: 0 var(--smrt-spacing-1);
109
131
  font-size: var(--smrt-typography-label-small-size, 0.6875rem);
110
132
  }
133
+ .chip.sm {
134
+ border-radius: var(--smrt-control-radius-small);
135
+ }
111
136
  .chip.md {
112
- padding: 0 var(--smrt-spacing-2, 8px);
137
+ min-block-size: var(--smrt-control-height-small);
138
+ padding: 0 var(--smrt-spacing-2);
113
139
  font-size: var(--smrt-typography-label-large-size, 0.875rem);
114
140
  }
115
141
 
@@ -120,7 +146,7 @@ const {
120
146
  }
121
147
 
122
148
  .chip.disabled {
123
- opacity: 0.5;
149
+ opacity: var(--smrt-state-disabled);
124
150
  pointer-events: none;
125
151
  }
126
152
 
@@ -130,7 +156,7 @@ const {
130
156
  border: none;
131
157
  color: inherit;
132
158
  font: inherit;
133
- padding: var(--smrt-spacing-1, 4px) var(--smrt-spacing-1, 4px);
159
+ padding: var(--smrt-spacing-1) var(--smrt-spacing-1);
134
160
  cursor: default;
135
161
  white-space: nowrap;
136
162
  overflow: hidden;
@@ -150,18 +176,19 @@ const {
150
176
  border: none;
151
177
  color: inherit;
152
178
  cursor: pointer;
153
- padding: var(--smrt-spacing-1, 4px);
154
- border-radius: var(--smrt-radius-full, 9999px);
179
+ padding: var(--smrt-spacing-1);
180
+ border-radius: var(--smrt-radius-full);
155
181
  opacity: 0.7;
156
182
  }
157
183
  .chip__close:hover {
158
184
  opacity: 1;
159
- background: color-mix(in srgb, currentColor 12%, transparent);
185
+ background: color-mix(in srgb, currentColor calc(var(--smrt-state-hover) * 100%), transparent);
160
186
  }
161
187
 
162
188
  .chip__body:focus-visible,
163
189
  .chip__close:focus-visible {
164
- outline: 2px solid var(--smrt-color-primary);
165
- outline-offset: 1px;
190
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
191
+ outline-offset: var(--smrt-focus-ring-offset);
166
192
  }
193
+ }
167
194
  </style>
@@ -36,7 +36,7 @@ function handleToggle(event: Event) {
36
36
  }
37
37
  </script>
38
38
 
39
- <details class="disclosure {className}" bind:open ontoggle={handleToggle}>
39
+ <details class="disclosure {className}" bind:open ontoggle={handleToggle} data-smrt-component="Disclosure">
40
40
  <summary aria-disabled={disabled} onclick={(event) => disabled && event.preventDefault()}>
41
41
  <span>{title}</span><span class="chevron" aria-hidden="true">⌄</span>
42
42
  </summary>
@@ -44,13 +44,16 @@ function handleToggle(event: Event) {
44
44
  </details>
45
45
 
46
46
  <style>
47
+ @layer smrt.reset, smrt.structure, smrt.theme;
48
+ @layer smrt.theme {
47
49
  .disclosure { border-bottom: 1px solid var(--smrt-color-outline-variant); color: var(--smrt-color-on-surface); }
48
50
  summary { display: flex; justify-content: space-between; gap: var(--smrt-spacing-3); padding: var(--smrt-spacing-3) 0; font: var(--smrt-typography-title-small-font); cursor: pointer; list-style: none; }
49
51
  summary::-webkit-details-marker { display: none; }
50
- summary:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: 2px; }
52
+ summary:focus-visible { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
51
53
  summary[aria-disabled='true'] { opacity: .5; cursor: not-allowed; }
52
54
  .chevron { transition: transform var(--smrt-duration-short4) var(--smrt-easing-standard); }
53
55
  details[open] .chevron { transform: rotate(180deg); }
54
56
  .content { padding: 0 0 var(--smrt-spacing-4); color: var(--smrt-color-on-surface-variant); }
55
57
  @media (prefers-reduced-motion: reduce) { .chevron { transition: none; } }
58
+ }
56
59
  </style>
@@ -147,7 +147,7 @@ $effect(() => {
147
147
  });
148
148
  </script>
149
149
 
150
- <span class="dropdown" bind:this={wrapEl}>
150
+ <span class="dropdown" bind:this={wrapEl} data-smrt-component="Dropdown">
151
151
  <button
152
152
  bind:this={triggerEl}
153
153
  type="button"
@@ -193,6 +193,8 @@ $effect(() => {
193
193
  </span>
194
194
 
195
195
  <style>
196
+ @layer smrt.reset, smrt.structure, smrt.theme;
197
+ @layer smrt.theme {
196
198
  .dropdown {
197
199
  position: relative;
198
200
  display: inline-flex;
@@ -202,15 +204,16 @@ $effect(() => {
202
204
  appearance: none;
203
205
  font: inherit;
204
206
  cursor: pointer;
205
- padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-3, 12px);
206
- border-radius: var(--smrt-radius-medium, 8px);
207
- border: 1px solid var(--smrt-color-outline, #79747e);
208
- background: var(--smrt-color-surface);
207
+ padding: var(--smrt-spacing-2) var(--smrt-spacing-3);
208
+ border-radius: var(--smrt-control-radius);
209
+ border: var(--smrt-hairline-strong);
210
+ background: var(--smrt-surface-control);
209
211
  color: var(--smrt-color-on-surface);
212
+ font-weight: var(--smrt-control-weight);
210
213
  }
211
214
  .dropdown__trigger:focus-visible {
212
- outline: 2px solid var(--smrt-color-primary);
213
- outline-offset: 2px;
215
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
216
+ outline-offset: var(--smrt-focus-ring-offset);
214
217
  }
215
218
  /* Icon trigger: a 44px round "more" button in the surrounding text colour. */
216
219
  .dropdown__trigger--icon {
@@ -222,16 +225,16 @@ $effect(() => {
222
225
  block-size: 2.75rem;
223
226
  padding: 0;
224
227
  border: none;
225
- border-radius: var(--smrt-radius-full, 999px);
228
+ border-radius: var(--smrt-radius-full);
226
229
  background: transparent;
227
230
  color: inherit;
228
231
  }
229
232
  .dropdown__trigger--icon:hover:not(:disabled),
230
233
  .dropdown__trigger--icon[aria-expanded='true'] {
231
- background: color-mix(in srgb, currentColor 10%, transparent);
234
+ background: color-mix(in srgb, currentColor calc(var(--smrt-state-hover) * 100%), transparent);
232
235
  }
233
236
  .dropdown__trigger:disabled {
234
- opacity: 0.5;
237
+ opacity: var(--smrt-state-disabled);
235
238
  cursor: not-allowed;
236
239
  }
237
240
 
@@ -239,15 +242,16 @@ $effect(() => {
239
242
  position: absolute;
240
243
  z-index: var(--smrt-z-index-dropdown, 1000);
241
244
  min-width: 10rem;
242
- margin: var(--smrt-spacing-1, 4px) 0;
243
- padding: var(--smrt-spacing-1, 4px);
245
+ margin: var(--smrt-spacing-1) 0;
246
+ padding: var(--smrt-spacing-1);
244
247
  display: flex;
245
248
  flex-direction: column;
246
- gap: var(--smrt-spacing-1, 4px);
247
- border-radius: var(--smrt-radius-medium, 8px);
248
- background: var(--smrt-color-surface-container, #f3edf7);
249
- border: 1px solid var(--smrt-color-outline-variant, #cac4d0);
250
- box-shadow: var(--smrt-elevation-2, 0 2px 6px rgba(0, 0, 0, 0.15));
249
+ gap: var(--smrt-spacing-1);
250
+ border-radius: var(--smrt-radius-medium);
251
+ background: var(--smrt-glass-surface-strong);
252
+ backdrop-filter: blur(var(--smrt-glass-blur)) saturate(var(--smrt-glass-saturation));
253
+ border: var(--smrt-hairline);
254
+ box-shadow: var(--smrt-inset-highlight), var(--smrt-elevation-3);
251
255
  }
252
256
 
253
257
  .dropdown__menu--bottom-start {
@@ -271,16 +275,16 @@ $effect(() => {
271
275
  appearance: none;
272
276
  display: flex;
273
277
  align-items: center;
274
- gap: var(--smrt-spacing-2, 8px);
278
+ gap: var(--smrt-spacing-2);
275
279
  /* A touch target: menu rows are at least 44px tall. */
276
280
  min-block-size: 2.75rem;
277
281
  box-sizing: border-box;
278
282
  font: inherit;
279
283
  text-align: left;
280
284
  cursor: pointer;
281
- padding: var(--smrt-spacing-2, 8px) var(--smrt-spacing-3, 12px);
285
+ padding: var(--smrt-spacing-2) var(--smrt-spacing-3);
282
286
  border: none;
283
- border-radius: var(--smrt-radius-small, 4px);
287
+ border-radius: var(--smrt-radius-small);
284
288
  background: none;
285
289
  color: var(--smrt-color-on-surface);
286
290
  white-space: nowrap;
@@ -291,7 +295,10 @@ $effect(() => {
291
295
  color: var(--smrt-color-on-surface-variant);
292
296
  }
293
297
  .dropdown__item:hover:not(:disabled) {
294
- background: var(--smrt-color-surface-container-high);
298
+ background: color-mix(in srgb, currentColor calc(var(--smrt-state-hover) * 100%), transparent);
299
+ }
300
+ .dropdown__item:active:not(:disabled) {
301
+ background: color-mix(in srgb, currentColor calc(var(--smrt-state-pressed) * 100%), transparent);
295
302
  }
296
303
  /* Keyboard focus needs its own indicator: the hover background alone is a
297
304
  ~1.1:1 change against the menu surface in every preset (1.00:1 in glass
@@ -299,12 +306,13 @@ $effect(() => {
299
306
  sits inside the item's padding box rather than overlapping the neighbouring
300
307
  item or the menu's edge (#2322). */
301
308
  .dropdown__item:focus-visible {
302
- background: var(--smrt-color-surface-container-high);
303
- outline: 2px solid var(--smrt-color-primary);
304
- outline-offset: -2px;
309
+ background: color-mix(in srgb, currentColor calc(var(--smrt-state-hover) * 100%), transparent);
310
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
311
+ outline-offset: calc(var(--smrt-focus-ring-width) * -1);
305
312
  }
306
313
  .dropdown__item:disabled {
307
- opacity: 0.5;
314
+ opacity: var(--smrt-state-disabled);
308
315
  cursor: not-allowed;
309
316
  }
317
+ }
310
318
  </style>
@@ -24,7 +24,7 @@ export interface Props
24
24
  pressed?: boolean;
25
25
  /** Optional count badge; included in the accessible name and tooltip. */
26
26
  count?: number;
27
- /** A CSS colour (a token such as `var(--status-draft)` or a value) shown when the toggle is pressed. */
27
+ /** A CSS colour (a token such as `var(--smrt-color-primary)` or a value) shown when the toggle is pressed. */
28
28
  tone?: string;
29
29
  /** The icon. Decorative: it is hidden from assistive technology. */
30
30
  children: Snippet;
@@ -58,7 +58,7 @@ const name = $derived(count === undefined ? label : `${label} (${count})`);
58
58
  class="icon-toggle {className}"
59
59
  class:pressed
60
60
  class:toned={tone !== undefined}
61
- style:--icon-toggle-tone={tone}
61
+ style:--smrt-icon-toggle-tone={tone}
62
62
  aria-pressed={pressed}
63
63
  aria-label={name}
64
64
  >
@@ -68,6 +68,8 @@ const name = $derived(count === undefined ? label : `${label} (${count})`);
68
68
  </Tooltip>
69
69
 
70
70
  <style>
71
+ @layer smrt.reset, smrt.structure, smrt.theme;
72
+ @layer smrt.theme {
71
73
  .icon-toggle {
72
74
  position: relative;
73
75
  display: inline-flex;
@@ -78,7 +80,7 @@ const name = $derived(count === undefined ? label : `${label} (${count})`);
78
80
  min-height: 2.75rem;
79
81
  padding: 0;
80
82
  border: 1px solid var(--smrt-color-outline-variant, var(--smrt-color-outline));
81
- border-radius: var(--smrt-radius-full, 999px);
83
+ border-radius: var(--smrt-radius-full);
82
84
  background: var(--smrt-color-surface-container, var(--smrt-color-surface));
83
85
  color: var(--smrt-color-on-surface);
84
86
  font: inherit;
@@ -100,13 +102,13 @@ const name = $derived(count === undefined ? label : `${label} (${count})`);
100
102
  color: var(--smrt-color-on-surface-variant, var(--smrt-color-on-surface));
101
103
  }
102
104
  .icon-toggle.toned.pressed {
103
- background: color-mix(in srgb, var(--icon-toggle-tone) 16%, var(--smrt-color-surface));
104
- color: var(--icon-toggle-tone);
105
- box-shadow: inset 0 0 0 2px var(--icon-toggle-tone);
105
+ background: color-mix(in srgb, var(--smrt-icon-toggle-tone) 16%, var(--smrt-color-surface));
106
+ color: var(--smrt-icon-toggle-tone);
107
+ box-shadow: inset 0 0 0 2px var(--smrt-icon-toggle-tone);
106
108
  }
107
109
  .icon-toggle:focus-visible {
108
- outline: 2px solid var(--smrt-color-primary);
109
- outline-offset: 2px;
110
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
111
+ outline-offset: var(--smrt-focus-ring-offset);
110
112
  }
111
113
  .icon-toggle:disabled {
112
114
  opacity: 0.5;
@@ -123,7 +125,7 @@ const name = $derived(count === undefined ? label : `${label} (${count})`);
123
125
  box-sizing: border-box;
124
126
  min-width: 1.15rem;
125
127
  padding: 0 0.3rem;
126
- border-radius: var(--smrt-radius-full, 999px);
128
+ border-radius: var(--smrt-radius-full);
127
129
  /* Outlined, not filled: a surface-coloured face (so the button's edge does
128
130
  not show through where the badge overlaps it), a 1px theme outline and
129
131
  neutral text, readable on light and dark themes alike. */
@@ -136,4 +138,5 @@ const name = $derived(count === undefined ? label : `${label} (${count})`);
136
138
  text-align: center;
137
139
  font-variant-numeric: tabular-nums;
138
140
  }
141
+ }
139
142
  </style>
@@ -17,7 +17,7 @@ export interface Props extends Omit<HTMLButtonAttributes, 'class' | 'children' |
17
17
  pressed?: boolean;
18
18
  /** Optional count badge; included in the accessible name and tooltip. */
19
19
  count?: number;
20
- /** A CSS colour (a token such as `var(--status-draft)` or a value) shown when the toggle is pressed. */
20
+ /** A CSS colour (a token such as `var(--smrt-color-primary)` or a value) shown when the toggle is pressed. */
21
21
  tone?: string;
22
22
  /** The icon. Decorative: it is hidden from assistive technology. */
23
23
  children: Snippet;
@@ -1 +1 @@
1
- {"version":3,"file":"IconToggle.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/ui/IconToggle.svelte.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAI5D,MAAM,WAAW,KACf,SAAQ,IAAI,CACV,oBAAoB,EACpB,OAAO,GAAG,UAAU,GAAG,YAAY,GAAG,cAAc,GAAG,OAAO,CAC/D;IACD,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wGAAwG;IACxG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAC;IAClB,+BAA+B;IAC/B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoCD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"IconToggle.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/ui/IconToggle.svelte.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAI5D,MAAM,WAAW,KACf,SAAQ,IAAI,CACV,oBAAoB,EACpB,OAAO,GAAG,UAAU,GAAG,YAAY,GAAG,cAAc,GAAG,OAAO,CAC/D;IACD,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8GAA8G;IAC9G,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAC;IAClB,+BAA+B;IAC/B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoCD,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -21,11 +21,13 @@ export interface Props {
21
21
  const { label, children, class: className = '' }: Props = $props();
22
22
  </script>
23
23
 
24
- <div class="icon-toggle-group {className}" role="group" aria-label={label}>
24
+ <div class="icon-toggle-group {className}" role="group" aria-label={label} data-smrt-component="IconToggleGroup">
25
25
  {@render children()}
26
26
  </div>
27
27
 
28
28
  <style>
29
+ @layer smrt.reset, smrt.structure, smrt.theme;
30
+ @layer smrt.theme {
29
31
  .icon-toggle-group {
30
32
  display: inline-flex;
31
33
  flex-wrap: wrap;
@@ -41,16 +43,17 @@ const { label, children, class: className = '' }: Props = $props();
41
43
  margin-inline-start: -1px;
42
44
  }
43
45
  .icon-toggle-group > :global(:first-child .icon-toggle) {
44
- border-start-start-radius: var(--smrt-radius-full, 999px);
45
- border-end-start-radius: var(--smrt-radius-full, 999px);
46
+ border-start-start-radius: var(--smrt-radius-full);
47
+ border-end-start-radius: var(--smrt-radius-full);
46
48
  }
47
49
  .icon-toggle-group > :global(:last-child .icon-toggle) {
48
- border-start-end-radius: var(--smrt-radius-full, 999px);
49
- border-end-end-radius: var(--smrt-radius-full, 999px);
50
+ border-start-end-radius: var(--smrt-radius-full);
51
+ border-end-end-radius: var(--smrt-radius-full);
50
52
  }
51
53
  /* The pressed one sits above its neighbours so its tint is not clipped by them. */
52
54
  .icon-toggle-group :global(.icon-toggle.pressed),
53
55
  .icon-toggle-group :global(.icon-toggle:focus-visible) {
54
56
  z-index: 1;
55
57
  }
58
+ }
56
59
  </style>