@happyvertical/smrt-ui 0.55.21 → 0.55.23

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 (56) hide show
  1. package/dist/components/calendar/DayView.svelte +1 -1
  2. package/dist/components/data/CollectionList.svelte +1 -1
  3. package/dist/components/data/ListSortSelect.svelte +1 -1
  4. package/dist/components/feedback/Alert.svelte +83 -22
  5. package/dist/components/feedback/LoadingOverlay.svelte +2 -2
  6. package/dist/components/feedback/PhoneSheet.svelte +1 -1
  7. package/dist/components/feedback/ToastViewport.svelte +1 -1
  8. package/dist/components/feedback/WorkingStrip.svelte +4 -4
  9. package/dist/components/forms/CameraCapture.svelte +1 -1
  10. package/dist/components/forms/Checkbox.svelte +130 -36
  11. package/dist/components/forms/Combobox.svelte +2 -2
  12. package/dist/components/forms/Fieldset.svelte +1 -1
  13. package/dist/components/forms/FilePicker.svelte +1 -1
  14. package/dist/components/forms/FormActionBar.svelte +2 -2
  15. package/dist/components/forms/Input.svelte +66 -61
  16. package/dist/components/forms/InputGroup.svelte +1 -1
  17. package/dist/components/forms/Listbox.svelte +1 -1
  18. package/dist/components/forms/MultiSelect.svelte +2 -2
  19. package/dist/components/forms/RangeSlider.svelte +1 -1
  20. package/dist/components/forms/SegmentedControl.svelte +2 -2
  21. package/dist/components/forms/Select.svelte +67 -61
  22. package/dist/components/forms/SignaturePad.svelte +1 -1
  23. package/dist/components/forms/Slider.svelte +1 -1
  24. package/dist/components/forms/StagedControlReview.svelte +4 -4
  25. package/dist/components/forms/Switch.svelte +127 -35
  26. package/dist/components/forms/TagsInput.svelte +1 -1
  27. package/dist/components/forms/ToggleButton.svelte +1 -1
  28. package/dist/components/layout/Footer.svelte +1 -1
  29. package/dist/components/layout/Masthead.svelte +1 -1
  30. package/dist/components/memberships/MembershipCard.svelte +3 -3
  31. package/dist/components/nav/FilterChips.svelte +1 -1
  32. package/dist/components/nav/Tabs.svelte +189 -190
  33. package/dist/components/nav/Tabs.svelte.d.ts.map +1 -1
  34. package/dist/components/permissions/PermissionPicker.svelte +4 -4
  35. package/dist/components/roles/RoleSelector.svelte +2 -2
  36. package/dist/components/ui/Accordion.svelte +1 -1
  37. package/dist/components/ui/AccordionItem.svelte +1 -1
  38. package/dist/components/ui/Badge.svelte +23 -25
  39. package/dist/components/ui/Button.svelte +101 -115
  40. package/dist/components/ui/Button.svelte.d.ts +1 -1
  41. package/dist/components/ui/Card.svelte +44 -57
  42. package/dist/components/ui/Chip.svelte +65 -60
  43. package/dist/components/ui/Disclosure.svelte +1 -1
  44. package/dist/components/ui/IconToggle.svelte +2 -2
  45. package/dist/styles/unstyled.css +2 -2
  46. package/dist/svelte/playground/BaseControlsPreview.svelte +2 -2
  47. package/dist/svelte/playground/CollectionsPreview.svelte +1 -1
  48. package/dist/svelte/playground/DataTablePreview.svelte +5 -5
  49. package/dist/svelte/playground/FeedbackPreview.svelte +1 -1
  50. package/dist/svelte/playground/InteractiveControlsPreview.svelte +1 -1
  51. package/dist/svelte/playground/StylelessPreview.svelte +35 -2
  52. package/dist/svelte/playground/StylelessPreview.svelte.d.ts.map +1 -1
  53. package/dist/themes/README.md +1 -1
  54. package/dist/themes/components/ColorSchemeToggle.svelte +3 -3
  55. package/dist/themes/components/ThemeSwitcher.svelte +3 -3
  56. package/package.json +2 -2
