@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
@@ -125,24 +125,24 @@ const placementClass = $derived(`modal--${placement}`);
125
125
  onkeydown={handleKeydown}
126
126
  oncancel={handleCancel}
127
127
  aria-label={ariaLabel ?? title}
128
- aria-describedby={ariaDescribedBy}
128
+ aria-describedby={ariaDescribedBy} data-smrt-component="Modal"
129
129
  >
130
130
  <div class="modal__container">
131
131
  {#if header}
132
- <header class="modal__header modal__header--custom">
132
+ <header class="modal__header modal__header--custom" data-smrt-part="header">
133
133
  {@render header()}
134
134
  </header>
135
135
  {:else if title || showClose}
136
- <header class="modal__header">
136
+ <header class="modal__header" data-smrt-part="header">
137
137
  {#if title}
138
- <h2 class="modal__title">{title}</h2>
138
+ <h2 class="modal__title" data-smrt-part="title">{title}</h2>
139
139
  {:else}
140
140
  <span></span>
141
141
  {/if}
142
142
  {#if showClose}
143
143
  <button
144
144
  type="button"
145
- class="modal__close"
145
+ class="modal__close" data-smrt-part="close"
146
146
  onclick={handleClose}
147
147
  aria-label={t(M['ui.modal.close'])}
148
148
  >
@@ -166,14 +166,14 @@ const placementClass = $derived(`modal--${placement}`);
166
166
  </header>
167
167
  {/if}
168
168
 
169
- <div class="modal__body">
169
+ <div class="modal__body" data-smrt-part="body">
170
170
  {#if children}
171
171
  {@render children()}
172
172
  {/if}
173
173
  </div>
174
174
 
175
175
  {#if footer}
176
- <footer class="modal__footer">
176
+ <footer class="modal__footer" data-smrt-part="footer">
177
177
  {@render footer()}
178
178
  </footer>
179
179
  {/if}
@@ -181,6 +181,8 @@ const placementClass = $derived(`modal--${placement}`);
181
181
  </dialog>
182
182
 
183
183
  <style>
184
+ @layer smrt.reset, smrt.structure, smrt.theme;
185
+ @layer smrt.theme {
184
186
  .modal {
185
187
  position: fixed;
186
188
  inset: 0;
@@ -199,8 +201,8 @@ const placementClass = $derived(`modal--${placement}`);
199
201
  }
200
202
 
201
203
  .modal::backdrop {
202
- background: var(--smrt-color-scrim, rgba(0, 0, 0, 0.5));
203
- backdrop-filter: blur(2px);
204
+ background: var(--smrt-surface-overlay);
205
+ backdrop-filter: blur(var(--smrt-glass-blur));
204
206
  }
205
207
 
206
208
  .modal:not([open]) {
@@ -212,9 +214,11 @@ const placementClass = $derived(`modal--${placement}`);
212
214
  flex-direction: column;
213
215
  max-height: calc(100vh - var(--smrt-spacing-8, 2rem));
214
216
  max-width: calc(100vw - var(--smrt-spacing-8, 2rem));
215
- background: var(--smrt-color-surface, #ffffff);
217
+ background: var(--smrt-glass-surface-strong);
218
+ backdrop-filter: blur(var(--smrt-glass-blur)) saturate(var(--smrt-glass-saturation));
219
+ border: var(--smrt-hairline);
216
220
  border-radius: var(--smrt-radius-large, 0.75rem);
217
- box-shadow: var(--smrt-elevation-3, 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05));
221
+ box-shadow: var(--smrt-inset-highlight), var(--smrt-elevation-4);
218
222
  overflow: hidden;
219
223
  animation: modal-enter var(--smrt-duration-medium2, 300ms) var(--smrt-easing-emphasized, cubic-bezier(0.2, 0, 0, 1));
220
224
  }
@@ -310,7 +314,7 @@ const placementClass = $derived(`modal--${placement}`);
310
314
  justify-content: space-between;
311
315
  gap: var(--smrt-spacing-3, 0.75rem);
312
316
  padding: var(--smrt-spacing-4, 1rem) var(--smrt-spacing-5, 1.25rem);
313
- border-bottom: 1px solid var(--smrt-color-outline-variant, #c4c6cf);
317
+ border-bottom: var(--smrt-hairline);
314
318
  flex-shrink: 0;
315
319
  }
316
320
 
@@ -323,7 +327,7 @@ const placementClass = $derived(`modal--${placement}`);
323
327
  margin: 0;
324
328
  font-size: var(--smrt-typography-headline-small-size, 1.125rem);
325
329
  font-weight: var(--smrt-typography-weight-semibold, 600);
326
- color: var(--smrt-color-on-surface, #1b1b1f);
330
+ color: var(--smrt-color-on-surface);
327
331
  line-height: var(--smrt-typography-headline-small-line-height, 1.4);
328
332
  }
329
333
 
@@ -336,21 +340,21 @@ const placementClass = $derived(`modal--${placement}`);
336
340
  padding: 0;
337
341
  border: none;
338
342
  background: transparent;
339
- border-radius: var(--smrt-radius-full, 9999px);
340
- color: var(--smrt-color-on-surface-variant, #43474e);
343
+ border-radius: var(--smrt-radius-full);
344
+ color: var(--smrt-color-on-surface-variant);
341
345
  cursor: pointer;
342
346
  transition: all var(--smrt-duration-short3, 150ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1));
343
347
  flex-shrink: 0;
344
348
  }
345
349
 
346
350
  .modal__close:hover {
347
- background: var(--smrt-color-surface-container-highest, #e0e2ec);
348
- color: var(--smrt-color-on-surface, #1b1b1f);
351
+ background: color-mix(in srgb, currentColor calc(var(--smrt-state-hover) * 100%), transparent);
352
+ color: var(--smrt-color-on-surface);
349
353
  }
350
354
 
351
355
  .modal__close:focus-visible {
352
- outline: 2px solid var(--smrt-color-primary, #005ac1);
353
- outline-offset: 2px;
356
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
357
+ outline-offset: var(--smrt-focus-ring-offset);
354
358
  }
355
359
 
356
360
  /* Body */
@@ -358,7 +362,7 @@ const placementClass = $derived(`modal--${placement}`);
358
362
  flex: 1;
359
363
  padding: var(--smrt-spacing-5, 1.25rem);
360
364
  overflow-y: auto;
361
- color: var(--smrt-color-on-surface, #1b1b1f);
365
+ color: var(--smrt-color-on-surface);
362
366
  }
363
367
 
364
368
  /* Footer */
@@ -368,7 +372,7 @@ const placementClass = $derived(`modal--${placement}`);
368
372
  justify-content: flex-end;
369
373
  gap: var(--smrt-spacing-3, 0.75rem);
370
374
  padding: var(--smrt-spacing-4, 1rem) var(--smrt-spacing-5, 1.25rem);
371
- border-top: 1px solid var(--smrt-color-outline-variant, #c4c6cf);
375
+ border-top: var(--smrt-hairline);
372
376
  flex-shrink: 0;
373
377
  }
374
378
 
@@ -394,4 +398,5 @@ const placementClass = $derived(`modal--${placement}`);
394
398
  max-height: 100%;
395
399
  }
396
400
  }
401
+ }
397
402
  </style>
@@ -89,7 +89,7 @@ function onkeydown(event: KeyboardEvent): void {
89
89
  tabindex="-1"
90
90
  data-escape-scope
91
91
  data-testid="phone-sheet"
92
- {onkeydown}
92
+ {onkeydown} data-smrt-component="PhoneSheet"
93
93
  >
94
94
  <header
95
95
  class="phone-sheet__header"
@@ -129,6 +129,8 @@ function onkeydown(event: KeyboardEvent): void {
129
129
  </div>
130
130
 
131
131
  <style>
132
+ @layer smrt.reset, smrt.structure, smrt.theme;
133
+ @layer smrt.theme {
132
134
  .phone-sheet {
133
135
  position: absolute;
134
136
  inset: 0;
@@ -205,8 +207,8 @@ function onkeydown(event: KeyboardEvent): void {
205
207
  }
206
208
 
207
209
  .phone-sheet__close:focus-visible {
208
- outline: 2px solid var(--smrt-color-primary);
209
- outline-offset: -2px;
210
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
211
+ outline-offset: calc(var(--smrt-focus-ring-width) * -1);
210
212
  }
211
213
 
212
214
  .phone-sheet__body {
@@ -221,4 +223,5 @@ function onkeydown(event: KeyboardEvent): void {
221
223
  transition: none;
222
224
  }
223
225
  }
226
+ }
224
227
  </style>
@@ -39,18 +39,20 @@ const displayValue = $derived(
39
39
  </script>
40
40
  {#if variant === 'circular'}
41
41
  <div class="progress progress--circular progress--{size} {className}" class:indeterminate role="progressbar" aria-label={label}
42
- aria-valuemin={indeterminate ? undefined : min} aria-valuemax={indeterminate ? undefined : max} aria-valuenow={value}>
43
- <svg viewBox="0 0 44 44" aria-hidden="true"><circle class="track" cx="22" cy="22" r="18"></circle><circle class="indicator" cx="22" cy="22" r="18" style={`--progress: ${normalized}`}></circle></svg>
42
+ aria-valuemin={indeterminate ? undefined : min} aria-valuemax={indeterminate ? undefined : max} aria-valuenow={value} data-smrt-component="Progress">
43
+ <svg viewBox="0 0 44 44" aria-hidden="true"><circle class="track" cx="22" cy="22" r="18"></circle><circle class="indicator" cx="22" cy="22" r="18" style={`--smrt-progress-value: ${normalized}`}></circle></svg>
44
44
  {#if showValue && !indeterminate}<span>{displayValue}</span>{/if}
45
45
  </div>
46
46
  {:else}
47
47
  <div class="progress progress--linear progress--{size} {className}" class:indeterminate role="progressbar" aria-label={label}
48
- aria-valuemin={indeterminate ? undefined : min} aria-valuemax={indeterminate ? undefined : max} aria-valuenow={value}>
48
+ aria-valuemin={indeterminate ? undefined : min} aria-valuemax={indeterminate ? undefined : max} aria-valuenow={value} data-smrt-component="Progress">
49
49
  <span class="track"><span class="indicator" style:width={indeterminate ? undefined : `${normalized}%`}></span></span>
50
50
  {#if showValue && !indeterminate}<span class="value">{displayValue}</span>{/if}
51
51
  </div>
52
52
  {/if}
53
53
  <style>
54
+ @layer smrt.reset, smrt.structure, smrt.theme;
55
+ @layer smrt.theme {
54
56
  .progress--linear { display: flex; align-items: center; gap: var(--smrt-spacing-2); width: 100%; }
55
57
  .progress--linear .track { position: relative; display: block; flex: 1; height: 4px; overflow: hidden; border-radius: var(--smrt-radius-full); background: var(--smrt-color-surface-container-highest); }
56
58
  .progress--linear.progress--sm .track { height: 2px; } .progress--linear.progress--lg .track { height: 8px; }
@@ -61,9 +63,10 @@ const displayValue = $derived(
61
63
  .progress--circular.progress--sm { width: 1.75rem; height: 1.75rem; } .progress--circular.progress--lg { width: 4rem; height: 4rem; }
62
64
  .progress--circular svg { width: 100%; height: 100%; transform: rotate(-90deg); }
63
65
  .progress--circular circle { fill: none; stroke-width: 4; } .progress--circular .track { stroke: var(--smrt-color-surface-container-highest); }
64
- .progress--circular .indicator { stroke: var(--smrt-color-primary); stroke-linecap: round; stroke-dasharray: 113.1; stroke-dashoffset: calc(113.1 - (113.1 * var(--progress)) / 100); transition: stroke-dashoffset var(--smrt-duration-short4); }
66
+ .progress--circular .indicator { stroke: var(--smrt-color-primary); stroke-linecap: round; stroke-dasharray: 113.1; stroke-dashoffset: calc(113.1 - (113.1 * var(--smrt-progress-value)) / 100); transition: stroke-dashoffset var(--smrt-duration-short4); }
65
67
  .progress--circular.indeterminate svg { animation: spin 1s linear infinite; } .progress--circular.indeterminate .indicator { stroke-dasharray: 70 113.1; }
66
68
  .progress--circular > span { position: absolute; font: var(--smrt-typography-label-small-font); }
67
69
  @keyframes linear-progress { from { left: -40%; } to { left: 105%; } } @keyframes spin { to { transform: rotate(270deg); } }
68
- @media (prefers-reduced-motion: reduce) { .indicator { transition: none !important; animation-duration: 2.5s !important; } }
70
+ @media (prefers-reduced-motion: reduce) { .progress--linear .indicator, .progress--circular .indicator { transition: none; } .progress--linear.indeterminate .indicator { animation-duration: 2.5s; } }
71
+ }
69
72
  </style>
@@ -76,7 +76,7 @@ const displayLabel = $derived.by(() => {
76
76
  });
77
77
  </script>
78
78
 
79
- <div class="progress-container" class:sm={size === 'sm'} class:lg={size === 'lg'}>
79
+ <div class="progress-container" class:sm={size === 'sm'} class:lg={size === 'lg'} data-smrt-component="ProgressBar">
80
80
  {#if showLabel || showValue}
81
81
  <div class="progress-header">
82
82
  <span class="progress-label">{displayLabel}</span>
@@ -102,6 +102,8 @@ const displayLabel = $derived.by(() => {
102
102
  </div>
103
103
 
104
104
  <style>
105
+ @layer smrt.reset, smrt.structure, smrt.theme;
106
+ @layer smrt.theme {
105
107
  .progress-container {
106
108
  width: 100%;
107
109
  }
@@ -110,12 +112,12 @@ const displayLabel = $derived.by(() => {
110
112
  display: flex;
111
113
  justify-content: space-between;
112
114
  align-items: center;
113
- margin-bottom: var(--smrt-spacing-2, 8px);
115
+ margin-bottom: var(--smrt-spacing-2);
114
116
  }
115
117
 
116
118
  .progress-label {
117
119
  font: var(--smrt-typography-label-large-font);
118
- color: var(--smrt-color-on-surface-variant, #43474e);
120
+ color: var(--smrt-color-on-surface-variant);
119
121
  }
120
122
 
121
123
  .sm .progress-label {
@@ -125,14 +127,14 @@ const displayLabel = $derived.by(() => {
125
127
  .over-badge {
126
128
  font: var(--smrt-typography-label-small-font);
127
129
  font-weight: var(--smrt-typography-weight-semibold, 600);
128
- color: var(--smrt-color-error, #ba1a1a);
130
+ color: var(--smrt-color-error);
129
131
  }
130
132
 
131
133
  .progress-track {
132
134
  width: 100%;
133
135
  height: 4px;
134
- background-color: var(--smrt-color-surface-container-highest, #e0e2ec);
135
- border-radius: var(--smrt-radius-sm, 4px);
136
+ background-color: var(--smrt-color-surface-container-highest);
137
+ border-radius: var(--smrt-radius-sm);
136
138
  overflow: hidden;
137
139
  }
138
140
 
@@ -142,7 +144,7 @@ const displayLabel = $derived.by(() => {
142
144
 
143
145
  .lg .progress-track {
144
146
  height: 8px;
145
- border-radius: var(--smrt-radius-sm, 4px);
147
+ border-radius: var(--smrt-radius-sm);
146
148
  }
147
149
 
148
150
  .progress-bar {
@@ -156,7 +158,8 @@ const displayLabel = $derived.by(() => {
156
158
  }
157
159
  }
158
160
 
159
- .color-primary { background-color: var(--smrt-color-primary, #005ac1); }
160
- .color-tertiary { background-color: var(--smrt-color-tertiary, #6b5778); }
161
- .color-error { background-color: var(--smrt-color-error, #ba1a1a); }
161
+ .color-primary { background-color: var(--smrt-color-primary); }
162
+ .color-tertiary { background-color: var(--smrt-color-tertiary); }
163
+ .color-error { background-color: var(--smrt-color-error); }
164
+ }
162
165
  </style>
@@ -9,12 +9,15 @@ export interface Props {
9
9
  }
10
10
  let { label = 'Loading', size = 'md', class: className = '' }: Props = $props();
11
11
  </script>
12
- <span class="spinner spinner--{size} {className}" role="status"><span class="spinner__ring" aria-hidden="true"></span><span class="visually-hidden">{label}</span></span>
12
+ <span class="spinner spinner--{size} {className}" role="status" data-smrt-component="Spinner"><span class="spinner__ring" aria-hidden="true"></span><span class="visually-hidden">{label}</span></span>
13
13
  <style>
14
+ @layer smrt.reset, smrt.structure, smrt.theme;
15
+ @layer smrt.theme {
14
16
  .spinner { display: inline-flex; }
15
17
  .spinner__ring { width: 1.5rem; height: 1.5rem; border: 2px solid var(--smrt-color-surface-container-highest); border-top-color: var(--smrt-color-primary); border-radius: 50%; animation: spin .75s linear infinite; }
16
18
  .spinner--sm .spinner__ring { width: 1rem; height: 1rem; } .spinner--lg .spinner__ring { width: 2.5rem; height: 2.5rem; border-width: 3px; }
17
19
  .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
18
20
  @keyframes spin { to { transform: rotate(360deg); } }
19
21
  @media (prefers-reduced-motion: reduce) { .spinner__ring { animation-duration: 1.5s; } }
22
+ }
20
23
  </style>
@@ -161,12 +161,12 @@ async function runAction(toast: Toast) {
161
161
  </script>
162
162
 
163
163
  <section use:followModal class="viewport viewport--{position} {className}" aria-label="Notifications" aria-live="polite"
164
- style:--toast-inset={inset}
165
- style:--toast-top={bounds ? `${bounds.top}px` : undefined}
166
- style:--toast-right={bounds ? `${bounds.right}px` : undefined}
167
- style:--toast-bottom={bounds ? `${bounds.bottom}px` : undefined}
168
- style:--toast-left={bounds ? `${bounds.left}px` : undefined}
169
- style:--toast-region-width={bounds ? `${bounds.width}px` : undefined}
164
+ style:--smrt-toast-viewport-inset={inset}
165
+ style:--smrt-toast-viewport-top={bounds ? `${bounds.top}px` : undefined}
166
+ style:--smrt-toast-viewport-right={bounds ? `${bounds.right}px` : undefined}
167
+ style:--smrt-toast-viewport-bottom={bounds ? `${bounds.bottom}px` : undefined}
168
+ style:--smrt-toast-viewport-left={bounds ? `${bounds.left}px` : undefined}
169
+ style:--smrt-toast-viewport-region-width={bounds ? `${bounds.width}px` : undefined} data-smrt-component="ToastViewport"
170
170
  >
171
171
  {#each toasts as toast (toast.id)}
172
172
  <div role="group" aria-label={toast.title ?? 'Notification'}
@@ -186,14 +186,17 @@ async function runAction(toast: Toast) {
186
186
  </section>
187
187
 
188
188
  <style>
189
- .viewport { position: fixed; z-index: var(--smrt-z-index-toast, 1200); display: grid; width: min(24rem, calc(var(--toast-region-width, 100vw) - 2 * var(--toast-inset))); gap: var(--smrt-spacing-2); pointer-events: none; }
190
- .viewport :global(.alert) { pointer-events: auto; box-shadow: var(--smrt-elevation-3); }
191
- .viewport--top-start { top: calc(var(--toast-top, 0px) + var(--toast-inset)); left: calc(var(--toast-left, 0px) + var(--toast-inset)); }
192
- .viewport--top-end { top: calc(var(--toast-top, 0px) + var(--toast-inset)); right: calc(var(--toast-right, 0px) + var(--toast-inset)); }
193
- .viewport--bottom-start { bottom: calc(var(--toast-bottom, 0px) + var(--toast-inset)); left: calc(var(--toast-left, 0px) + var(--toast-inset)); }
194
- .viewport--bottom-end { right: calc(var(--toast-right, 0px) + var(--toast-inset)); bottom: calc(var(--toast-bottom, 0px) + var(--toast-inset)); }
195
- .viewport--top-center, .viewport--bottom-center { left: calc(var(--toast-left, 0px) + var(--toast-region-width, 100vw) / 2); transform: translateX(-50%); }
196
- .viewport--top-center { top: calc(var(--toast-top, 0px) + var(--toast-inset)); }
197
- .viewport--bottom-center { bottom: calc(var(--toast-bottom, 0px) + var(--toast-inset)); }
189
+ @layer smrt.reset, smrt.structure, smrt.theme;
190
+ @layer smrt.theme {
191
+ .viewport { position: fixed; z-index: var(--smrt-z-index-toast, 1200); display: grid; width: min(24rem, calc(var(--smrt-toast-viewport-region-width, 100vw) - 2 * var(--smrt-toast-viewport-inset))); gap: var(--smrt-spacing-2); pointer-events: none; }
192
+ .viewport :global(.alert) { pointer-events: auto; background: color-mix(in srgb, var(--smrt-alert-color) 8%, var(--smrt-glass-surface-strong)); backdrop-filter: blur(var(--smrt-glass-blur)) saturate(var(--smrt-glass-saturation)); box-shadow: var(--smrt-inset-highlight), var(--smrt-elevation-3); }
193
+ .viewport--top-start { top: calc(var(--smrt-toast-viewport-top, 0px) + var(--smrt-toast-viewport-inset)); left: calc(var(--smrt-toast-viewport-left, 0px) + var(--smrt-toast-viewport-inset)); }
194
+ .viewport--top-end { top: calc(var(--smrt-toast-viewport-top, 0px) + var(--smrt-toast-viewport-inset)); right: calc(var(--smrt-toast-viewport-right, 0px) + var(--smrt-toast-viewport-inset)); }
195
+ .viewport--bottom-start { bottom: calc(var(--smrt-toast-viewport-bottom, 0px) + var(--smrt-toast-viewport-inset)); left: calc(var(--smrt-toast-viewport-left, 0px) + var(--smrt-toast-viewport-inset)); }
196
+ .viewport--bottom-end { right: calc(var(--smrt-toast-viewport-right, 0px) + var(--smrt-toast-viewport-inset)); bottom: calc(var(--smrt-toast-viewport-bottom, 0px) + var(--smrt-toast-viewport-inset)); }
197
+ .viewport--top-center, .viewport--bottom-center { left: calc(var(--smrt-toast-viewport-left, 0px) + var(--smrt-toast-viewport-region-width, 100vw) / 2); transform: translateX(-50%); }
198
+ .viewport--top-center { top: calc(var(--smrt-toast-viewport-top, 0px) + var(--smrt-toast-viewport-inset)); }
199
+ .viewport--bottom-center { bottom: calc(var(--smrt-toast-viewport-bottom, 0px) + var(--smrt-toast-viewport-inset)); }
198
200
  button { padding: var(--smrt-spacing-1) var(--smrt-spacing-2); border: 1px solid currentColor; border-radius: var(--smrt-radius-small); background: transparent; color: inherit; font: inherit; cursor: pointer; }
201
+ }
199
202
  </style>
@@ -144,10 +144,10 @@ function handleKeydown(event: KeyboardEvent) {
144
144
  }
145
145
  </script>
146
146
 
147
- <div class="working-strip__live" aria-live="polite" aria-atomic="true">
147
+ <div class="working-strip__live" aria-live="polite" aria-atomic="true" data-smrt-component="WorkingStrip">
148
148
  {politeText}
149
149
  </div>
150
- <div class="working-strip__live" aria-live="assertive" aria-atomic="true">
150
+ <div class="working-strip__live" aria-live="assertive" aria-atomic="true" data-smrt-component="WorkingStrip">
151
151
  {visible && assertive ? accessibleSummary : ''}
152
152
  </div>
153
153
 
@@ -157,7 +157,7 @@ function handleKeydown(event: KeyboardEvent) {
157
157
  class="working-strip working-strip--{variant}"
158
158
  data-phase={phase}
159
159
  data-testid="working-strip"
160
- onkeydown={handleKeydown}
160
+ onkeydown={handleKeydown} data-smrt-component="WorkingStrip"
161
161
  >
162
162
  {#snippet content()}
163
163
  <span class="working-strip__icon" aria-hidden="true">
@@ -243,6 +243,8 @@ function handleKeydown(event: KeyboardEvent) {
243
243
  {/if}
244
244
 
245
245
  <style>
246
+ @layer smrt.reset, smrt.structure, smrt.theme;
247
+ @layer smrt.theme {
246
248
  .working-strip__live,
247
249
  .working-strip__sr {
248
250
  position: absolute;
@@ -423,4 +425,5 @@ function handleKeydown(event: KeyboardEvent) {
423
425
  border-inline-start-color: currentColor;
424
426
  }
425
427
  }
428
+ }
426
429
  </style>
@@ -451,7 +451,7 @@ useControlRegistration(() => {
451
451
  data-smrt-control={controlId}
452
452
  data-smrt-form={interactionContext?.formId}
453
453
  data-smrt-subject-type={interaction === false ? undefined : interaction?.subject?.type}
454
- data-smrt-subject-id={interaction === false ? undefined : interaction?.subject?.id}
454
+ data-smrt-subject-id={interaction === false ? undefined : interaction?.subject?.id} data-smrt-component="CameraCapture"
455
455
  >
456
456
  <div class="stage" class:empty={cameraState !== 'streaming' && !previewUrl}>
457
457
  <video
@@ -524,6 +524,8 @@ useControlRegistration(() => {
524
524
  </div>
525
525
 
526
526
  <style>
527
+ @layer smrt.reset, smrt.structure, smrt.theme;
528
+ @layer smrt.theme {
527
529
  .camera-capture {
528
530
  display: grid;
529
531
  gap: var(--smrt-spacing-3);
@@ -578,8 +580,8 @@ useControlRegistration(() => {
578
580
  justify-content: center;
579
581
  /* Capture is a touch flow: its actions are touch targets at every
580
582
  density, on the shared scale (smrt#3252). */
581
- min-width: var(--smrt-touch-target-min, 48px);
582
- min-height: var(--smrt-touch-target-min, 48px);
583
+ min-width: var(--smrt-touch-target-min);
584
+ min-height: var(--smrt-touch-target-min);
583
585
  padding: 0 var(--smrt-spacing-4);
584
586
  border: 1px solid var(--smrt-color-outline);
585
587
  border-radius: var(--smrt-radius-small);
@@ -596,8 +598,8 @@ useControlRegistration(() => {
596
598
  }
597
599
  .action:focus-visible,
598
600
  .picker:has(input:focus-visible) {
599
- outline: 2px solid var(--smrt-color-primary);
600
- outline-offset: 2px;
601
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
602
+ outline-offset: var(--smrt-focus-ring-offset);
601
603
  }
602
604
  .action:disabled,
603
605
  .picker.disabled {
@@ -616,4 +618,5 @@ useControlRegistration(() => {
616
618
  outline: 3px solid var(--smrt-color-tertiary);
617
619
  outline-offset: 4px;
618
620
  }
621
+ }
619
622
  </style>
@@ -130,7 +130,7 @@ useControlRegistration(() => {
130
130
  data-smrt-control={controlId}
131
131
  data-smrt-form={interactionContext?.formId}
132
132
  data-smrt-subject-type={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.type}
133
- data-smrt-subject-id={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.id}
133
+ data-smrt-subject-id={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.id} data-smrt-component="Checkbox"
134
134
  >
135
135
  <input
136
136
  bind:this={inputEl}
@@ -154,6 +154,8 @@ useControlRegistration(() => {
154
154
  </label>
155
155
 
156
156
  <style>
157
+ @layer smrt.reset, smrt.structure, smrt.theme;
158
+ @layer smrt.theme {
157
159
  /* The visible box stays 18px. A transparent 44x44 hit area (WCAG 2.5.8 / M3 touch target) is
158
160
  centred on it with ::before, so clicks nearby still land on the wrapping <label>. It adds no
159
161
  layout. The box and the label text sit above every hit area (z-index 1 over 0), so one
@@ -162,22 +164,22 @@ useControlRegistration(() => {
162
164
  checkbox stands alone in its parent. Beside anything else (a row title, a link, other text)
163
165
  it keeps to the box's own column and grows vertically only. Resize it with
164
166
  --smrt-control-hit-size (0px turns it off). */
165
- .checkbox { --_box: 1.125rem; --_hit: max(var(--smrt-control-hit-size, 2.75rem), var(--_box)); position: relative; display: inline-flex; align-items: center; gap: var(--smrt-spacing-2, .5rem); cursor: pointer; color: var(--smrt-color-on-surface); }
166
- .checkbox::before { content: ''; position: absolute; z-index: 0; inset-block-start: 50%; inset-inline-start: calc(var(--_box) / 2); inline-size: var(--_hit); block-size: var(--_hit); margin-block-start: calc(var(--_hit) / -2); margin-inline-start: calc(var(--_hit) / -2); }
167
+ .checkbox { --smrt-checkbox-box: 1.125rem; --smrt-checkbox-hit: max(var(--smrt-control-hit-size, 2.75rem), var(--smrt-checkbox-box)); position: relative; display: inline-flex; align-items: center; gap: var(--smrt-spacing-2, .5rem); cursor: pointer; color: var(--smrt-color-on-surface); }
168
+ .checkbox::before { content: ''; position: absolute; z-index: 0; inset-block-start: 50%; inset-inline-start: calc(var(--smrt-checkbox-box) / 2); inline-size: var(--smrt-checkbox-hit); block-size: var(--smrt-checkbox-hit); margin-block-start: calc(var(--smrt-checkbox-hit) / -2); margin-inline-start: calc(var(--smrt-checkbox-hit) / -2); }
167
169
  .checkbox--disabled { opacity: .5; cursor: not-allowed; }
168
170
  input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
169
- .checkbox__box { position: relative; z-index: 1; width: 1.125rem; height: 1.125rem; display: grid; place-items: center; border: 2px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-extra-small, 3px); background: var(--smrt-color-surface); transition: background var(--smrt-duration-short2), border-color var(--smrt-duration-short2); }
171
+ .checkbox__box { position: relative; z-index: 1; width: 1.125rem; height: 1.125rem; display: grid; place-items: center; border: 2px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-extra-small); background: var(--smrt-color-surface); transition: background var(--smrt-duration-short2), border-color var(--smrt-duration-short2); }
170
172
  .checkbox__label { position: relative; z-index: 1; }
171
173
  input:checked + .checkbox__box, input:indeterminate + .checkbox__box { background: var(--smrt-color-primary); border-color: var(--smrt-color-primary); color: var(--smrt-color-on-primary); }
172
- input:focus-visible + .checkbox__box { outline: 2px solid var(--smrt-color-primary); outline-offset: 3px; }
174
+ input:focus-visible + .checkbox__box { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
173
175
  .checkbox__check { font-size: .8rem; font-weight: 800; line-height: 1; }
174
176
  .checkbox__dash { width: .6rem; height: 2px; background: currentColor; }
175
- .checkbox:not(:only-child)::before { inline-size: var(--_box); margin-inline-start: calc(var(--_box) / -2); }
177
+ .checkbox:not(:only-child)::before { inline-size: var(--smrt-checkbox-box); margin-inline-start: calc(var(--smrt-checkbox-box) / -2); }
176
178
  .checkbox[data-smrt-highlighted='true'] { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; border-radius: var(--smrt-radius-small); }
177
179
 
178
180
  /* Table cells: neighbouring cells hold links and other controls, so the hit area never leaves
179
181
  the cell's column. It grows vertically only (box wide)... */
180
- :global(:is(td, th)) .checkbox::before { inline-size: var(--_box); margin-inline-start: calc(var(--_box) / -2); }
182
+ :global(:is(td, th)) .checkbox::before { inline-size: var(--smrt-checkbox-box); margin-inline-start: calc(var(--smrt-checkbox-box) / -2); }
181
183
  /* ...unless the checkbox is the cell's only content: then the whole cell, clipped to its own
182
184
  edges, is the hit area (the 44px target the header and every row share). The cell only needs
183
185
  a containing block, so :where() keeps that rule at zero specificity: a sticky header or a
@@ -188,7 +190,8 @@ useControlRegistration(() => {
188
190
 
189
191
  @media (prefers-reduced-motion: reduce) { .checkbox__box { transition: none; } }
190
192
 
191
- .checkbox[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min, 48px); }
193
+ .checkbox[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
192
194
  .checkbox[data-density='comfortable'] { --smrt-control-target-min: 0px; }
193
195
  .checkbox { min-height: var(--smrt-control-target-min, 0px); min-width: var(--smrt-control-target-min, 0px); }
196
+ }
194
197
  </style>
@@ -334,7 +334,7 @@ useControlRegistration(() => {
334
334
  </script>
335
335
  <div bind:this={rootEl} class="combobox {className}" data-smrt-control={controlId} data-smrt-form={interactionContext?.formId}
336
336
  data-smrt-subject-type={interaction === false ? undefined : interaction?.subject?.type}
337
- data-smrt-subject-id={interaction === false ? undefined : interaction?.subject?.id}>
337
+ data-smrt-subject-id={interaction === false ? undefined : interaction?.subject?.id} data-smrt-component="Combobox">
338
338
  <FieldLabel for={inputId} {label} {required} />
339
339
  <!-- The form posts the committed value (an option id), never the label the person sees. -->
340
340
  {#if name}<input type="hidden" {name} {value} {disabled} />{/if}
@@ -347,11 +347,14 @@ useControlRegistration(() => {
347
347
  {#if status !== undefined}<div class="sr-status" role="status" aria-live="polite">{open ? status : ''}</div>{/if}
348
348
  </div>
349
349
  <style>
350
+ @layer smrt.reset, smrt.structure, smrt.theme;
351
+ @layer smrt.theme {
350
352
  .combobox { position: relative; display: grid; gap: 0; color: var(--smrt-color-on-surface); }
351
353
  input { box-sizing: border-box; width: 100%; padding: var(--smrt-spacing-2) var(--smrt-spacing-3); border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); color: inherit; }
352
- input:focus { outline: 2px solid var(--smrt-color-primary); outline-offset: 1px; } .options { position: absolute; z-index: var(--smrt-z-index-dropdown); top: 100%; left: 0; right: 0; display: grid; padding: var(--smrt-spacing-1); border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface-container); box-shadow: var(--smrt-elevation-2); }
354
+ input:focus { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); } .options { position: absolute; z-index: var(--smrt-z-index-dropdown); top: 100%; left: 0; right: 0; display: grid; padding: var(--smrt-spacing-1); border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface-container); box-shadow: var(--smrt-elevation-2); }
353
355
  .options button { padding: var(--smrt-spacing-2) var(--smrt-spacing-3); border: 0; border-radius: var(--smrt-radius-extra-small); background: transparent; color: var(--smrt-color-on-surface); text-align: left; } .options button.active { background: var(--smrt-color-secondary-container); }
354
356
  .options.empty { padding: var(--smrt-spacing-2) var(--smrt-spacing-3); color: var(--smrt-color-on-surface-variant); }
355
357
  .sr-status { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
356
358
  :global(.combobox[data-smrt-highlighted='true']) { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
359
+ }
357
360
  </style>
@@ -25,5 +25,10 @@ let {
25
25
  const instanceId = $props.id();
26
26
  const resolvedId = $derived(id ?? `smrt-date-${instanceId}`);
27
27
  </script>
28
- <label class="picker {className}" for={resolvedId}><span>{label}</span><Input id={resolvedId} {name} type="date" aria-label={label} {interaction} bind:value {...rest} /></label>
29
- <style>.picker { display: grid; gap: var(--smrt-spacing-1); color: var(--smrt-color-on-surface); font: var(--smrt-typography-label-large-font); }</style>
28
+ <label class="picker {className}" for={resolvedId} data-smrt-component="DatePicker"><span>{label}</span><Input id={resolvedId} {name} type="date" aria-label={label} {interaction} bind:value {...rest} /></label>
29
+ <style>
30
+ @layer smrt.reset, smrt.structure, smrt.theme;
31
+ @layer smrt.theme {
32
+ .picker { display: grid; gap: var(--smrt-spacing-1); color: var(--smrt-color-on-surface); font: var(--smrt-typography-label-large-font); }
33
+ }
34
+ </style>
@@ -118,10 +118,12 @@ function handleClick() {
118
118
  {/if}
119
119
  </Button>
120
120
  {#if hint !== name}
121
- <span id={helpId} class="smrt-dictation-help">{hint}</span>
121
+ <span id={helpId} class="smrt-dictation-help" data-smrt-component="DictationButton">{hint}</span>
122
122
  {/if}
123
123
 
124
124
  <style>
125
+ @layer smrt.reset, smrt.structure, smrt.theme;
126
+ @layer smrt.theme {
125
127
  :global(.smrt-dictation-button) {
126
128
  position: relative;
127
129
  flex-shrink: 0;
@@ -132,13 +134,13 @@ function handleClick() {
132
134
  block-size: 44px;
133
135
  min-inline-size: 44px;
134
136
  padding: 0;
135
- border-radius: var(--smrt-radius-full, 9999px);
136
- color: var(--smrt-color-on-surface-variant, #43474e);
137
+ border-radius: var(--smrt-radius-full);
138
+ color: var(--smrt-color-on-surface-variant);
137
139
  }
138
140
 
139
141
  :global(.smrt-dictation-button--listening) {
140
- background: var(--smrt-color-error-container, #ffdad6);
141
- color: var(--smrt-color-on-error-container, #410002);
142
+ background: var(--smrt-color-error-container);
143
+ color: var(--smrt-color-on-error-container);
142
144
  }
143
145
 
144
146
  /* Read out with the button's name; never shown. */
@@ -158,22 +160,22 @@ function handleClick() {
158
160
  inline-size: 10px;
159
161
  block-size: 10px;
160
162
  border-radius: 50%;
161
- background: var(--smrt-color-error, #ba1a1a);
163
+ background: var(--smrt-color-error);
162
164
  animation: smrt-dictation-pulse 1.2s ease-in-out infinite;
163
165
  }
164
166
 
165
167
  /* Hands-free: a steady ring while listening ... */
166
168
  :global(.smrt-dictation-button--hands-free) {
167
- box-shadow: 0 0 0 2px var(--smrt-color-error, #ba1a1a);
169
+ box-shadow: 0 0 0 2px var(--smrt-color-error);
168
170
  }
169
171
 
170
172
  /* ... and a halo that swells with the voice while someone is speaking. */
171
173
  :global(.smrt-dictation-button--speaking) {
172
174
  box-shadow:
173
- 0 0 0 3px var(--smrt-color-error, #ba1a1a),
175
+ 0 0 0 3px var(--smrt-color-error),
174
176
  0 0 0
175
177
  calc(4px + 10px * var(--smrt-dictation-level, 0))
176
- color-mix(in srgb, var(--smrt-color-error, #ba1a1a) 28%, transparent);
178
+ color-mix(in srgb, var(--smrt-color-error) 28%, transparent);
177
179
  transition: box-shadow 80ms linear;
178
180
  }
179
181
 
@@ -181,7 +183,7 @@ function handleClick() {
181
183
  :global(.smrt-dictation-button--paused) {
182
184
  opacity: 0.6;
183
185
  box-shadow: none;
184
- outline: 2px dashed var(--smrt-color-outline, #73777f);
186
+ outline: 2px dashed var(--smrt-color-outline);
185
187
  outline-offset: -2px;
186
188
  }
187
189
 
@@ -198,9 +200,9 @@ function handleClick() {
198
200
  inline-size: 10px;
199
201
  block-size: 10px;
200
202
  border-radius: 50%;
201
- border: 2px solid var(--smrt-color-outline-variant, #c4c6cf);
202
- border-block-start-color: var(--smrt-color-primary, #2f5ea8);
203
- background: var(--smrt-color-surface, #fff);
203
+ border: 2px solid var(--smrt-color-outline-variant);
204
+ border-block-start-color: var(--smrt-color-primary);
205
+ background: var(--smrt-color-surface);
204
206
  animation: smrt-dictation-spin 0.9s linear infinite;
205
207
  }
206
208
 
@@ -229,7 +231,8 @@ function handleClick() {
229
231
  /* Still ringed, but the halo no longer animates with the voice. */
230
232
  :global(.smrt-dictation-button--speaking) {
231
233
  transition: none;
232
- box-shadow: 0 0 0 4px var(--smrt-color-error, #ba1a1a);
234
+ box-shadow: 0 0 0 4px var(--smrt-color-error);
233
235
  }
234
236
  }
237
+ }
235
238
  </style>