@happyvertical/smrt-ui 0.55.15 → 0.55.17

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 (138) hide show
  1. package/AGENTS.md +14 -0
  2. package/README.md +68 -0
  3. package/dist/components/calendar/Calendar.svelte +59 -56
  4. package/dist/components/calendar/CalendarView.svelte +79 -76
  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 +10 -7
  8. package/dist/components/chat/TypingIndicator.svelte +12 -9
  9. package/dist/components/data/CollectionList.svelte +10 -7
  10. package/dist/components/data/CollectionToolbar.svelte +5 -2
  11. package/dist/components/data/DataTable.svelte +51 -48
  12. package/dist/components/data/ListSortSelect.svelte +8 -5
  13. package/dist/components/data/SortableHeader.svelte +9 -6
  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 +9 -6
  20. package/dist/components/feedback/ConfirmDialog.svelte +13 -10
  21. package/dist/components/feedback/LoadingOverlay.svelte +35 -32
  22. package/dist/components/feedback/Meter.svelte +4 -1
  23. package/dist/components/feedback/Modal.svelte +20 -17
  24. package/dist/components/feedback/PhoneSheet.svelte +4 -1
  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 +17 -14
  29. package/dist/components/feedback/WorkingStrip.svelte +6 -3
  30. package/dist/components/forms/CameraCapture.svelte +6 -3
  31. package/dist/components/forms/Checkbox.svelte +10 -7
  32. package/dist/components/forms/Combobox.svelte +4 -1
  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 +4 -1
  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 +14 -11
  44. package/dist/components/forms/InputGroup.svelte +5 -2
  45. package/dist/components/forms/Listbox.svelte +4 -1
  46. package/dist/components/forms/MultiSelect.svelte +4 -1
  47. package/dist/components/forms/Radio.svelte +8 -5
  48. package/dist/components/forms/RadioGroup.svelte +5 -2
  49. package/dist/components/forms/RangeSlider.svelte +4 -1
  50. package/dist/components/forms/RelationInput.svelte +7 -4
  51. package/dist/components/forms/SearchInput.svelte +5 -2
  52. package/dist/components/forms/SegmentedControl.svelte +5 -2
  53. package/dist/components/forms/Select.svelte +12 -9
  54. package/dist/components/forms/SignaturePad.svelte +6 -3
  55. package/dist/components/forms/Slider.svelte +4 -1
  56. package/dist/components/forms/StagedControlReview.svelte +15 -12
  57. package/dist/components/forms/Switch.svelte +7 -4
  58. package/dist/components/forms/TagsInput.svelte +4 -1
  59. package/dist/components/forms/Textarea.svelte +13 -10
  60. package/dist/components/forms/TimePicker.svelte +7 -2
  61. package/dist/components/forms/Toggle.svelte +13 -10
  62. package/dist/components/forms/ToggleButton.svelte +4 -1
  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 +10 -7
  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 +28 -24
  77. package/dist/components/permissions/PermissionPicker.svelte +4 -1
  78. package/dist/components/roles/RoleBadge.svelte +8 -5
  79. package/dist/components/roles/RoleSelector.svelte +21 -18
  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 +4 -1
  83. package/dist/components/ui/Avatar.svelte +10 -7
  84. package/dist/components/ui/Badge.svelte +4 -1
  85. package/dist/components/ui/Button.svelte +11 -8
  86. package/dist/components/ui/Card.svelte +7 -4
  87. package/dist/components/ui/Chip.svelte +12 -9
  88. package/dist/components/ui/Disclosure.svelte +4 -1
  89. package/dist/components/ui/Dropdown.svelte +17 -14
  90. package/dist/components/ui/IconToggle.svelte +10 -7
  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 +5 -2
  96. package/dist/components/ui/Skeleton.svelte +10 -7
  97. package/dist/components/ui/Tooltip.svelte +12 -9
  98. package/dist/components/ui/Tree.svelte +10 -7
  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 +25 -0
  121. package/dist/themes/README.md +25 -0
  122. package/dist/themes/ThemeProvider.svelte +11 -2
  123. package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -1
  124. package/dist/themes/components/ColorSchemeToggle.svelte +17 -14
  125. package/dist/themes/components/ThemeSwitcher.svelte +14 -11
  126. package/dist/themes/create-theme.d.ts.map +1 -1
  127. package/dist/themes/create-theme.js +8 -5
  128. package/dist/themes/css-generator.d.ts.map +1 -1
  129. package/dist/themes/css-generator.js +5 -4
  130. package/dist/themes/required-tokens.generated.json +172 -0
  131. package/dist/themes/styles/base.css +4 -1
  132. package/dist/themes/styles/glass.css +4 -1
  133. package/dist/themes/styles/happyvertical.css +7 -3
  134. package/dist/themes/styles/smrt.css +4 -1
  135. package/dist/themes/styles/studio.css +4 -1
  136. package/dist/utils/theme/typography.d.ts +21 -21
  137. package/dist/utils/theme/typography.js +21 -21
  138. package/package.json +8 -2
