@happyvertical/smrt-ui 0.55.17 → 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 (90) hide show
  1. package/README.md +7 -0
  2. package/dist/components/calendar/CalendarView.svelte +2 -2
  3. package/dist/components/chat/ReactionPicker.svelte +2 -2
  4. package/dist/components/data/CollectionList.svelte +1 -1
  5. package/dist/components/data/CollectionToolbar.svelte +1 -1
  6. package/dist/components/data/DataTable.svelte +44 -32
  7. package/dist/components/data/SortableHeader.svelte +2 -2
  8. package/dist/components/feedback/Alert.svelte +4 -3
  9. package/dist/components/feedback/LoadingOverlay.svelte +2 -2
  10. package/dist/components/feedback/Modal.svelte +11 -9
  11. package/dist/components/feedback/PhoneSheet.svelte +2 -2
  12. package/dist/components/feedback/ToastViewport.svelte +1 -1
  13. package/dist/components/forms/CameraCapture.svelte +2 -2
  14. package/dist/components/forms/Checkbox.svelte +1 -1
  15. package/dist/components/forms/Combobox.svelte +1 -1
  16. package/dist/components/forms/FilePicker.svelte +1 -1
  17. package/dist/components/forms/Input.svelte +11 -7
  18. package/dist/components/forms/InputGroup.svelte +1 -1
  19. package/dist/components/forms/Listbox.svelte +1 -1
  20. package/dist/components/forms/Radio.svelte +1 -1
  21. package/dist/components/forms/RangeSlider.svelte +1 -1
  22. package/dist/components/forms/SearchInput.svelte +2 -2
  23. package/dist/components/forms/SegmentedControl.svelte +1 -1
  24. package/dist/components/forms/Select.svelte +11 -7
  25. package/dist/components/forms/SignaturePad.svelte +2 -2
  26. package/dist/components/forms/Slider.svelte +1 -1
  27. package/dist/components/forms/StagedControlReview.svelte +1 -1
  28. package/dist/components/forms/Switch.svelte +1 -1
  29. package/dist/components/forms/TagsInput.svelte +1 -1
  30. package/dist/components/forms/Textarea.svelte +10 -6
  31. package/dist/components/forms/Toggle.svelte +2 -2
  32. package/dist/components/forms/ToggleButton.svelte +1 -1
  33. package/dist/components/layout/PageHeader.svelte +2 -2
  34. package/dist/components/nav/Tabs.svelte +47 -17
  35. package/dist/components/permissions/PermissionPicker.svelte +1 -1
  36. package/dist/components/roles/RoleSelector.svelte +2 -2
  37. package/dist/components/ui/AccordionItem.svelte +1 -1
  38. package/dist/components/ui/Badge.svelte +2 -1
  39. package/dist/components/ui/Button.svelte +38 -29
  40. package/dist/components/ui/Card.svelte +11 -10
  41. package/dist/components/ui/Chip.svelte +31 -7
  42. package/dist/components/ui/Disclosure.svelte +1 -1
  43. package/dist/components/ui/Dropdown.svelte +20 -15
  44. package/dist/components/ui/IconToggle.svelte +2 -2
  45. package/dist/components/ui/Popover.svelte +4 -4
  46. package/dist/components/ui/Tooltip.svelte +1 -0
  47. package/dist/components/ui/Tree.svelte +2 -2
  48. package/dist/themes/CUSTOM_THEME_GUIDE.md +15 -0
  49. package/dist/themes/README.md +126 -0
  50. package/dist/themes/ThemeProvider.svelte +173 -13
  51. package/dist/themes/ThemeProvider.svelte.d.ts +7 -2
  52. package/dist/themes/ThemeProvider.svelte.d.ts.map +1 -1
  53. package/dist/themes/create-theme.d.ts +22 -1
  54. package/dist/themes/create-theme.d.ts.map +1 -1
  55. package/dist/themes/create-theme.js +93 -5
  56. package/dist/themes/css-generator.d.ts.map +1 -1
  57. package/dist/themes/css-generator.js +15 -16
  58. package/dist/themes/derive.d.ts +31 -0
  59. package/dist/themes/derive.d.ts.map +1 -0
  60. package/dist/themes/derive.js +183 -0
  61. package/dist/themes/glass/index.d.ts.map +1 -1
  62. package/dist/themes/glass/index.js +127 -14
  63. package/dist/themes/happyvertical/index.d.ts.map +1 -1
  64. package/dist/themes/happyvertical/index.js +101 -20
  65. package/dist/themes/index.d.ts +2 -2
  66. package/dist/themes/index.d.ts.map +1 -1
  67. package/dist/themes/index.js +1 -1
  68. package/dist/themes/material/index.d.ts.map +1 -1
  69. package/dist/themes/material/index.js +129 -28
  70. package/dist/themes/required-tokens.generated.json +29 -0
  71. package/dist/themes/shared.d.ts +35 -0
  72. package/dist/themes/shared.d.ts.map +1 -1
  73. package/dist/themes/shared.js +32 -0
  74. package/dist/themes/smrt/index.d.ts +3 -0
  75. package/dist/themes/smrt/index.d.ts.map +1 -1
  76. package/dist/themes/smrt/index.js +108 -26
  77. package/dist/themes/studio/index.d.ts.map +1 -1
  78. package/dist/themes/studio/index.js +116 -40
  79. package/dist/themes/styles/base.css +225 -0
  80. package/dist/themes/styles/glass.css +103 -19
  81. package/dist/themes/styles/happyvertical.css +104 -12
  82. package/dist/themes/styles/material.css +127 -35
  83. package/dist/themes/styles/smrt.css +122 -30
  84. package/dist/themes/styles/studio.css +128 -36
  85. package/dist/themes/theme-script.d.ts +3 -1
  86. package/dist/themes/theme-script.d.ts.map +1 -1
  87. package/dist/themes/theme-script.js +7 -2
  88. package/dist/themes/types.d.ts +118 -6
  89. package/dist/themes/types.d.ts.map +1 -1
  90. package/package.json +2 -2