@@ -330,7 +330,7 @@ function getTypeLabel(type: string): string {
330
330
 
331
331
  .event-card--link:hover {
332
332
  background: var(--smrt-day-view-card-hover);
333
- border-color: var(--smrt-color-outline);
333
+ border-color: var(--smrt-hairline-strong-color);
334
334
  }
335
335
 
336
336
  .event-card--link:hover .event-arrow {
@@ -112,7 +112,7 @@ function toggle(key: string | number) {
112
112
  @layer smrt.theme {
113
113
  .collection { display: grid; gap: var(--smrt-spacing-2); margin: 0; padding: 0; list-style: none; }
114
114
  .collection--grid { grid-template-columns: repeat(auto-fill, minmax(min(18rem, 100%), 1fr)); }
115
- li { position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--smrt-spacing-3); min-width: 0; padding: var(--smrt-spacing-3); border: var(--smrt-card-border, 1px solid var(--smrt-color-outline-variant)); border-radius: var(--smrt-radius-medium); background: var(--smrt-card-background, var(--smrt-color-surface)); color: var(--smrt-color-on-surface); }
115
+ li { position: relative; display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--smrt-spacing-3); min-width: 0; padding: var(--smrt-spacing-3); border: var(--smrt-card-border, var(--smrt-hairline)); border-radius: var(--smrt-radius-medium); background: var(--smrt-card-background, var(--smrt-color-surface)); color: var(--smrt-color-on-surface); }
116
116
  li.selected { border-color: var(--smrt-color-primary); background: var(--smrt-color-primary-container); }
117
117
  /* The selection box and actions sit above an item's stretched link. */
118
118
  .select { position: relative; z-index: 1; display: inline-flex; align-items: center; justify-content: center; min-width: 2.75rem; min-height: 2.75rem; }
@@ -114,7 +114,7 @@ function handleChange(event: Event) {
114
114
  .list-sort-select__control {
115
115
  min-height: 2.75rem;
116
116
  padding: 0.25rem 0.5rem;
117
- border: 1px solid var(--smrt-color-outline-variant, currentColor);
117
+ border: var(--smrt-hairline);
118
118
  border-radius: var(--smrt-radius-md, 0.375rem);
119
119
  background: var(--smrt-color-surface, transparent);
120
120
  color: inherit;
@@ -32,38 +32,99 @@ const role = $derived(
32
32
  );
33
33
  </script>
34
34
 
35
- <div class="alert alert--{variant} {className}" {role} data-smrt-component="Alert">
36
- <span class="marker" aria-hidden="true"></span>
37
- <div class="content">
38
- {#if title}<strong>{title}</strong>{/if}
39
- {#if children}<div class="message">{@render children()}</div>{/if}
35
+ <div class="smrt-alert__root smrt-alert__{variant} {className}" {role} data-smrt-component="Alert">
36
+ <span class="smrt-alert__marker" data-smrt-part="marker" aria-hidden="true"></span>
37
+ <div class="smrt-alert__content" data-smrt-part="body">
38
+ {#if title}<strong data-smrt-part="title">{title}</strong>{/if}
39
+ {#if children}<div class="smrt-alert__message" data-smrt-part="message">{@render children()}</div>{/if}
40
40
  </div>
41
- {#if action}<div class="action">{@render action()}</div>{/if}
41
+ {#if action}<div class="smrt-alert__action" data-smrt-part="actions">{@render action()}</div>{/if}
42
42
  {#if dismissible}
43
- <button type="button" class="dismiss" aria-label="Dismiss" onclick={ondismiss}>×</button>
43
+ <button type="button" class="smrt-alert__dismiss" data-smrt-part="close" aria-label="Dismiss" onclick={ondismiss}>×</button>
44
44
  {/if}
45
45
  </div>
46
46
 
47
47
  <style>
48
48
  @layer smrt.reset, smrt.structure, smrt.theme;
49
+ @layer smrt.structure {
50
+ .smrt-alert__root {
51
+ display: grid;
52
+ grid-template-columns: auto minmax(0, 1fr) auto auto;
53
+ align-items: start;
54
+ gap: var(--smrt-spacing-3);
55
+ padding: var(--smrt-spacing-3) var(--smrt-spacing-4);
56
+ }
57
+ .smrt-alert__marker {
58
+ width: .65rem;
59
+ height: .65rem;
60
+ margin-top: .4rem;
61
+ }
62
+ .smrt-alert__content {
63
+ min-width: 0;
64
+ }
65
+ strong {
66
+ display: block;
67
+ }
68
+ .smrt-alert__action {
69
+ align-self: center;
70
+ }
71
+ .smrt-alert__dismiss {
72
+ width: 2rem;
73
+ height: 2rem;
74
+ margin: -.35rem;
75
+ cursor: pointer;
76
+ }
77
+ }
49
78
  @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: 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); }
52
- .alert--success { --smrt-alert-color: var(--smrt-color-success); }
53
- .alert--warning { --smrt-alert-color: var(--smrt-color-warning); }
54
- .alert--error { --smrt-alert-color: var(--smrt-color-error); }
55
- .marker { width: .65rem; height: .65rem; margin-top: .4rem; border-radius: 50%; background: var(--smrt-alert-color); }
56
- .content { min-width: 0; }
57
- strong { display: block; font: var(--smrt-typography-title-small-font); }
58
- .message { color: var(--smrt-color-on-surface-variant); }
59
- .action { align-self: center; }
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; }
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); }
79
+ .smrt-alert__root {
80
+ --smrt-alert-color: var(--smrt-color-primary);
81
+ border: var(--smrt-hairline);
82
+ border-color: color-mix(in srgb, var(--smrt-alert-color) 35%, var(--smrt-hairline-color));
83
+ box-shadow: var(--smrt-inset-highlight);
84
+ border-radius: var(--smrt-radius-medium);
85
+ background: color-mix(in srgb, var(--smrt-alert-color) 8%, var(--smrt-surface-panel));
86
+ color: var(--smrt-color-on-surface);
87
+ }
88
+ .smrt-alert__info {
89
+ --smrt-alert-color: var(--smrt-color-info);
90
+ }
91
+ .smrt-alert__success {
92
+ --smrt-alert-color: var(--smrt-color-success);
93
+ }
94
+ .smrt-alert__warning {
95
+ --smrt-alert-color: var(--smrt-color-warning);
96
+ }
97
+ .smrt-alert__error {
98
+ --smrt-alert-color: var(--smrt-color-error);
99
+ }
100
+ .smrt-alert__marker {
101
+ border-radius: 50%;
102
+ background: var(--smrt-alert-color);
103
+ }
104
+ strong {
105
+ font: var(--smrt-typography-title-small-font);
106
+ }
107
+ .smrt-alert__message {
108
+ color: var(--smrt-color-on-surface-variant);
109
+ }
110
+ .smrt-alert__dismiss {
111
+ border: 0;
112
+ border-radius: var(--smrt-radius-full);
113
+ background: transparent;
114
+ color: inherit;
115
+ font-size: 1.25rem;
116
+ }
117
+ .smrt-alert__dismiss:hover {
118
+ background: color-mix(in srgb, currentColor calc(var(--smrt-state-hover) * 100%), transparent);
119
+ }
120
+ .smrt-alert__dismiss:focus-visible {
121
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
122
+ outline-offset: var(--smrt-focus-ring-offset);
123
+ }
63
124
  /* Forced colors (#3908): system colours where colour alone carries meaning. */
64
125
  @media (forced-colors: active) {
65
- .alert { background: Canvas; border-color: CanvasText; }
66
- .marker { background: CanvasText; }
126
+ .smrt-alert__root { background: Canvas; border-color: CanvasText; }
127
+ .smrt-alert__marker { background: CanvasText; }
67
128
  }
68
129
  }
69
130
  </style>
@@ -261,7 +261,7 @@ function handleKeydown(e: KeyboardEvent) {
261
261
  font-size: var(--smrt-typography-label-large-size, 0.875rem);
262
262
  color: var(--smrt-color-on-surface-variant);
263
263
  background: transparent;
264
- border: 1px solid var(--smrt-color-outline);
264
+ border: var(--smrt-hairline-strong);
265
265
  border-radius: var(--smrt-radius-md);
266
266
  cursor: pointer;
267
267
  transition: all var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
@@ -269,7 +269,7 @@ function handleKeydown(e: KeyboardEvent) {
269
269
 
270
270
  .dismiss-btn:hover {
271
271
  background: var(--smrt-color-surface-container-highest);
272
- border-color: var(--smrt-color-outline);
272
+ border-color: var(--smrt-hairline-strong-color);
273
273
  }
274
274
 
275
275
  .dismiss-btn:focus-visible {
@@ -169,7 +169,7 @@ function onkeydown(event: KeyboardEvent): void {
169
169
  min-block-size: 3.5rem;
170
170
  padding-block: 0;
171
171
  padding-inline: var(--smrt-spacing-4) var(--smrt-spacing-2);
172
- border-block-end: 1px solid var(--smrt-color-outline-variant);
172
+ border-block-end: var(--smrt-hairline);
173
173
  touch-action: pan-x;
174
174
  }
175
175
 
@@ -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; 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); }
192
+ .viewport :global([data-smrt-component="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)); }
@@ -269,7 +269,7 @@ function handleKeydown(event: KeyboardEvent) {
269
269
  box-sizing: border-box;
270
270
  min-block-size: 2.75rem;
271
271
  padding-inline-end: var(--smrt-spacing-2);
272
- border-block-start: 1px solid var(--smrt-color-outline-variant);
272
+ border-block-start: var(--smrt-hairline);
273
273
  box-shadow: var(--smrt-elevation-2);
274
274
  }
275
275
 
@@ -279,14 +279,14 @@ function handleKeydown(event: KeyboardEvent) {
279
279
  max-inline-size: min(34rem, calc(100vw - 2rem));
280
280
  padding-block: var(--smrt-spacing-1);
281
281
  padding-inline-end: var(--smrt-spacing-2);
282
- border: 1px solid var(--smrt-color-outline-variant);
282
+ border: var(--smrt-hairline);
283
283
  border-radius: var(--smrt-radius-xl, 1.25rem);
284
284
  box-shadow: var(--smrt-elevation-3);
285
285
  }
286
286
 
287
287
  .working-strip--inline {
288
288
  padding: var(--smrt-spacing-2) var(--smrt-spacing-3);
289
- border-block-end: 1px solid var(--smrt-color-outline-variant);
289
+ border-block-end: var(--smrt-hairline);
290
290
  }
291
291
 
292
292
  .working-strip[data-phase='waiting'] {
@@ -390,7 +390,7 @@ function handleKeydown(event: KeyboardEvent) {
390
390
  min-inline-size: 2.75rem;
391
391
  min-block-size: 2.75rem;
392
392
  padding: 0 var(--smrt-spacing-4);
393
- border: 1px solid var(--smrt-color-outline-variant);
393
+ border: var(--smrt-hairline);
394
394
  border-radius: var(--smrt-radius-full);
395
395
  background: var(--smrt-color-surface);
396
396
  color: var(--smrt-color-on-surface);
@@ -583,7 +583,7 @@ useControlRegistration(() => {
583
583
  min-width: var(--smrt-touch-target-min);
584
584
  min-height: var(--smrt-touch-target-min);
585
585
  padding: 0 var(--smrt-spacing-4);
586
- border: 1px solid var(--smrt-color-outline);
586
+ border: var(--smrt-hairline-strong);
587
587
  border-radius: var(--smrt-radius-small);
588
588
  background: var(--smrt-color-surface);
589
589
  color: var(--smrt-color-on-surface);
@@ -124,8 +124,8 @@ useControlRegistration(() => {
124
124
  </script>
125
125
 
126
126
  <label
127
- data-density={density} class="checkbox {className}"
128
- class:checkbox--disabled={disabled}
127
+ data-density={density} class="smrt-checkbox__root {className}"
128
+ class:smrt-checkbox__disabled={disabled}
129
129
  data-smrt-hit-target
130
130
  data-smrt-control={controlId}
131
131
  data-smrt-form={interactionContext?.formId}
@@ -145,17 +145,17 @@ useControlRegistration(() => {
145
145
  aria-describedby={resolvedDescription}
146
146
  aria-invalid={formGroup?.().invalid ? 'true' : undefined}
147
147
  onchange={handleChange}
148
- {...rest}
148
+ {...rest} data-smrt-part="input"
149
149
  />
150
- <span class="checkbox__box" aria-hidden="true">
151
- {#if indeterminate}<span class="checkbox__dash"></span>{:else if checked}<span class="checkbox__check">✓</span>{/if}
150
+ <span class="smrt-checkbox__box" data-smrt-part="box" aria-hidden="true">
151
+ {#if indeterminate}<span class="smrt-checkbox__dash" data-smrt-part="indicator"></span>{:else if checked}<span class="smrt-checkbox__check" data-smrt-part="indicator">✓</span>{/if}
152
152
  </span>
153
- {#if label}<span class="checkbox__label">{label}</span>{/if}
153
+ {#if label}<span class="smrt-checkbox__label" data-smrt-part="label">{label}</span>{/if}
154
154
  </label>
155
155
 
156
156
  <style>
157
157
  @layer smrt.reset, smrt.structure, smrt.theme;
158
- @layer smrt.theme {
158
+ @layer smrt.structure {
159
159
  /* The visible box stays 18px. A transparent 44x44 hit area (WCAG 2.5.8 / M3 touch target) is
160
160
  centred on it with ::before, so clicks nearby still land on the wrapping <label>. It adds no
161
161
  layout. The box and the label text sit above every hit area (z-index 1 over 0), so one
@@ -164,43 +164,137 @@ useControlRegistration(() => {
164
164
  checkbox stands alone in its parent. Beside anything else (a row title, a link, other text)
165
165
  it keeps to the box's own column and grows vertically only. Resize it with
166
166
  --smrt-control-hit-size (0px turns it off). */
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); }
169
- .checkbox--disabled { opacity: .5; cursor: not-allowed; }
170
- input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
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
- .checkbox__label { position: relative; z-index: 1; }
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: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color); outline-offset: var(--smrt-focus-ring-offset); }
175
- .checkbox__check { font-size: .8rem; font-weight: 800; line-height: 1; }
176
- .checkbox__dash { width: .6rem; height: 2px; background: currentColor; }
177
- .checkbox:not(:only-child)::before { inline-size: var(--smrt-checkbox-box); margin-inline-start: calc(var(--smrt-checkbox-box) / -2); }
178
- .checkbox[data-smrt-highlighted='true'] { outline: 3px solid var(--smrt-color-tertiary); outline-offset: 4px; border-radius: var(--smrt-radius-small); }
179
-
167
+ .smrt-checkbox__root {
168
+ --smrt-checkbox-box: 1.125rem;
169
+ --smrt-checkbox-hit: max(var(--smrt-control-hit-size, 2.75rem), var(--smrt-checkbox-box));
170
+ position: relative;
171
+ display: inline-flex;
172
+ align-items: center;
173
+ gap: var(--smrt-spacing-2);
174
+ cursor: pointer;
175
+ }
176
+ .smrt-checkbox__root::before {
177
+ content: '';
178
+ position: absolute;
179
+ z-index: 0;
180
+ inset-block-start: 50%;
181
+ inset-inline-start: calc(var(--smrt-checkbox-box) / 2);
182
+ inline-size: var(--smrt-checkbox-hit);
183
+ block-size: var(--smrt-checkbox-hit);
184
+ margin-block-start: calc(var(--smrt-checkbox-hit) / -2);
185
+ margin-inline-start: calc(var(--smrt-checkbox-hit) / -2);
186
+ }
187
+ .smrt-checkbox__disabled {
188
+ cursor: not-allowed;
189
+ }
190
+ input {
191
+ position: absolute;
192
+ width: 1px;
193
+ height: 1px;
194
+ opacity: 0;
195
+ pointer-events: none;
196
+ }
197
+ .smrt-checkbox__box {
198
+ position: relative;
199
+ z-index: 1;
200
+ width: 1.125rem;
201
+ height: 1.125rem;
202
+ display: grid;
203
+ place-items: center;
204
+ }
205
+ .smrt-checkbox__label {
206
+ position: relative;
207
+ z-index: 1;
208
+ }
209
+ .smrt-checkbox__dash {
210
+ width: .6rem;
211
+ height: 2px;
212
+ }
213
+ .smrt-checkbox__root:not(:only-child)::before {
214
+ inline-size: var(--smrt-checkbox-box);
215
+ margin-inline-start: calc(var(--smrt-checkbox-box) / -2);
216
+ }
180
217
  /* Table cells: neighbouring cells hold links and other controls, so the hit area never leaves
181
218
  the cell's column. It grows vertically only (box wide)... */
182
- :global(:is(td, th)) .checkbox::before { inline-size: var(--smrt-checkbox-box); margin-inline-start: calc(var(--smrt-checkbox-box) / -2); }
219
+ :global(:is(td, th)) .smrt-checkbox__root::before {
220
+ inline-size: var(--smrt-checkbox-box);
221
+ margin-inline-start: calc(var(--smrt-checkbox-box) / -2);
222
+ }
183
223
  /* ...unless the checkbox is the cell's only content: then the whole cell, clipped to its own
184
224
  edges, is the hit area (the 44px target the header and every row share). The cell only needs
185
225
  a containing block, so :where() keeps that rule at zero specificity: a sticky header or a
186
226
  pinned column's own position always wins (a sticky cell contains the hit area too). */
187
- :global(:where(td:has(> [data-smrt-hit-target]:only-child), th:has(> [data-smrt-hit-target]:only-child))) { position: relative; }
188
- :global(:is(td, th):has(> [data-smrt-hit-target]:only-child)) > .checkbox { position: static; }
189
- :global(:is(td, th):has(> [data-smrt-hit-target]:only-child)) > .checkbox::before { inset: 0; inline-size: auto; block-size: auto; margin: 0; }
190
-
191
- @media (prefers-reduced-motion: reduce) { .checkbox__box { transition: none; } }
192
-
193
- .checkbox[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
194
- .checkbox[data-density='comfortable'] { --smrt-control-target-min: 0px; }
195
- .checkbox { min-height: var(--smrt-control-target-min, 0px); min-width: var(--smrt-control-target-min, 0px); }
227
+ :global(:where(td:has(> [data-smrt-hit-target]:only-child), th:has(> [data-smrt-hit-target]:only-child))) {
228
+ position: relative;
229
+ }
230
+ :global(:is(td, th):has(> [data-smrt-hit-target]:only-child)) > .smrt-checkbox__root {
231
+ position: static;
232
+ }
233
+ :global(:is(td, th):has(> [data-smrt-hit-target]:only-child)) > .smrt-checkbox__root::before {
234
+ inset: 0;
235
+ inline-size: auto;
236
+ block-size: auto;
237
+ margin: 0;
238
+ }
239
+ .smrt-checkbox__root[data-density='touch'] {
240
+ --smrt-checkbox-target-min: var(--smrt-touch-target-min);
241
+ }
242
+ .smrt-checkbox__root[data-density='comfortable'] {
243
+ --smrt-checkbox-target-min: 0px;
244
+ }
245
+ .smrt-checkbox__root {
246
+ min-height: var(--smrt-checkbox-target-min, var(--smrt-control-target-min));
247
+ min-width: var(--smrt-checkbox-target-min, var(--smrt-control-target-min));
248
+ }
249
+ }
250
+ @layer smrt.theme {
251
+ .smrt-checkbox__root {
252
+ color: var(--smrt-color-on-surface);
253
+ }
254
+ .smrt-checkbox__disabled {
255
+ opacity: .5;
256
+ }
257
+ .smrt-checkbox__box {
258
+ border: 2px solid var(--smrt-color-outline);
259
+ border-radius: var(--smrt-radius-extra-small);
260
+ background: var(--smrt-color-surface);
261
+ transition: background var(--smrt-duration-short2), border-color var(--smrt-duration-short2);
262
+ }
263
+ input:checked + .smrt-checkbox__box, input:indeterminate + .smrt-checkbox__box {
264
+ background: var(--smrt-color-primary);
265
+ border-color: var(--smrt-color-primary);
266
+ color: var(--smrt-color-on-primary);
267
+ }
268
+ input:focus-visible + .smrt-checkbox__box {
269
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
270
+ outline-offset: var(--smrt-focus-ring-offset);
271
+ }
272
+ .smrt-checkbox__check {
273
+ font-size: .8rem;
274
+ font-weight: 800;
275
+ line-height: 1;
276
+ }
277
+ .smrt-checkbox__dash {
278
+ background: currentColor;
279
+ }
280
+ .smrt-checkbox__root[data-smrt-highlighted='true'] {
281
+ outline: 3px solid var(--smrt-color-tertiary);
282
+ outline-offset: 4px;
283
+ border-radius: var(--smrt-radius-small);
284
+ }
285
+ @media (prefers-reduced-motion: reduce) {
286
+ .smrt-checkbox__box {
287
+ transition: none;
288
+ }
289
+ }
196
290
  /* Forced colors (#3908): system colours where colour alone carries meaning. */
197
291
  @media (forced-colors: active) {
198
- .checkbox__box { background: Canvas; border-color: CanvasText; }
199
- input:checked + .checkbox__box, input:indeterminate + .checkbox__box { background: Highlight; border-color: Highlight; color: HighlightText; }
200
- .checkbox__dash { background: HighlightText; }
201
- .checkbox--disabled { opacity: 1; color: GrayText; }
202
- .checkbox--disabled .checkbox__box, .checkbox--disabled input:checked + .checkbox__box, .checkbox--disabled input:indeterminate + .checkbox__box { background: Canvas; border-color: GrayText; color: GrayText; }
203
- .checkbox--disabled .checkbox__dash { background: GrayText; }
292
+ .smrt-checkbox__box { background: Canvas; border-color: CanvasText; }
293
+ input:checked + .smrt-checkbox__box, input:indeterminate + .smrt-checkbox__box { background: Highlight; border-color: Highlight; color: HighlightText; }
294
+ .smrt-checkbox__dash { background: HighlightText; }
295
+ .smrt-checkbox__disabled { opacity: 1; color: GrayText; }
296
+ .smrt-checkbox__disabled .smrt-checkbox__box, .smrt-checkbox__disabled input:checked + .smrt-checkbox__box, .smrt-checkbox__disabled input:indeterminate + .smrt-checkbox__box { background: Canvas; border-color: GrayText; color: GrayText; }
297
+ .smrt-checkbox__disabled .smrt-checkbox__dash { background: GrayText; }
204
298
  }
205
299
  }
206
300
  </style>
@@ -350,8 +350,8 @@ useControlRegistration(() => {
350
350
  @layer smrt.reset, smrt.structure, smrt.theme;
351
351
  @layer smrt.theme {
352
352
  .combobox { position: relative; display: grid; gap: 0; color: var(--smrt-color-on-surface); }
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: 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
+ input { box-sizing: border-box; width: 100%; padding: var(--smrt-spacing-2) var(--smrt-spacing-3); border: var(--smrt-hairline-strong); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); color: inherit; }
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: var(--smrt-hairline); 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; }
@@ -36,7 +36,7 @@ const errorId = $derived(error ? `fieldset-${instanceId}-error` : undefined);
36
36
  <style>
37
37
  @layer smrt.reset, smrt.structure, smrt.theme;
38
38
  @layer smrt.theme {
39
- .fieldset { margin: 0; padding: var(--smrt-spacing-4); border: 1px solid var(--smrt-color-outline-variant); border-radius: var(--smrt-radius-medium); }
39
+ .fieldset { margin: 0; padding: var(--smrt-spacing-4); border: var(--smrt-hairline); border-radius: var(--smrt-radius-medium); }
40
40
  legend { padding: 0 var(--smrt-spacing-2); color: var(--smrt-color-on-surface); font: var(--smrt-typography-title-small-font); }
41
41
  .content--stack { display: grid; gap: var(--smrt-spacing-4); }
42
42
  .description, .error { margin: 0 0 var(--smrt-spacing-3); font: var(--smrt-typography-body-small-font); color: var(--smrt-color-on-surface-variant); } .error { margin: var(--smrt-spacing-3) 0 0; color: var(--smrt-color-error); }
@@ -118,7 +118,7 @@ useControlRegistration(() => {
118
118
  <style>
119
119
  @layer smrt.reset, smrt.structure, smrt.theme;
120
120
  @layer smrt.theme {
121
- .file-picker { display: inline-flex; flex-direction: column; gap: var(--smrt-spacing-1); padding: var(--smrt-spacing-3) var(--smrt-spacing-4); border: 1px solid var(--smrt-color-outline); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); color: var(--smrt-color-on-surface); cursor: pointer; }
121
+ .file-picker { display: inline-flex; flex-direction: column; gap: var(--smrt-spacing-1); padding: var(--smrt-spacing-3) var(--smrt-spacing-4); border: var(--smrt-hairline-strong); border-radius: var(--smrt-radius-small); background: var(--smrt-color-surface); color: var(--smrt-color-on-surface); cursor: pointer; }
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; }
@@ -49,7 +49,7 @@ const { t } = useI18n();
49
49
  gap: var(--smrt-spacing-3);
50
50
  margin-block-start: var(--smrt-spacing-6);
51
51
  padding-block-start: var(--smrt-spacing-4);
52
- border-block-start: 1px solid var(--smrt-color-outline-variant);
52
+ border-block-start: var(--smrt-hairline);
53
53
  }
54
54
 
55
55
  .form-action-bar.plain {
@@ -69,7 +69,7 @@ const { t } = useI18n();
69
69
  margin: 0;
70
70
  padding: var(--smrt-spacing-2) var(--smrt-spacing-4)
71
71
  calc(var(--smrt-spacing-2) + env(safe-area-inset-bottom));
72
- border-block-start: 1px solid var(--smrt-color-outline-variant);
72
+ border-block-start: var(--smrt-hairline);
73
73
  background: var(--smrt-color-surface);
74
74
  box-shadow: var(--smrt-elevation-2);
75
75
  }
@@ -213,7 +213,7 @@ export function getElement(): HTMLInputElement | null {
213
213
  aria-label={ariaLabel}
214
214
  aria-describedby={resolvedDescribedBy}
215
215
  aria-invalid={resolvedInvalid}
216
- data-density={density} class="input {className}"
216
+ data-density={density} class="smrt-input__root {className}"
217
217
  data-smrt-control={resolvedControlId}
218
218
  data-smrt-form={controlInteraction?.formId}
219
219
  data-smrt-subject-type={resolvedInteraction === false ? undefined : resolvedInteraction.subject?.type}
@@ -223,66 +223,71 @@ export function getElement(): HTMLInputElement | null {
223
223
 
224
224
  <style>
225
225
  @layer smrt.reset, smrt.structure, smrt.theme;
226
+ @layer smrt.structure {
227
+ .smrt-input__root {
228
+ box-sizing: border-box;
229
+ display: block;
230
+ width: 100%;
231
+ padding: 0.5rem 0.75rem;
232
+ }
233
+ .smrt-input__root:disabled {
234
+ cursor: not-allowed;
235
+ }
236
+ @media (max-width: 48rem) {
237
+ .smrt-input__root {
238
+ min-height: 2.75rem;
239
+ }
240
+ }
241
+ .smrt-input__root[data-density='touch'] {
242
+ --smrt-input-target-min: var(--smrt-touch-target-min);
243
+ }
244
+ .smrt-input__root[data-density='comfortable'] {
245
+ --smrt-input-target-min: 0px;
246
+ }
247
+ .smrt-input__root {
248
+ min-height: max(var(--smrt-control-height), var(--smrt-input-target-min, var(--smrt-control-target-min)));
249
+ }
250
+ }
226
251
  @layer smrt.theme {
227
- .input {
228
- box-sizing: border-box;
229
- display: block;
230
- width: 100%;
231
- padding: 0.5rem 0.75rem;
232
- font-size: var(--smrt-typography-body-medium-size, 0.875rem);
233
- font-weight: var(--smrt-control-weight);
234
- line-height: var(--smrt-typography-body-medium-line-height, 1.5);
235
- color: var(--smrt-color-on-surface);
236
- background-color: var(--smrt-surface-control);
237
- border: var(--smrt-hairline-strong);
238
- border-radius: var(--smrt-control-radius);
239
- transition:
240
- border-color var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease-in-out),
241
- box-shadow var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease-in-out);
242
- }
243
-
244
- .input:focus {
245
- border-color: var(--smrt-color-primary);
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);
251
- }
252
-
253
- .input[data-smrt-highlighted='true'] {
254
- outline: 3px solid var(--smrt-color-tertiary);
255
- outline-offset: 3px;
256
- box-shadow: 0 0 0 6px color-mix(in srgb, var(--smrt-color-tertiary) 20%, transparent);
257
- }
258
-
259
- .input:disabled {
260
- background-color: var(--smrt-color-surface-container-high);
261
- cursor: not-allowed;
262
- opacity: var(--smrt-state-disabled);
263
- }
264
-
265
- .input::placeholder {
266
- color: var(--smrt-color-on-surface-variant);
267
- }
268
-
269
- /* Phones: 16px text so iOS Safari doesn't zoom the page when the field
270
- gets focus, and a 44px touch target. */
271
- @media (max-width: 48rem) {
272
- .input {
273
- font-size: max(var(--smrt-typography-body-medium-size, 0.875rem), 16px);
274
- min-height: 2.75rem;
275
- }
276
- }
277
-
278
- @media (prefers-reduced-motion: reduce) {
279
- .input {
280
- transition: none;
281
- }
282
- }
283
-
284
- .input[data-density='touch'] { --smrt-control-target-min: var(--smrt-touch-target-min); }
285
- .input[data-density='comfortable'] { --smrt-control-target-min: 0px; }
286
- .input { min-height: max(var(--smrt-control-height), var(--smrt-control-target-min, 0px)); }
252
+ .smrt-input__root {
253
+ font-size: var(--smrt-typography-body-medium-size);
254
+ font-weight: var(--smrt-control-weight);
255
+ line-height: var(--smrt-typography-body-medium-line-height);
256
+ color: var(--smrt-color-on-surface);
257
+ background-color: var(--smrt-surface-control);
258
+ border: var(--smrt-hairline-strong);
259
+ border-radius: var(--smrt-control-radius);
260
+ transition: border-color var(--smrt-duration-short2) var(--smrt-easing-standard),
261
+ box-shadow var(--smrt-duration-short2) var(--smrt-easing-standard);
262
+ }
263
+ .smrt-input__root:focus {
264
+ border-color: var(--smrt-color-primary);
265
+ }
266
+ .smrt-input__root:focus-visible {
267
+ outline: var(--smrt-focus-ring-width) solid var(--smrt-focus-ring-color);
268
+ outline-offset: var(--smrt-focus-ring-offset);
269
+ }
270
+ .smrt-input__root[data-smrt-highlighted='true'] {
271
+ outline: 3px solid var(--smrt-color-tertiary);
272
+ outline-offset: 3px;
273
+ box-shadow: 0 0 0 6px color-mix(in srgb, var(--smrt-color-tertiary) 20%, transparent);
274
+ }
275
+ .smrt-input__root:disabled {
276
+ background-color: var(--smrt-color-surface-container-high);
277
+ opacity: var(--smrt-state-disabled);
278
+ }
279
+ .smrt-input__root::placeholder {
280
+ color: var(--smrt-color-on-surface-variant);
281
+ }
282
+ @media (max-width: 48rem) {
283
+ .smrt-input__root {
284
+ font-size: max(var(--smrt-typography-body-medium-size), 16px);
285
+ }
286
+ }
287
+ @media (prefers-reduced-motion: reduce) {
288
+ .smrt-input__root {
289
+ transition: none;
290
+ }
291
+ }
287
292
  }
288
293
  </style>