@@ -57,7 +57,7 @@ function handleClick(value: string) {
57
57
  }
58
58
  </script>
59
59
 
60
- <div data-density={density} class="filter-chips" class:sm={size === 'sm'} role="radiogroup">
60
+ <div data-density={density} class="filter-chips" class:sm={size === 'sm'} role="radiogroup" data-smrt-component="FilterChips">
61
61
  {#each allOptions as option (option.value)}
62
62
  {@const isActive = option.value === selected}
63
63
  <button
@@ -84,24 +84,26 @@ function handleClick(value: string) {
84
84
  </div>
85
85
 
86
86
  <style>
87
+ @layer smrt.reset, smrt.structure, smrt.theme;
88
+ @layer smrt.theme {
87
89
  .filter-chips {
88
90
  display: flex;
89
- gap: var(--smrt-spacing-2, 8px);
91
+ gap: var(--smrt-spacing-2);
90
92
  flex-wrap: wrap;
91
93
  }
92
94
 
93
95
  .filter-chip {
94
96
  display: inline-flex;
95
97
  align-items: center;
96
- gap: var(--smrt-spacing-2, 8px);
98
+ gap: var(--smrt-spacing-2);
97
99
  height: 32px;
98
- padding: 0 var(--smrt-spacing-4, 16px);
100
+ padding: 0 var(--smrt-spacing-4);
99
101
  font: var(--smrt-typography-label-large-font);
100
102
  font-weight: var(--smrt-typography-weight-medium, 500);
101
103
  color: var(--smrt-color-on-surface-variant);
102
104
  background-color: transparent;
103
105
  border: 1px solid var(--smrt-color-outline);
104
- border-radius: var(--smrt-radius-md, 8px);
106
+ border-radius: var(--smrt-radius-md);
105
107
  cursor: pointer;
106
108
  transition: all 200ms cubic-bezier(0.2, 0, 0, 1);
107
109
  white-space: nowrap;
@@ -111,9 +113,9 @@ function handleClick(value: string) {
111
113
 
112
114
  .sm .filter-chip {
113
115
  height: 28px;
114
- padding: 0 var(--smrt-spacing-3, 12px);
116
+ padding: 0 var(--smrt-spacing-3);
115
117
  font: var(--smrt-typography-label-medium-font);
116
- gap: var(--smrt-spacing-2, 8px);
118
+ gap: var(--smrt-spacing-2);
117
119
  }
118
120
 
119
121
  .filter-chip:hover:not(:disabled) {
@@ -124,11 +126,11 @@ function handleClick(value: string) {
124
126
  background-color: var(--smrt-color-secondary-container);
125
127
  border-color: transparent;
126
128
  color: var(--smrt-color-on-secondary-container);
127
- padding-left: var(--smrt-spacing-3, 12px);
129
+ padding-left: var(--smrt-spacing-3);
128
130
  }
129
131
 
130
132
  .sm .filter-chip.active {
131
- padding-left: var(--smrt-spacing-2, 8px);
133
+ padding-left: var(--smrt-spacing-2);
132
134
  }
133
135
 
134
136
  .filter-chip:disabled {
@@ -153,7 +155,8 @@ function handleClick(value: string) {
153
155
  color: var(--smrt-color-on-secondary-container);
154
156
  }
155
157
 
156
- .filter-chips[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min, 48px); }
158
+ .filter-chips[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
157
159
  .filter-chips[data-density='comfortable'] { --smrt-control-target-min: 0px; }
158
160
  .filter-chip { min-height: var(--smrt-control-target-min, 0px); min-width: var(--smrt-control-target-min, 0px); }
161
+ }
159
162
  </style>
@@ -154,6 +154,7 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
154
154
  {#snippet linkTab(tab: Tab)}
155
155
  <a
156
156
  class="tab-link"
157
+ data-smrt-part="tab"
157
158
  class:active={tab.id === active}
158
159
  href={tab.disabled ? undefined : tab.href}
159
160
  aria-current={tab.id === active ? 'page' : undefined}
@@ -167,20 +168,20 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
167
168
  handleClick(tab.id);
168
169
  }}
169
170
  >
170
- <span class="tab-label">{tab.label}</span>
171
+ <span class="tab-label" data-smrt-part="label">{tab.label}</span>
171
172
  {#if tab.count !== undefined}
172
- <span class="tab-count">{tab.count}</span>
173
+ <span class="tab-count" data-smrt-part="count">{tab.count}</span>
173
174
  {/if}
174
175
  {#if hasBadge(tab)}
175
- <span class="tab-badge">{tab.badge}</span>
176
+ <span class="tab-badge" data-smrt-part="badge">{tab.badge}</span>
176
177
  {/if}
177
178
  </a>
178
179
  {/snippet}
179
180
 
180
181
  {#if linkMode}
181
- <div class="tabs-container">
182
+ <div class="tabs-container" data-smrt-component="Tabs">
182
183
  <nav
183
- class="tabs-links"
184
+ class="tabs-links" data-smrt-part="list"
184
185
  class:sm={size === 'sm'}
185
186
  class:lg={size === 'lg'}
186
187
  aria-label={ariaLabel}
@@ -193,14 +194,14 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
193
194
  </div>
194
195
  {#if split.overflow.length > 0}
195
196
  <details class="tabs-more" bind:this={moreMenu}>
196
- <summary class="tab-link">
197
- <span class="tab-label">{moreLabel}</span>
197
+ <summary class="tab-link" data-smrt-part="trigger">
198
+ <span class="tab-label" data-smrt-part="label">{moreLabel}</span>
198
199
  {#if overflowHasBadge}
199
200
  <span class="tab-dot" aria-hidden="true"></span>
200
201
  <span class="visually-hidden">({moreBadgeLabel})</span>
201
202
  {/if}
202
203
  </summary>
203
- <div class="tabs-more-menu">
204
+ <div class="tabs-more-menu" data-smrt-part="menu">
204
205
  {#each split.overflow as tab (tab.id)}
205
206
  {@render linkTab(tab)}
206
207
  {/each}
@@ -209,15 +210,15 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
209
210
  {/if}
210
211
  </nav>
211
212
  {#if children}
212
- <div class="tab-panel">
213
+ <div class="tab-panel" data-smrt-part="panel">
213
214
  {@render children()}
214
215
  </div>
215
216
  {/if}
216
217
  </div>
217
218
  {:else}
218
- <div class="tabs-container">
219
+ <div class="tabs-container" data-smrt-component="Tabs">
219
220
  <div
220
- class="tabs-nav"
221
+ class="tabs-nav" data-smrt-part="list"
221
222
  class:sm={size === 'sm'}
222
223
  class:lg={size === 'lg'}
223
224
  class:secondary={variant === 'secondary'}
@@ -228,7 +229,7 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
228
229
  {#each tabs as tab (tab.id)}
229
230
  <button
230
231
  type="button"
231
- class="tab-button"
232
+ class="tab-button" data-smrt-part="tab"
232
233
  class:active={tab.id === active}
233
234
  disabled={tab.disabled}
234
235
  role="tab"
@@ -242,12 +243,12 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
242
243
  use:ripple
243
244
  >
244
245
  <span class="tab-content-wrapper">
245
- <span class="tab-label">{tab.label}</span>
246
+ <span class="tab-label" data-smrt-part="label">{tab.label}</span>
246
247
  {#if tab.count !== undefined}
247
- <span class="tab-count">{tab.count}</span>
248
+ <span class="tab-count" data-smrt-part="count">{tab.count}</span>
248
249
  {/if}
249
250
  {#if hasBadge(tab)}
250
- <span class="tab-badge">{tab.badge}</span>
251
+ <span class="tab-badge" data-smrt-part="badge">{tab.badge}</span>
251
252
  {/if}
252
253
  </span>
253
254
  <div class="active-indicator"></div>
@@ -257,7 +258,7 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
257
258
 
258
259
  {#if children}
259
260
  <div
260
- class="tab-panel"
261
+ class="tab-panel" data-smrt-part="panel"
261
262
  role="tabpanel"
262
263
  id="tab-panel-{instanceId}"
263
264
  aria-labelledby="tab-{instanceId}-{active}"
@@ -269,6 +270,8 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
269
270
  {/if}
270
271
 
271
272
  <style>
273
+ @layer smrt.reset, smrt.structure, smrt.theme;
274
+ @layer smrt.theme {
272
275
  .tabs-container {
273
276
  display: flex;
274
277
  flex-direction: column;
@@ -287,7 +290,7 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
287
290
  flex-direction: column;
288
291
  align-items: center;
289
292
  justify-content: center;
290
- padding: 0 var(--smrt-spacing-4, 16px);
293
+ padding: 0 var(--smrt-spacing-4);
291
294
  height: 48px;
292
295
  font: var(--smrt-typography-title-small-font);
293
296
  font-weight: var(--smrt-typography-weight-medium, 500);
@@ -333,7 +336,7 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
333
336
  .tab-content-wrapper {
334
337
  display: flex;
335
338
  align-items: center;
336
- gap: var(--smrt-spacing-2, 8px);
339
+ gap: var(--smrt-spacing-2);
337
340
  height: 100%;
338
341
  }
339
342
 
@@ -347,7 +350,7 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
347
350
  bottom: 0;
348
351
  height: 3px;
349
352
  background-color: var(--smrt-color-primary);
350
- border-radius: var(--smrt-radius-sm, 4px) var(--smrt-radius-sm, 4px) 0 0;
353
+ border-radius: var(--smrt-radius-sm) var(--smrt-radius-sm) 0 0;
351
354
  transition: width 200ms, opacity 200ms;
352
355
  width: 0;
353
356
  opacity: 0;
@@ -383,7 +386,7 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
383
386
  .tabs-links {
384
387
  display: flex;
385
388
  align-items: stretch;
386
- gap: var(--smrt-spacing-1, 4px);
389
+ gap: var(--smrt-spacing-1);
387
390
  width: 100%;
388
391
  border-bottom: 1px solid var(--smrt-color-outline-variant);
389
392
  }
@@ -392,7 +395,7 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
392
395
  display: flex;
393
396
  flex: 1 1 auto;
394
397
  min-width: 0;
395
- gap: var(--smrt-spacing-1, 4px);
398
+ gap: var(--smrt-spacing-1);
396
399
  overflow-x: auto;
397
400
  scrollbar-width: thin;
398
401
  }
@@ -401,9 +404,9 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
401
404
  display: inline-flex;
402
405
  flex: 0 0 auto;
403
406
  align-items: center;
404
- gap: var(--smrt-spacing-2, 8px);
407
+ gap: var(--smrt-spacing-2);
405
408
  min-height: 44px;
406
- padding: 0 var(--smrt-spacing-4, 16px);
409
+ padding: 0 var(--smrt-spacing-4);
407
410
  margin-bottom: -1px;
408
411
  border-bottom: 2px solid transparent;
409
412
  color: var(--smrt-color-on-surface-variant);
@@ -469,7 +472,7 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
469
472
  z-index: var(--smrt-z-index-dropdown, 20);
470
473
  display: grid;
471
474
  min-width: 12rem;
472
- padding: var(--smrt-spacing-1, 4px);
475
+ padding: var(--smrt-spacing-1);
473
476
  border: 1px solid var(--smrt-color-outline-variant);
474
477
  border-radius: var(--smrt-radius-small, 0.5rem);
475
478
  background: var(--smrt-color-surface);
@@ -490,4 +493,5 @@ function handleKeydown(event: KeyboardEvent, tabId: string) {
490
493
  clip: rect(0 0 0 0);
491
494
  white-space: nowrap;
492
495
  }
496
+ }
493
497
  </style>
@@ -130,7 +130,7 @@ function matches(permission: PermissionPickerItem, category: string): boolean {
130
130
  }
131
131
  </script>
132
132
 
133
- <section class="permission-picker" aria-labelledby={labelId}>
133
+ <section class="permission-picker" aria-labelledby={labelId} data-smrt-component="PermissionPicker">
134
134
  <div class="heading">
135
135
  <div>
136
136
  <h2 id={labelId}>{label}</h2>
@@ -188,6 +188,8 @@ function matches(permission: PermissionPickerItem, category: string): boolean {
188
188
  </section>
189
189
 
190
190
  <style>
191
+ @layer smrt.reset, smrt.structure, smrt.theme;
192
+ @layer smrt.theme {
191
193
  .permission-picker { display: grid; gap: var(--smrt-spacing-4); min-inline-size: 0; }
192
194
  .heading { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: var(--smrt-spacing-4); }
193
195
  h2, p { margin: 0; }
@@ -210,4 +212,5 @@ function matches(permission: PermissionPickerItem, category: string): boolean {
210
212
  .no-results { padding: var(--smrt-spacing-4); border-radius: var(--smrt-radius-medium); background: var(--smrt-color-surface-container-low); }
211
213
  .visually-hidden { position: absolute; inline-size: 1px; block-size: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
212
214
  [hidden] { display: none; }
215
+ }
213
216
  </style>
@@ -31,33 +31,35 @@ const variantClass = $derived.by(() => {
31
31
  <span
32
32
  class="role-badge {variantClass}"
33
33
  class:sm={size === 'sm'}
34
- class:lg={size === 'lg'}
34
+ class:lg={size === 'lg'} data-smrt-component="RoleBadge"
35
35
  >
36
36
  {role.name}
37
37
  </span>
38
38
 
39
39
  <style>
40
+ @layer smrt.reset, smrt.structure, smrt.theme;
41
+ @layer smrt.theme {
40
42
  .role-badge {
41
43
  display: inline-flex;
42
44
  align-items: center;
43
- padding: 0 var(--smrt-spacing-3, 12px);
45
+ padding: 0 var(--smrt-spacing-3);
44
46
  height: 24px;
45
47
  font: var(--smrt-typography-label-large-font);
46
48
  font-weight: var(--smrt-typography-weight-semibold, 600);
47
- border-radius: var(--smrt-radius-md, 8px);
49
+ border-radius: var(--smrt-radius-md);
48
50
  white-space: nowrap;
49
51
  transition: all 200ms cubic-bezier(0.2, 0, 0, 1);
50
52
  }
51
53
 
52
54
  .role-badge.sm {
53
- padding: 0 var(--smrt-spacing-2, 8px);
55
+ padding: 0 var(--smrt-spacing-2);
54
56
  height: 20px;
55
57
  font: var(--smrt-typography-label-small-font);
56
58
  font-weight: var(--smrt-typography-weight-semibold, 600);
57
59
  }
58
60
 
59
61
  .role-badge.lg {
60
- padding: 0 var(--smrt-spacing-4, 16px);
62
+ padding: 0 var(--smrt-spacing-4);
61
63
  height: 32px;
62
64
  font: var(--smrt-typography-title-small-font);
63
65
  font-weight: var(--smrt-typography-weight-semibold, 600);
@@ -83,4 +85,5 @@ const variantClass = $derived.by(() => {
83
85
  background-color: var(--smrt-color-surface-variant);
84
86
  color: var(--smrt-color-on-surface-variant);
85
87
  }
88
+ }
86
89
  </style>
@@ -122,7 +122,7 @@ $effect(() => {
122
122
  });
123
123
  </script>
124
124
 
125
- <div class="role-selector" class:disabled bind:this={wrapEl}>
125
+ <div class="role-selector" class:disabled bind:this={wrapEl} data-smrt-component="RoleSelector">
126
126
  <button
127
127
  bind:this={triggerEl}
128
128
  type="button"
@@ -189,6 +189,8 @@ $effect(() => {
189
189
  </div>
190
190
 
191
191
  <style>
192
+ @layer smrt.reset, smrt.structure, smrt.theme;
193
+ @layer smrt.theme {
192
194
  .role-selector {
193
195
  position: relative;
194
196
  width: 100%;
@@ -205,7 +207,7 @@ $effect(() => {
205
207
  width: 100%;
206
208
  padding: 0.5rem 0.75rem;
207
209
  background: var(--smrt-color-surface, white);
208
- border: 1px solid var(--smrt-color-outline, #6b7280);
210
+ border: 1px solid var(--smrt-color-outline);
209
211
  border-radius: var(--smrt-radius-small, 0.375rem);
210
212
  font-size: var(--smrt-typography-body-medium-size, 0.875rem);
211
213
  cursor: pointer;
@@ -214,16 +216,16 @@ $effect(() => {
214
216
 
215
217
  .trigger:focus-visible {
216
218
  outline: none;
217
- border-color: var(--smrt-color-primary, #005ac1);
218
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--smrt-color-primary, #005ac1) 10%, transparent);
219
+ border-color: var(--smrt-color-primary);
220
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--smrt-color-primary) 10%, transparent);
219
221
  }
220
222
 
221
223
  .trigger.open {
222
- border-color: var(--smrt-color-primary, #005ac1);
224
+ border-color: var(--smrt-color-primary);
223
225
  }
224
226
 
225
227
  .trigger:disabled {
226
- background: var(--smrt-color-surface-container-high, #f3f4f6);
228
+ background: var(--smrt-color-surface-container-high);
227
229
  cursor: not-allowed;
228
230
  }
229
231
 
@@ -234,13 +236,13 @@ $effect(() => {
234
236
  }
235
237
 
236
238
  .placeholder {
237
- color: var(--smrt-color-on-surface-variant, #9ca3af);
239
+ color: var(--smrt-color-on-surface-variant);
238
240
  }
239
241
 
240
242
  .chevron {
241
243
  width: 1.25rem;
242
244
  height: 1.25rem;
243
- color: var(--smrt-color-on-surface-variant, #6b7280);
245
+ color: var(--smrt-color-on-surface-variant);
244
246
  transition: transform var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
245
247
  }
246
248
 
@@ -255,7 +257,7 @@ $effect(() => {
255
257
  right: 0;
256
258
  margin-top: 0.25rem;
257
259
  background: var(--smrt-color-surface, white);
258
- border: 1px solid var(--smrt-color-outline-variant, #e5e7eb);
260
+ border: 1px solid var(--smrt-color-outline-variant);
259
261
  border-radius: var(--smrt-radius-small, 0.375rem);
260
262
  box-shadow: var(--smrt-elevation-2, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
261
263
  z-index: var(--smrt-z-index-dropdown, 1000);
@@ -277,20 +279,20 @@ $effect(() => {
277
279
  }
278
280
 
279
281
  .option:hover {
280
- background: var(--smrt-color-surface-container-high, #f3f4f6);
282
+ background: var(--smrt-color-surface-container-high);
281
283
  }
282
284
  /* Keyboard focus needs its own indicator: the hover background alone is a
283
285
  ~1.1:1 change against the list surface in every preset, which is invisible
284
286
  and fails WCAG 2.4.11. Inset so it is not clipped by the list's overflow
285
287
  (#2322). */
286
288
  .option:focus-visible {
287
- background: var(--smrt-color-surface-container-high, #f3f4f6);
288
- outline: 2px solid var(--smrt-color-primary, #005ac1);
289
+ background: var(--smrt-color-surface-container-high);
290
+ outline: 2px solid var(--smrt-color-primary);
289
291
  outline-offset: -2px;
290
292
  }
291
293
 
292
294
  .option.selected {
293
- background: var(--smrt-color-primary-container, #eff6ff);
295
+ background: var(--smrt-color-primary-container);
294
296
  }
295
297
 
296
298
  .option-content {
@@ -301,22 +303,23 @@ $effect(() => {
301
303
 
302
304
  .role-name {
303
305
  font-weight: var(--smrt-typography-weight-medium, 500);
304
- color: var(--smrt-color-on-surface, #111827);
306
+ color: var(--smrt-color-on-surface);
305
307
  text-transform: capitalize;
306
308
  }
307
309
 
308
310
  .system-badge {
309
311
  font-size: var(--smrt-typography-label-small-size, 0.625rem);
310
312
  padding: 0.125rem 0.375rem;
311
- background: var(--smrt-color-primary-container, #dbeafe);
312
- color: var(--smrt-color-on-primary-container, #1e40af);
313
- border-radius: var(--smrt-radius-full, 9999px);
313
+ background: var(--smrt-color-primary-container);
314
+ color: var(--smrt-color-on-primary-container);
315
+ border-radius: var(--smrt-radius-full);
314
316
  text-transform: uppercase;
315
317
  font-weight: var(--smrt-typography-weight-semibold, 600);
316
318
  }
317
319
 
318
320
  .description {
319
321
  font-size: var(--smrt-typography-body-small-size, 0.75rem);
320
- color: var(--smrt-color-on-surface-variant, #6b7280);
322
+ color: var(--smrt-color-on-surface-variant);
321
323
  }
324
+ }
322
325
  </style>
@@ -36,11 +36,11 @@ $effect(() => {
36
36
  const cssVariables = $derived.by(() => {
37
37
  const themeVars = Object.entries(themeState.theme)
38
38
  .map(([key, value]) => {
39
- // camelCase to kebab-case: onPrimary -> --md-sys-color-on-primary
39
+ // camelCase to kebab-case: onPrimary -> --smrt-color-on-primary
40
40
  const kebab = key
41
41
  .replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2')
42
42
  .toLowerCase();
43
- return `--md-sys-color-${kebab}: ${value};`;
43
+ return `--smrt-color-${kebab}: ${value};`;
44
44
  })
45
45
  .join('\n');
46
46
 
@@ -61,11 +61,14 @@ const cssVariables = $derived.by(() => {
61
61
  </div>
62
62
 
63
63
  <style>
64
+ @layer smrt.reset, smrt.structure, smrt.theme;
65
+ @layer smrt.theme {
64
66
  .smrt-theme-provider {
65
67
  display: contents;
66
68
  /* Base typography and other global resets can go here later */
67
- color: var(--md-sys-color-on-background);
68
- background-color: var(--md-sys-color-background);
69
+ color: var(--smrt-color-on-background);
70
+ background-color: var(--smrt-color-background);
69
71
  font-family: var(--smrt-font-family, Roboto, sans-serif);
70
72
  }
73
+ }
71
74
  </style>
@@ -42,8 +42,11 @@ function toggle(item: string) {
42
42
  setAccordionContext({ isOpen, toggle });
43
43
  </script>
44
44
 
45
- <div class="accordion {className}">{#if children}{@render children()}{/if}</div>
45
+ <div class="accordion {className}" data-smrt-component="Accordion">{#if children}{@render children()}{/if}</div>
46
46
 
47
47
  <style>
48
+ @layer smrt.reset, smrt.structure, smrt.theme;
49
+ @layer smrt.theme {
48
50
  .accordion { overflow: hidden; border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-large); background: var(--smrt-color-surface); }
51
+ }
49
52
  </style>
@@ -20,7 +20,7 @@ const context = getAccordionContext();
20
20
  const open = $derived(context.isOpen(value));
21
21
  </script>
22
22
 
23
- <section class="item">
23
+ <section class="item" data-smrt-component="AccordionItem">
24
24
  <h3>
25
25
  <button id={triggerId} type="button" aria-expanded={open} aria-controls={panelId} {disabled} onclick={() => context.toggle(value)}>
26
26
  <span>{title}</span><span class="chevron" aria-hidden="true">⌄</span>
@@ -32,6 +32,8 @@ const open = $derived(context.isOpen(value));
32
32
  </section>
33
33
 
34
34
  <style>
35
+ @layer smrt.reset, smrt.structure, smrt.theme;
36
+ @layer smrt.theme {
35
37
  .item { border-bottom: 1px solid var(--smrt-color-outline-variant); }
36
38
  .item:last-child { border-bottom: 0; }
37
39
  h3 { margin: 0; }
@@ -43,4 +45,5 @@ const open = $derived(context.isOpen(value));
43
45
  button[aria-expanded='true'] .chevron { transform: rotate(180deg); }
44
46
  .panel { padding: 0 var(--smrt-spacing-4) var(--smrt-spacing-4); color: var(--smrt-color-on-surface-variant); }
45
47
  @media (prefers-reduced-motion: reduce) { .chevron { transition: none; } }
48
+ }
46
49
  </style>
@@ -57,7 +57,7 @@ const statusLabels: Record<AvatarStatus, string> = {
57
57
  };
58
58
  </script>
59
59
 
60
- <span class="avatar {size} {className}" {...rest}>
60
+ <span class="avatar {size} {className}" {...rest} data-smrt-component="Avatar">
61
61
  {#if src && !imgFailed}
62
62
  <img
63
63
  class="avatar__img"
@@ -80,13 +80,15 @@ const statusLabels: Record<AvatarStatus, string> = {
80
80
  </span>
81
81
 
82
82
  <style>
83
+ @layer smrt.reset, smrt.structure, smrt.theme;
84
+ @layer smrt.theme {
83
85
  .avatar {
84
86
  position: relative;
85
87
  display: inline-flex;
86
88
  align-items: center;
87
89
  justify-content: center;
88
90
  flex-shrink: 0;
89
- border-radius: var(--smrt-radius-full, 9999px);
91
+ border-radius: var(--smrt-radius-full);
90
92
  background: var(--smrt-color-surface-container-high);
91
93
  color: var(--smrt-color-on-surface-variant);
92
94
  font-weight: var(--smrt-typography-weight-medium, 500);
@@ -136,21 +138,21 @@ const statusLabels: Record<AvatarStatus, string> = {
136
138
  height: 28%;
137
139
  min-width: 8px;
138
140
  min-height: 8px;
139
- border-radius: var(--smrt-radius-full, 9999px);
141
+ border-radius: var(--smrt-radius-full);
140
142
  border: 2px solid var(--smrt-color-surface);
141
143
  box-sizing: border-box;
142
144
  }
143
145
  .avatar__status.online {
144
- background: var(--smrt-color-success, #2e7d32);
146
+ background: var(--smrt-color-success);
145
147
  }
146
148
  .avatar__status.offline {
147
- background: var(--smrt-color-outline, #79747e);
149
+ background: var(--smrt-color-outline);
148
150
  }
149
151
  .avatar__status.away {
150
- background: var(--smrt-color-warning, #ed6c02);
152
+ background: var(--smrt-color-warning);
151
153
  }
152
154
  .avatar__status.busy {
153
- background: var(--smrt-color-error, #ba1a1a);
155
+ background: var(--smrt-color-error);
154
156
  }
155
157
 
156
158
  .avatar__sr-only {
@@ -164,4 +166,5 @@ const statusLabels: Record<AvatarStatus, string> = {
164
166
  white-space: nowrap;
165
167
  border: 0;
166
168
  }
169
+ }
167
170
  </style>
@@ -15,11 +15,13 @@ interface Props extends Omit<HTMLAttributes<HTMLSpanElement>, 'class'> {
15
15
  const { variant = 'default', size = 'md', children, ...rest }: Props = $props();
16
16
  </script>
17
17
 
18
- <span class="badge {variant} {size}" {...rest}>
18
+ <span class="badge {variant} {size}" {...rest} data-smrt-component="Badge">
19
19
  {@render children?.()}
20
20
  </span>
21
21
 
22
22
  <style>
23
+ @layer smrt.reset, smrt.structure, smrt.theme;
24
+ @layer smrt.theme {
23
25
  .badge {
24
26
  display: inline-flex;
25
27
  align-items: center;
@@ -70,4 +72,5 @@ const { variant = 'default', size = 'md', children, ...rest }: Props = $props();
70
72
  background: var(--smrt-color-primary-container);
71
73
  color: var(--smrt-color-on-primary-container);
72
74
  }
75
+ }
73
76
  </style>
@@ -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,18 +147,20 @@ 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;
@@ -255,7 +257,7 @@ function handleClick(event: MouseEvent) {
255
257
  height: 1em;
256
258
  border: 2px solid currentColor;
257
259
  border-right-color: transparent;
258
- border-radius: var(--smrt-radius-full, 9999px);
260
+ border-radius: var(--smrt-radius-full);
259
261
  animation: spin 0.75s linear infinite;
260
262
  }
261
263
 
@@ -312,7 +314,8 @@ function handleClick(event: MouseEvent) {
312
314
  }
313
315
  }
314
316
 
315
- .button[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min, 48px); }
317
+ .button[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
316
318
  .button[data-density='comfortable'] { --smrt-control-target-min: 0px; }
317
319
  .button { min-height: var(--smrt-control-target-min, 0px); min-width: var(--smrt-control-target-min, 0px); }
320
+ }
318
321
  </style>