package/README.md CHANGED
@@ -1163,6 +1163,13 @@ app for `:global(` and for unlayered rules that target smrt components, then
1163
1163
  confirm each still renders as intended. Remove overrides that only existed to
1164
1164
  win a specificity fight.
1165
1165
 
1166
+ Also check for an unlayered universal reset (`*`, `html`/`body` with
1167
+ `margin`/`padding: 0`, or Tailwind preflight / normalize.css imported without a
1168
+ layer). It strips padding from every component. Put it in `@layer smrt.reset`, or
1169
+ import `@happyvertical/smrt-ui/themes/styles/base.css`. The recipes are in
1170
+ [agents/styling.md](agents/styling.md#host-resets-universal-reset-tailwind-preflight-normalize).
1171
+ `smrt doctor` flags these.
1172
+
1166
1173
  ### Adding or changing a component stylesheet
1167
1174
 
1168
1175
  Component `<style>` blocks must be inside `@layer smrt.*`. The lint gate
@@ -917,8 +917,8 @@ $effect(() => {
917
917
 
918
918
  button:focus-visible,
919
919
  a:focus-visible {
920
- outline: 2px solid var(--smrt-color-primary);
921
- outline-offset: 2px;
920
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
921
+ outline-offset: var(--smrt-focus-ring-offset);
922
922
  }
923
923
 
924
924
  /* ---- Month grid ---- */
@@ -108,8 +108,8 @@ function nameFor(emoji: string): string {
108
108
  transform: scale(1.15);
109
109
  }
110
110
  .reactions__btn:focus-visible {
111
- outline: 2px solid var(--smrt-color-primary);
112
- outline-offset: 1px;
111
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
112
+ outline-offset: var(--smrt-focus-ring-offset);
113
113
  }
114
114
 
115
115
  @media (prefers-reduced-motion: reduce) {
@@ -119,7 +119,7 @@ function toggle(key: string | number) {
119
119
  input { width: 1.125rem; height: 1.125rem; accent-color: var(--smrt-color-primary); }
120
120
  .main { display: grid; min-width: 0; gap: var(--smrt-spacing-1); padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; }
121
121
  button.main { width: 100%; cursor: pointer; }
122
- button.main:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: 3px; }
122
+ button.main:focus-visible { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
123
123
  .main strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: var(--smrt-typography-title-small-font); }
124
124
  .main span { color: var(--smrt-color-on-surface-variant); }
125
125
  .actions { position: relative; z-index: 1; display: flex; gap: var(--smrt-spacing-1); }
@@ -278,7 +278,7 @@ onDestroy(() => surfaceRegistration?.cleanup());
278
278
  @layer smrt.reset, smrt.structure, smrt.theme;
279
279
  @layer smrt.theme {
280
280
  .toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--smrt-spacing-2); padding: var(--smrt-spacing-2) 0; color: var(--smrt-color-on-surface); }
281
- .toolbar--highlighted { outline: 2px solid var(--smrt-color-primary); outline-offset: 3px; }
281
+ .toolbar--highlighted { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
282
282
  .search { flex: 1 1 14rem; max-width: 24rem; }
283
283
  .filters, .actions, .bulk { display: flex; align-items: center; gap: var(--smrt-spacing-2); }
284
284
  .bulk { padding: var(--smrt-spacing-1) var(--smrt-spacing-2); border-radius: var(--smrt-radius-small); background: var(--smrt-color-secondary-container); color: var(--smrt-color-on-secondary-container); }
@@ -1930,20 +1930,20 @@ $effect(() => {
1930
1930
  }
1931
1931
 
1932
1932
  .data-table-container--overflowing:focus-visible {
1933
- outline: 2px solid var(--smrt-color-primary);
1934
- outline-offset: 2px;
1933
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
1934
+ outline-offset: var(--smrt-focus-ring-offset);
1935
1935
  }
1936
1936
 
1937
1937
  .data-table-container--highlighted {
1938
- outline: 2px solid var(--smrt-color-primary);
1939
- outline-offset: 3px;
1938
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
1939
+ outline-offset: var(--smrt-focus-ring-offset);
1940
1940
  }
1941
1941
 
1942
1942
  .data-table__overflow-cue {
1943
1943
  display: flex;
1944
1944
  justify-content: space-between;
1945
1945
  padding: var(--smrt-spacing-2, 0.5rem) var(--smrt-spacing-3, 0.75rem);
1946
- border-top: 1px solid var(--smrt-color-outline-variant);
1946
+ border-top: var(--smrt-hairline);
1947
1947
  background: var(--smrt-color-surface-container-low);
1948
1948
  color: var(--smrt-color-on-surface-variant);
1949
1949
  font-size: var(--smrt-typography-label-small-size, 0.75rem);
@@ -1984,7 +1984,7 @@ $effect(() => {
1984
1984
  font-family: var(--smrt-font-family, inherit);
1985
1985
  font-size: var(--smrt-typography-body-medium-size, 0.875rem);
1986
1986
  color: var(--smrt-color-on-surface);
1987
- background: var(--smrt-color-surface);
1987
+ background: var(--smrt-surface-panel);
1988
1988
  }
1989
1989
 
1990
1990
  .data-table__caption {
@@ -1997,7 +1997,7 @@ $effect(() => {
1997
1997
 
1998
1998
  /* Header */
1999
1999
  .data-table__head {
2000
- background: var(--smrt-color-surface-container);
2000
+ background: var(--smrt-surface-sunken);
2001
2001
  }
2002
2002
 
2003
2003
  .data-table-container--sticky .data-table__head {
@@ -2007,7 +2007,7 @@ $effect(() => {
2007
2007
  }
2008
2008
 
2009
2009
  .data-table__row--header {
2010
- border-bottom: 1px solid var(--smrt-color-outline-variant);
2010
+ border-bottom: var(--smrt-hairline);
2011
2011
  }
2012
2012
 
2013
2013
  .data-table__cell--header {
@@ -2072,8 +2072,8 @@ $effect(() => {
2072
2072
  }
2073
2073
 
2074
2074
  .data-table__resize-handle:focus-visible {
2075
- outline: 2px solid var(--smrt-color-primary);
2076
- outline-offset: -2px;
2075
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
2076
+ outline-offset: calc(var(--smrt-focus-ring-width) * -1);
2077
2077
  }
2078
2078
 
2079
2079
  .data-table__sort-button:hover {
@@ -2092,11 +2092,11 @@ $effect(() => {
2092
2092
 
2093
2093
  /* Body */
2094
2094
  .data-table__body {
2095
- background: var(--smrt-color-surface);
2095
+ background: var(--smrt-surface-panel);
2096
2096
  }
2097
2097
 
2098
2098
  .data-table__row {
2099
- border-bottom: 1px solid var(--smrt-color-outline-variant);
2099
+ border-bottom: var(--smrt-hairline);
2100
2100
  transition: background-color var(--smrt-duration-fast, 150ms) var(--smrt-easing-standard, ease);
2101
2101
  }
2102
2102
 
@@ -2106,16 +2106,24 @@ $effect(() => {
2106
2106
  }
2107
2107
 
2108
2108
  .data-table--hoverable .data-table__row:hover:not(.data-table__row--loading):not(.data-table__row--empty):not(.data-table__row--selected) {
2109
- background: var(--smrt-color-surface-container-low);
2109
+ background: color-mix(
2110
+ in srgb,
2111
+ var(--smrt-color-on-surface) calc(var(--smrt-state-hover) * 100%),
2112
+ var(--smrt-surface-panel)
2113
+ );
2110
2114
  }
2111
2115
 
2112
2116
  .data-table__row--selected {
2113
- background: var(--smrt-color-primary-container);
2117
+ background: color-mix(
2118
+ in srgb,
2119
+ var(--smrt-color-primary) calc(var(--smrt-state-selected) * 100%),
2120
+ var(--smrt-surface-panel)
2121
+ );
2114
2122
  }
2115
2123
 
2116
2124
  .data-table--striped:not(.data-table--virtualized) .data-table__row:nth-child(even):not(.data-table__row--selected),
2117
2125
  .data-table__row--striped:not(.data-table__row--selected) {
2118
- background: var(--smrt-color-surface-container-lowest);
2126
+ background: var(--smrt-surface-sunken);
2119
2127
  }
2120
2128
 
2121
2129
  .data-table__row--interactive {
@@ -2123,13 +2131,13 @@ $effect(() => {
2123
2131
  }
2124
2132
 
2125
2133
  .data-table__row--interactive:focus-visible {
2126
- outline: 2px solid var(--smrt-color-primary);
2127
- outline-offset: -2px;
2134
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
2135
+ outline-offset: calc(var(--smrt-focus-ring-width) * -1);
2128
2136
  }
2129
2137
 
2130
2138
  .data-table-container--virtualized .data-table__row:focus-visible {
2131
- outline: 2px solid var(--smrt-color-primary);
2132
- outline-offset: -2px;
2139
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
2140
+ outline-offset: calc(var(--smrt-focus-ring-width) * -1);
2133
2141
  }
2134
2142
 
2135
2143
  .data-table__cell {
@@ -2141,18 +2149,22 @@ $effect(() => {
2141
2149
  .data-table__cell--pinned-end {
2142
2150
  position: sticky;
2143
2151
  z-index: 1;
2144
- background: var(--smrt-color-surface);
2152
+ background: var(--smrt-surface-panel);
2145
2153
  }
2146
2154
 
2147
2155
  .data-table__cell--header.data-table__cell--pinned-start,
2148
2156
  .data-table__cell--header.data-table__cell--pinned-end {
2149
2157
  z-index: 3;
2150
- background: var(--smrt-color-surface-container);
2158
+ background: var(--smrt-surface-sunken);
2151
2159
  }
2152
2160
 
2153
2161
  .data-table__row--selected .data-table__cell--pinned-start,
2154
2162
  .data-table__row--selected .data-table__cell--pinned-end {
2155
- background: var(--smrt-color-primary-container);
2163
+ background: color-mix(
2164
+ in srgb,
2165
+ var(--smrt-color-primary) calc(var(--smrt-state-selected) * 100%),
2166
+ var(--smrt-surface-panel)
2167
+ );
2156
2168
  }
2157
2169
 
2158
2170
  .data-table__cell--checkbox {
@@ -2161,12 +2173,12 @@ $effect(() => {
2161
2173
  }
2162
2174
 
2163
2175
  .data-table__cell--expand { width: 2.75rem; text-align: center; }
2164
- .data-table__expand-button { width: 1.75rem; height: 1.75rem; border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-full); background: transparent; color: var(--smrt-color-on-surface); cursor: pointer; }
2165
- .data-table__expand-button:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: 2px; }
2166
- .data-table__cell--expanded { padding: var(--smrt-spacing-4); background: var(--smrt-color-surface-container-low); }
2167
- .data-table__footer { border-top: 2px solid var(--smrt-color-outline-variant); font-weight: var(--smrt-typography-weight-medium); }
2168
- .data-table__row--structural { background: var(--smrt-color-surface-container-low); }
2169
- .data-table__row--footer { border-top: 2px solid var(--smrt-color-outline-variant); }
2176
+ .data-table__expand-button { width: 1.75rem; height: 1.75rem; border: var(--smrt-hairline-strong); border-radius: var(--smrt-radius-full); background: transparent; color: var(--smrt-color-on-surface); cursor: pointer; }
2177
+ .data-table__expand-button:focus-visible { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
2178
+ .data-table__cell--expanded { padding: var(--smrt-spacing-4); background: var(--smrt-surface-sunken); }
2179
+ .data-table__footer { border-top: var(--smrt-hairline-strong); font-weight: var(--smrt-typography-weight-medium); }
2180
+ .data-table__row--structural { background: var(--smrt-surface-sunken); }
2181
+ .data-table__row--footer { border-top: var(--smrt-hairline-strong); }
2170
2182
  .data-table__cell--structural-label { font-weight: var(--smrt-typography-weight-semibold, 600); }
2171
2183
 
2172
2184
  .data-table__checkbox {
@@ -2228,7 +2240,7 @@ $effect(() => {
2228
2240
 
2229
2241
  .data-table__async-status {
2230
2242
  padding: var(--smrt-spacing-3, 0.75rem);
2231
- border-top: 1px solid var(--smrt-color-outline-variant);
2243
+ border-top: var(--smrt-hairline);
2232
2244
  font: var(--smrt-typography-body-small-font);
2233
2245
  }
2234
2246
 
@@ -2243,8 +2255,8 @@ $effect(() => {
2243
2255
  }
2244
2256
 
2245
2257
  .data-table__retry-button:focus-visible {
2246
- outline: 2px solid var(--smrt-color-primary);
2247
- outline-offset: 2px;
2258
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
2259
+ outline-offset: var(--smrt-focus-ring-offset);
2248
2260
  }
2249
2261
 
2250
2262
  /* Size variants */
@@ -2316,7 +2328,7 @@ $effect(() => {
2316
2328
  display: grid;
2317
2329
  gap: var(--smrt-spacing-1, 0.35rem);
2318
2330
  padding: var(--smrt-spacing-3, 0.85rem) var(--smrt-spacing-4, 1rem);
2319
- border-bottom: 1px solid var(--smrt-color-outline-variant);
2331
+ border-bottom: var(--smrt-hairline);
2320
2332
  }
2321
2333
  .data-table-container--phone-cards .data-table__cell {
2322
2334
  display: block;
@@ -158,8 +158,8 @@ const actionLabel = $derived(listSortActionLabel(sort, columnId, label, spec));
158
158
  text-decoration: underline;
159
159
  }
160
160
  .sortable-header__control:focus-visible {
161
- outline: 2px solid var(--smrt-color-primary, var(--smrt-color-primary, currentColor));
162
- outline-offset: -2px;
161
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
162
+ outline-offset: calc(var(--smrt-focus-ring-width) * -1);
163
163
  }
164
164
  .sortable-header__icon {
165
165
  opacity: 0.35;
@@ -47,7 +47,8 @@ const role = $derived(
47
47
  <style>
48
48
  @layer smrt.reset, smrt.structure, smrt.theme;
49
49
  @layer smrt.theme {
50
- .alert { --smrt-alert-color: var(--smrt-color-primary); display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; align-items: start; gap: var(--smrt-spacing-3); padding: var(--smrt-spacing-3) var(--smrt-spacing-4); border: 1px solid color-mix(in srgb, var(--smrt-alert-color) 35%, var(--smrt-color-outline-variant)); border-radius: var(--smrt-radius-medium); background: color-mix(in srgb, var(--smrt-alert-color) 8%, var(--smrt-color-surface)); color: var(--smrt-color-on-surface); }
50
+ .alert { --smrt-alert-color: var(--smrt-color-primary); display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; align-items: start; gap: var(--smrt-spacing-3); padding: var(--smrt-spacing-3) var(--smrt-spacing-4); border: var(--smrt-hairline); border-color: color-mix(in srgb, var(--smrt-alert-color) 35%, var(--smrt-hairline-color)); box-shadow: var(--smrt-inset-highlight); border-radius: var(--smrt-radius-medium); background: color-mix(in srgb, var(--smrt-alert-color) 8%, var(--smrt-surface-panel)); color: var(--smrt-color-on-surface); }
51
+ .alert--info { --smrt-alert-color: var(--smrt-color-info); }
51
52
  .alert--success { --smrt-alert-color: var(--smrt-color-success); }
52
53
  .alert--warning { --smrt-alert-color: var(--smrt-color-warning); }
53
54
  .alert--error { --smrt-alert-color: var(--smrt-color-error); }
@@ -57,7 +58,7 @@ const role = $derived(
57
58
  .message { color: var(--smrt-color-on-surface-variant); }
58
59
  .action { align-self: center; }
59
60
  .dismiss { width: 2rem; height: 2rem; margin: -.35rem; border: 0; border-radius: var(--smrt-radius-full); background: transparent; color: inherit; font-size: 1.25rem; cursor: pointer; }
60
- .dismiss:hover { background: color-mix(in srgb, currentColor 10%, transparent); }
61
- .dismiss:focus-visible { outline: 2px solid var(--smrt-color-primary); }
61
+ .dismiss:hover { background: color-mix(in srgb, currentColor calc(var(--smrt-state-hover) * 100%), transparent); }
62
+ .dismiss:focus-visible { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
62
63
  }
63
64
  </style>
@@ -273,8 +273,8 @@ function handleKeydown(e: KeyboardEvent) {
273
273
  }
274
274
 
275
275
  .dismiss-btn:focus-visible {
276
- outline: 2px solid var(--smrt-color-primary);
277
- outline-offset: 2px;
276
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
277
+ outline-offset: var(--smrt-focus-ring-offset);
278
278
  }
279
279
 
280
280
  @media (prefers-reduced-motion: reduce) {
@@ -201,8 +201,8 @@ const placementClass = $derived(`modal--${placement}`);
201
201
  }
202
202
 
203
203
  .modal::backdrop {
204
- background: var(--smrt-color-scrim, rgba(0, 0, 0, 0.5));
205
- backdrop-filter: blur(2px);
204
+ background: var(--smrt-surface-overlay);
205
+ backdrop-filter: blur(var(--smrt-glass-blur));
206
206
  }
207
207
 
208
208
  .modal:not([open]) {
@@ -214,9 +214,11 @@ const placementClass = $derived(`modal--${placement}`);
214
214
  flex-direction: column;
215
215
  max-height: calc(100vh - var(--smrt-spacing-8, 2rem));
216
216
  max-width: calc(100vw - var(--smrt-spacing-8, 2rem));
217
- background: var(--smrt-color-surface);
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);
218
220
  border-radius: var(--smrt-radius-large, 0.75rem);
219
- 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);
220
222
  overflow: hidden;
221
223
  animation: modal-enter var(--smrt-duration-medium2, 300ms) var(--smrt-easing-emphasized, cubic-bezier(0.2, 0, 0, 1));
222
224
  }
@@ -312,7 +314,7 @@ const placementClass = $derived(`modal--${placement}`);
312
314
  justify-content: space-between;
313
315
  gap: var(--smrt-spacing-3, 0.75rem);
314
316
  padding: var(--smrt-spacing-4, 1rem) var(--smrt-spacing-5, 1.25rem);
315
- border-bottom: 1px solid var(--smrt-color-outline-variant);
317
+ border-bottom: var(--smrt-hairline);
316
318
  flex-shrink: 0;
317
319
  }
318
320
 
@@ -346,13 +348,13 @@ const placementClass = $derived(`modal--${placement}`);
346
348
  }
347
349
 
348
350
  .modal__close:hover {
349
- background: var(--smrt-color-surface-container-highest);
351
+ background: color-mix(in srgb, currentColor calc(var(--smrt-state-hover) * 100%), transparent);
350
352
  color: var(--smrt-color-on-surface);
351
353
  }
352
354
 
353
355
  .modal__close:focus-visible {
354
- outline: 2px solid var(--smrt-color-primary);
355
- outline-offset: 2px;
356
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
357
+ outline-offset: var(--smrt-focus-ring-offset);
356
358
  }
357
359
 
358
360
  /* Body */
@@ -370,7 +372,7 @@ const placementClass = $derived(`modal--${placement}`);
370
372
  justify-content: flex-end;
371
373
  gap: var(--smrt-spacing-3, 0.75rem);
372
374
  padding: var(--smrt-spacing-4, 1rem) var(--smrt-spacing-5, 1.25rem);
373
- border-top: 1px solid var(--smrt-color-outline-variant);
375
+ border-top: var(--smrt-hairline);
374
376
  flex-shrink: 0;
375
377
  }
376
378
 
@@ -207,8 +207,8 @@ function onkeydown(event: KeyboardEvent): void {
207
207
  }
208
208
 
209
209
  .phone-sheet__close:focus-visible {
210
- outline: 2px solid var(--smrt-color-primary);
211
- 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);
212
212
  }
213
213
 
214
214
  .phone-sheet__body {
@@ -189,7 +189,7 @@ async function runAction(toast: Toast) {
189
189
  @layer smrt.reset, smrt.structure, smrt.theme;
190
190
  @layer smrt.theme {
191
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; box-shadow: var(--smrt-elevation-3); }
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
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
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
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)); }
@@ -598,8 +598,8 @@ useControlRegistration(() => {
598
598
  }
599
599
  .action:focus-visible,
600
600
  .picker:has(input:focus-visible) {
601
- outline: 2px solid var(--smrt-color-primary);
602
- outline-offset: 2px;
601
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
602
+ outline-offset: var(--smrt-focus-ring-offset);
603
603
  }
604
604
  .action:disabled,
605
605
  .picker.disabled {
@@ -171,7 +171,7 @@ useControlRegistration(() => {
171
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); }
172
172
  .checkbox__label { position: relative; z-index: 1; }
173
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); }
174
- 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); }
175
175
  .checkbox__check { font-size: .8rem; font-weight: 800; line-height: 1; }
176
176
  .checkbox__dash { width: .6rem; height: 2px; background: currentColor; }
177
177
  .checkbox:not(:only-child)::before { inline-size: var(--smrt-checkbox-box); margin-inline-start: calc(var(--smrt-checkbox-box) / -2); }
@@ -351,7 +351,7 @@ useControlRegistration(() => {
351
351
  @layer smrt.theme {
352
352
  .combobox { position: relative; display: grid; gap: 0; color: var(--smrt-color-on-surface); }
353
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; }
354
- 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); }
355
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); }
356
356
  .options.empty { padding: var(--smrt-spacing-2) var(--smrt-spacing-3); color: var(--smrt-color-on-surface-variant); }
357
357
  .sr-status { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
@@ -122,7 +122,7 @@ useControlRegistration(() => {
122
122
  .file-picker.dropzone { width: 100%; min-height: 8rem; align-items: center; justify-content: center; border-style: dashed; text-align: center; }
123
123
  .file-picker.dragging { border-color: var(--smrt-color-primary); background: var(--smrt-color-primary-container); }
124
124
  .file-picker.disabled { opacity: .5; cursor: not-allowed; } input { position: absolute; width: 1px; height: 1px; opacity: 0; }
125
- input:focus-visible ~ .label { outline: 2px solid var(--smrt-color-primary); outline-offset: 3px; }
125
+ input:focus-visible ~ .label { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
126
126
  .label { font: var(--smrt-typography-label-large-font); } .description { font: var(--smrt-typography-body-small-font); color: var(--smrt-color-on-surface-variant); }
127
127
  .file-picker[data-smrt-highlighted='true'] { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
128
128
  }
@@ -230,20 +230,24 @@ export function getElement(): HTMLInputElement | null {
230
230
  width: 100%;
231
231
  padding: 0.5rem 0.75rem;
232
232
  font-size: var(--smrt-typography-body-medium-size, 0.875rem);
233
+ font-weight: var(--smrt-control-weight);
233
234
  line-height: var(--smrt-typography-body-medium-line-height, 1.5);
234
235
  color: var(--smrt-color-on-surface);
235
- background-color: var(--smrt-color-surface);
236
- border: 1px solid var(--smrt-color-outline);
237
- border-radius: var(--smrt-radius-small, 0.375rem);
236
+ background-color: var(--smrt-surface-control);
237
+ border: var(--smrt-hairline-strong);
238
+ border-radius: var(--smrt-control-radius);
238
239
  transition:
239
240
  border-color var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease-in-out),
240
241
  box-shadow var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease-in-out);
241
242
  }
242
243
 
243
244
  .input:focus {
244
- outline: none;
245
245
  border-color: var(--smrt-color-primary);
246
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--smrt-color-primary) 10%, transparent);
246
+ }
247
+
248
+ .input:focus-visible {
249
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
250
+ outline-offset: var(--smrt-focus-ring-offset);
247
251
  }
248
252
 
249
253
  .input[data-smrt-highlighted='true'] {
@@ -255,7 +259,7 @@ export function getElement(): HTMLInputElement | null {
255
259
  .input:disabled {
256
260
  background-color: var(--smrt-color-surface-container-high);
257
261
  cursor: not-allowed;
258
- opacity: 0.7;
262
+ opacity: var(--smrt-state-disabled);
259
263
  }
260
264
 
261
265
  .input::placeholder {
@@ -279,6 +283,6 @@ export function getElement(): HTMLInputElement | null {
279
283
 
280
284
  .input[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
281
285
  .input[data-density='comfortable'] { --smrt-control-target-min: 0px; }
282
- .input { min-height: var(--smrt-control-target-min, 0px); }
286
+ .input { min-height: max(var(--smrt-control-height), var(--smrt-control-target-min, 0px)); }
283
287
  }
284
288
  </style>
@@ -19,6 +19,6 @@ let { prefix, suffix, children, class: className = '' }: Props = $props();
19
19
  .input-group { display: flex; align-items: stretch; width: 100%; border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); overflow: hidden; }
20
20
  .adornment { display: inline-flex; align-items: center; padding: 0 var(--smrt-spacing-3); background: var(--smrt-color-surface-container-low); color: var(--smrt-color-on-surface-variant); }
21
21
  .control { flex: 1; min-width: 0; } .input-group .control :global(input), .input-group .control :global(select), .input-group .control :global(textarea) { border: 0; border-radius: 0; box-shadow: none; }
22
- .input-group:focus-within { outline: 2px solid var(--smrt-color-primary); outline-offset: 1px; }
22
+ .input-group:focus-within { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
23
23
  }
24
24
  </style>
@@ -167,7 +167,7 @@ useControlRegistration(() => {
167
167
  @layer smrt.theme {
168
168
  .listbox { display: grid; padding: var(--smrt-spacing-1); border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); }
169
169
  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; cursor: pointer; }
170
- button[aria-selected='true'] { background: var(--smrt-color-secondary-container); color: var(--smrt-color-on-secondary-container); } button:focus-visible { outline: 2px solid var(--smrt-color-primary); }
170
+ button[aria-selected='true'] { background: var(--smrt-color-secondary-container); color: var(--smrt-color-on-secondary-container); } button:focus-visible { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
171
171
  button:disabled { opacity: .5; cursor: not-allowed; } :global(.listbox[data-smrt-highlighted='true']) { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
172
172
  }
173
173
  </style>
@@ -43,7 +43,7 @@ function handleChange(event: Event & { currentTarget: HTMLInputElement }) {
43
43
  .radio__mark::after { content: ''; width: .55rem; height: .55rem; border-radius: 50%; background: var(--smrt-color-primary); transform: scale(0); transition: transform var(--smrt-duration-short2); }
44
44
  input:checked + .radio__mark { border-color: var(--smrt-color-primary); }
45
45
  input:checked + .radio__mark::after { transform: scale(1); }
46
- input:focus-visible + .radio__mark { outline: 2px solid var(--smrt-color-primary); outline-offset: 3px; }
46
+ input:focus-visible + .radio__mark { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
47
47
  .radio { min-height: var(--smrt-control-target-min, 0px); min-width: var(--smrt-control-target-min, 0px); }
48
48
 
49
49
  /* Table cells: grow vertically only, never into a neighbouring cell. */
@@ -202,7 +202,7 @@ useControlRegistration(() => {
202
202
  .range-slider__inputs { display: flex; gap: var(--smrt-spacing-3); }
203
203
  .range-slider__inputs label { display: grid; gap: var(--smrt-spacing-1); flex: 1; font: var(--smrt-typography-label-small-font); color: var(--smrt-color-on-surface-variant); }
204
204
  .range-slider__inputs input { width: 100%; padding: var(--smrt-spacing-2); border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); color: var(--smrt-color-on-surface); }
205
- input:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: 3px; }
205
+ input:focus-visible { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
206
206
  :global(.range-slider[data-smrt-highlighted='true']) { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; border-radius: var(--smrt-radius-small); }
207
207
  }
208
208
  </style>
@@ -196,8 +196,8 @@ onDestroy(cancel);
196
196
  color: var(--smrt-color-on-surface);
197
197
  }
198
198
  .search-input__clear:focus-visible {
199
- outline: 2px solid var(--smrt-color-primary);
200
- outline-offset: -2px;
199
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
200
+ outline-offset: calc(var(--smrt-focus-ring-width) * -1);
201
201
  }
202
202
  /* Phones: full width. */
203
203
  @media (max-width: 48rem) {
@@ -256,7 +256,7 @@ useControlRegistration(() => {
256
256
  .segmented.full-width { display: flex; width: 100%; }
257
257
  label { position: relative; display: inline-flex; align-items: center; justify-content: center; flex: 1; min-height: 2.5rem; padding: 0 var(--smrt-spacing-4); border: 0; border-right: 1px solid var(--smrt-color-outline); background: var(--smrt-color-surface); color: var(--smrt-color-on-surface); cursor: pointer; }
258
258
  label:last-child { border-right: 0; } label.selected { background: var(--smrt-color-secondary-container); color: var(--smrt-color-on-secondary-container); }
259
- label:has(input:focus-visible) { outline: 2px solid var(--smrt-color-primary); outline-offset: -3px; } label.disabled, label:has(input:disabled) { opacity: .5; cursor: not-allowed; }
259
+ label:has(input:focus-visible) { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: calc(var(--smrt-focus-ring-width) * -1); } label.disabled, label:has(input:disabled) { opacity: .5; cursor: not-allowed; }
260
260
  :global(.segmented[data-smrt-highlighted='true']) { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; }
261
261
 
262
262
  .segmented[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
@@ -164,13 +164,14 @@ export function getElement(): HTMLSelectElement | null {
164
164
  font-size: var(--smrt-typography-body-medium-size, 0.875rem);
165
165
  line-height: var(--smrt-typography-body-medium-line-height, 1.5);
166
166
  color: var(--smrt-color-on-surface);
167
- background-color: var(--smrt-color-surface);
167
+ background-color: var(--smrt-surface-control);
168
+ font-weight: var(--smrt-control-weight);
168
169
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%2379747e' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
169
170
  background-position: right 0.5rem center;
170
171
  background-repeat: no-repeat;
171
172
  background-size: 1.5em 1.5em;
172
- border: 1px solid var(--smrt-color-outline);
173
- border-radius: 0.375rem;
173
+ border: var(--smrt-hairline-strong);
174
+ border-radius: var(--smrt-control-radius);
174
175
  appearance: none;
175
176
  cursor: pointer;
176
177
  transition:
@@ -179,9 +180,12 @@ export function getElement(): HTMLSelectElement | null {
179
180
  }
180
181
 
181
182
  .select:focus {
182
- outline: none;
183
183
  border-color: var(--smrt-color-primary);
184
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--smrt-color-primary) 10%, transparent);
184
+ }
185
+
186
+ .select:focus-visible {
187
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
188
+ outline-offset: var(--smrt-focus-ring-offset);
185
189
  }
186
190
 
187
191
  .select[data-smrt-highlighted='true'] {
@@ -192,7 +196,7 @@ export function getElement(): HTMLSelectElement | null {
192
196
  .select:disabled {
193
197
  background-color: var(--smrt-color-surface-container-high);
194
198
  cursor: not-allowed;
195
- opacity: 0.7;
199
+ opacity: var(--smrt-state-disabled);
196
200
  }
197
201
 
198
202
  /* Phones: 16px text so iOS Safari doesn't zoom the page when the field
@@ -212,6 +216,6 @@ export function getElement(): HTMLSelectElement | null {
212
216
 
213
217
  .select[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
214
218
  .select[data-density='comfortable'] { --smrt-control-target-min: 0px; }
215
- .select { min-height: var(--smrt-control-target-min, 0px); }
219
+ .select { min-height: max(var(--smrt-control-height), var(--smrt-control-target-min, 0px)); }
216
220
  }
217
221
  </style>
@@ -424,8 +424,8 @@ useControlRegistration(() => {
424
424
  color: var(--smrt-color-on-primary);
425
425
  }
426
426
  .action:focus-visible {
427
- outline: 2px solid var(--smrt-color-primary);
428
- outline-offset: 2px;
427
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
428
+ outline-offset: var(--smrt-focus-ring-offset);
429
429
  }
430
430
  .action:disabled {
431
431
  opacity: 0.5;
@@ -158,7 +158,7 @@ useControlRegistration(() => {
158
158
  output { color: var(--smrt-color-primary); font-variant-numeric: tabular-nums; }
159
159
  input[type='range'] { flex: 1; min-width: 8rem; accent-color: var(--smrt-color-primary); cursor: pointer; }
160
160
  .slider__number { width: 5.5rem; padding: var(--smrt-spacing-2); border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); color: var(--smrt-color-on-surface); }
161
- input:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: 3px; }
161
+ input:focus-visible { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
162
162
  .slider[data-smrt-highlighted='true'] { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; border-radius: var(--smrt-radius-small); }
163
163
  }
164
164
  </style>
@@ -645,7 +645,7 @@ async function discardAll(event: MouseEvent): Promise<void> {
645
645
  input[type="checkbox"] { width: auto; }
646
646
  button { min-height: 2.25rem; padding: .4rem .75rem; color: var(--smrt-color-on-primary); background: var(--smrt-color-primary); border: 1px solid transparent; border-radius: var(--smrt-radius-small, .375rem); cursor: pointer; }
647
647
  button.secondary { color: var(--smrt-color-primary); background: transparent; border-color: var(--smrt-color-outline); }
648
- button:focus-visible, input:focus-visible, textarea:focus-visible { outline: 2px solid var(--smrt-color-primary); outline-offset: 2px; }
648
+ button:focus-visible, input:focus-visible, textarea:focus-visible { outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
649
649
  button:disabled { cursor: not-allowed; opacity: .55; }
650
650
  .item-actions { margin-block-start: var(--smrt-spacing-3, .75rem); }
651
651
  .problem { margin-block-start: var(--smrt-spacing-2, .5rem); color: var(--smrt-color-error); }