@threadlabs/looma 0.34.0 → 0.35.0

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 (88) hide show
  1. package/components/ui-badge/ui-badge.html +11 -3
  2. package/components/ui-button/ui-button.html +29 -4
  3. package/components/ui-card/ui-card.html +15 -13
  4. package/components/ui-checkbox/ui-checkbox.html +4 -2
  5. package/components/ui-dialog/ui-dialog.html +2 -0
  6. package/components/ui-icon-button/ui-icon-button.html +22 -8
  7. package/components/ui-input/ui-input.html +2 -1
  8. package/components/ui-input-group/ui-input-group.html +4 -1
  9. package/components/ui-light-editor/ui-light-editor.html +45 -0
  10. package/components/ui-light-editor/ui-light-editor.js +53 -0
  11. package/components/ui-menu/ui-menu.html +2 -0
  12. package/components/ui-number-input/ui-number-input.html +2 -1
  13. package/components/ui-number-input/ui-number-input.js +6 -3
  14. package/components/ui-popover/ui-popover.html +2 -0
  15. package/components/ui-select/ui-select.html +9 -7
  16. package/components/ui-switch/ui-switch.html +3 -0
  17. package/components/ui-textarea/ui-textarea.html +2 -1
  18. package/dist/index.js +24 -23
  19. package/docs/README.md +26 -2
  20. package/docs/catalog.json +566 -44
  21. package/docs/customizer-render.mjs +24 -11
  22. package/docs/customizer.js +67 -17
  23. package/docs/index.d.ts +4 -1
  24. package/docs/index.mjs +1 -1
  25. package/docs/render.mjs +1 -1
  26. package/docs/site.mjs +30 -20
  27. package/docs/theme-session.mjs +4 -2
  28. package/docs/theme.mjs +59 -7
  29. package/package.json +2 -2
  30. package/styles/ui-badge.css +11 -6
  31. package/styles/ui-button.css +29 -4
  32. package/styles/ui-card.css +9 -9
  33. package/styles/ui-checkbox.css +4 -2
  34. package/styles/ui-dialog.css +4 -0
  35. package/styles/ui-icon-button.css +22 -8
  36. package/styles/ui-input-group.css +4 -1
  37. package/styles/ui-input.css +2 -1
  38. package/styles/ui-light-editor.css +28 -0
  39. package/styles/ui-menu.css +2 -0
  40. package/styles/ui-popover.css +2 -0
  41. package/styles/ui-select.css +9 -7
  42. package/styles/ui-switch.css +3 -0
  43. package/styles/ui-textarea.css +2 -1
  44. package/theme-light.css +1 -0
  45. package/theme-system.css +43 -7
  46. package/tokens.css +4 -0
  47. package/vanilla/UiCard.d.ts +2 -1
  48. package/vanilla/UiCard.js +12 -12
  49. package/vanilla/UiLightEditor.d.ts +29 -0
  50. package/vanilla/UiLightEditor.js +102 -0
  51. package/vanilla/UiNumberInput.js +27 -20
  52. package/vanilla/index.js +1 -0
  53. package/vue/UiBadge.vue +29 -4
  54. package/vue/UiButton.vue +33 -4
  55. package/vue/UiCard.d.ts +8 -2
  56. package/vue/UiCard.vue +41 -17
  57. package/vue/UiCheckbox.vue +4 -2
  58. package/vue/UiDialog.vue +4 -0
  59. package/vue/UiIconButton.vue +26 -11
  60. package/vue/UiInput.vue +2 -1
  61. package/vue/UiInputGroup.vue +4 -1
  62. package/vue/UiLightEditor.d.ts +75 -0
  63. package/vue/UiLightEditor.js +2 -0
  64. package/vue/UiLightEditor.vue +402 -0
  65. package/vue/UiMenu.vue +2 -0
  66. package/vue/UiNumberInput.vue +32 -3
  67. package/vue/UiPopover.vue +2 -0
  68. package/vue/UiSelect.vue +16 -10
  69. package/vue/UiSwitch.vue +3 -0
  70. package/vue/UiTextarea.vue +2 -1
  71. package/vue/chunks/UiBadge.js +1 -1
  72. package/vue/chunks/UiButton.js +1 -1
  73. package/vue/chunks/UiCard.js +33 -5
  74. package/vue/chunks/UiCheckbox.js +1 -1
  75. package/vue/chunks/UiDialog.js +1 -1
  76. package/vue/chunks/UiIconButton.js +1 -1
  77. package/vue/chunks/UiInput.js +1 -1
  78. package/vue/chunks/UiInputGroup.js +1 -1
  79. package/vue/chunks/UiLightEditor.js +347 -0
  80. package/vue/chunks/UiMenu.js +1 -1
  81. package/vue/chunks/UiNumberInput.js +21 -8
  82. package/vue/chunks/UiPopover.js +1 -1
  83. package/vue/chunks/UiSelect.js +1 -1
  84. package/vue/chunks/UiSwitch.js +1 -1
  85. package/vue/chunks/UiTextarea.js +1 -1
  86. package/vue/components.css +306 -66
  87. package/vue/index.d.ts +1 -0
  88. package/vue/index.js +2 -1
@@ -0,0 +1,402 @@
1
+ <!-- Generated by HTML Next 1.0.0-alpha.69 for Vue 3.5. Do not edit. -->
2
+ <script setup lang="ts">
3
+ import { computed, getCurrentInstance, provide, ref, useTemplateRef, watchSyncEffect } from 'vue'
4
+ import type { PropType } from 'vue'
5
+ import {
6
+ checkHydratedRoot,
7
+ concat,
8
+ createDispatch,
9
+ nativeAttrs,
10
+ useComponentHost,
11
+ useReflectedProp,
12
+ } from './host'
13
+ import { acceptsControllerWrite } from './props'
14
+ import {
15
+ BINDING_INPUTS_PROP,
16
+ checkedProp,
17
+ checkPropInput,
18
+ type InputBindings,
19
+ propValidityState,
20
+ READY_INPUT,
21
+ requireInputStatus,
22
+ UNRESOLVED_INPUT,
23
+ vPropValidity,
24
+ vuePropInputs,
25
+ } from './props'
26
+ import UiNumberInput from './UiNumberInput.vue'
27
+
28
+ defineOptions({ inheritAttrs: false })
29
+
30
+ const propValidityContract = {
31
+ props: {
32
+ disabled: {
33
+ type: { kind: 'terminal', name: 'boolean' },
34
+ required: false,
35
+ target: { attribute: 'disabled' },
36
+ description: 'Prevents pointer, keyboard and exact edits.',
37
+ default: false,
38
+ },
39
+ label: {
40
+ type: { kind: 'terminal', name: 'string' },
41
+ required: false,
42
+ target: { attribute: 'aria-label' },
43
+ description: 'Accessible name for the position editor.',
44
+ default: 'Light position',
45
+ },
46
+ size: {
47
+ type: { kind: 'terminal', name: 'keyword' },
48
+ required: false,
49
+ target: { attribute: 'size' },
50
+ description:
51
+ 'Sets both coordinate fields to the shared small, medium or large control height.',
52
+ values: ['sm', 'md', 'lg'],
53
+ default: 'md',
54
+ },
55
+ x: {
56
+ type: { kind: 'terminal', name: 'number' },
57
+ required: false,
58
+ target: { attribute: 'x' },
59
+ description:
60
+ 'Horizontal source position from 0 (left) to 1 (right), relative to every surface.',
61
+ default: 0.5,
62
+ },
63
+ y: {
64
+ type: { kind: 'terminal', name: 'number' },
65
+ required: false,
66
+ target: { attribute: 'y' },
67
+ description:
68
+ 'Vertical source position from 0 (top) to 1 (bottom), relative to every surface.',
69
+ default: 0,
70
+ },
71
+ },
72
+ } as const
73
+ const props = defineProps({
74
+ disabled: { type: null as unknown as PropType<boolean | null> },
75
+ label: { type: null as unknown as PropType<string | null> },
76
+ size: { type: null as unknown as PropType<'sm' | 'md' | 'lg' | null> },
77
+ x: { type: null as unknown as PropType<number | null> },
78
+ y: { type: null as unknown as PropType<number | null> },
79
+ [BINDING_INPUTS_PROP]: { type: null as unknown as PropType<InputBindings> },
80
+ })
81
+ const acceptedProps: Record<string, unknown> = {
82
+ disabled: false,
83
+ label: 'Light position',
84
+ size: 'md',
85
+ x: 0.5,
86
+ y: 0,
87
+ }
88
+ const inputTracking = vuePropInputs(acceptedProps, {
89
+ disabled: false,
90
+ label: 'Light position',
91
+ size: 'md',
92
+ x: 0.5,
93
+ y: 0,
94
+ }, (name, value, source, values) => {
95
+ switch (name) {
96
+ case 'disabled':
97
+ return checkPropInput(value, { kind: 'terminal', name: 'boolean' }, true, true)
98
+ case 'label':
99
+ return checkPropInput(value, { kind: 'terminal', name: 'string' }, true, true)
100
+ case 'size':
101
+ return checkPropInput(value, { kind: 'terminal', name: 'keyword' }, true, true)
102
+ case 'x':
103
+ return checkPropInput(value, { kind: 'terminal', name: 'number' }, true, true)
104
+ case 'y':
105
+ return checkPropInput(value, { kind: 'terminal', name: 'number' }, true, true)
106
+ default:
107
+ return { ok: false }
108
+ }
109
+ }, () => {
110
+ void checkedProps.value
111
+ })
112
+ const propInputs = inputTracking.inputs
113
+ const checkedProps = computed(() => {
114
+ void inputTracking.version.value
115
+ return ({
116
+ disabled: propInputs.value<boolean | null>(
117
+ 'disabled',
118
+ props['disabled'],
119
+ props[BINDING_INPUTS_PROP],
120
+ ) as boolean,
121
+ label: propInputs.value<string | null>(
122
+ 'label',
123
+ props['label'],
124
+ props[BINDING_INPUTS_PROP],
125
+ ) as string,
126
+ size: propInputs.value<'sm' | 'md' | 'lg' | null>(
127
+ 'size',
128
+ props['size'],
129
+ props[BINDING_INPUTS_PROP],
130
+ ) as 'sm' | 'md' | 'lg',
131
+ x: propInputs.value<number | null>('x', props['x'], props[BINDING_INPUTS_PROP]) as number,
132
+ y: propInputs.value<number | null>('y', props['y'], props[BINDING_INPUTS_PROP]) as number,
133
+ })
134
+ })
135
+ const inputStatus = (name: string, handlesError: boolean) => {
136
+ void checkedProps.value
137
+ void inputTracking.version.value
138
+ return requireInputStatus(propInputs.status(name), handlesError)
139
+ }
140
+ void checkedProps.value
141
+ watchSyncEffect(() => {
142
+ void checkedProps.value
143
+ })
144
+ const reflectedProp = useReflectedProp()
145
+ const hydrationInstance = getCurrentInstance()
146
+ const emit = defineEmits<{
147
+ 'update:x': [value: number]
148
+ 'update:y': [value: number]
149
+ input: [event: CustomEvent<{ readonly x: number; readonly y: number }>]
150
+ change: [event: CustomEvent<{ readonly x: number; readonly y: number }>]
151
+ }>()
152
+
153
+ const root = ref<HTMLElement | null>(null)
154
+ const planeElement = useTemplateRef<HTMLElement>('planeElement')
155
+ const sourceElement = useTemplateRef<HTMLElement>('sourceElement')
156
+ const horizontal = ref('50')
157
+ const vertical = ref('0')
158
+ let guardedPrevious: any = null
159
+ const guarded = computed(() => {
160
+ const candidate: any = concat(
161
+ checkedProps.value.label,
162
+ '. Arrow keys move the source; Shift moves faster.',
163
+ )
164
+ if (candidate === Symbol.for('html-next.invalid-result')) return guardedPrevious
165
+ return guardedPrevious = candidate
166
+ })
167
+ provide('html-next:ui-light-editor:horizontal', horizontal)
168
+ provide('html-next:ui-light-editor:vertical', vertical)
169
+ checkHydratedRoot('span', 'ui-light-editor')
170
+
171
+ const isInputDetail = (
172
+ detail: unknown,
173
+ ): boolean => (detail !== null && typeof detail === 'object' && !Array.isArray(detail)
174
+ && (typeof (detail as Record<string, unknown>)['x'] === 'number'
175
+ && Number.isFinite((detail as Record<string, unknown>)['x']))
176
+ && (typeof (detail as Record<string, unknown>)['y'] === 'number'
177
+ && Number.isFinite((detail as Record<string, unknown>)['y']))
178
+ && Object.keys(detail as object).every((key) => ['x', 'y'].includes(key)))
179
+ const dispatch = createDispatch(root, emit as (name: string, detail: unknown) => void, {
180
+ checks: {
181
+ input: isInputDetail,
182
+ change: isInputDetail,
183
+ },
184
+ modeled: ['x', 'y'],
185
+ })
186
+
187
+ useComponentHost(() => import('../components/ui-light-editor/ui-light-editor.js'), {
188
+ root,
189
+ module: true,
190
+ dispatch,
191
+ acceptsState: (name: string, keys: readonly string[], value: unknown) =>
192
+ acceptsControllerWrite(
193
+ value,
194
+ ({
195
+ horizontal: { kind: 'terminal', name: 'string' },
196
+ vertical: { kind: 'terminal', name: 'string' },
197
+ } as Record<string, any>)[name],
198
+ keys,
199
+ ),
200
+ initials: { horizontal: '50', vertical: '0' },
201
+ controllerSource: {
202
+ specifier: '../components/ui-light-editor/ui-light-editor.js',
203
+ definition: './components/ui-light-editor/ui-light-editor.html',
204
+ },
205
+ props: checkedProps,
206
+ inputs: propInputs,
207
+ inputTracking,
208
+ propInputs: (name: string) => {
209
+ const incoming = hydrationInstance?.vnode.props
210
+ const kebab = name.replace(/[A-Z]/g, (letter) => '-' + letter.toLowerCase())
211
+ return incoming !== null && incoming !== undefined
212
+ ? (incoming[name] ?? incoming[kebab] ?? null)
213
+ : null
214
+ },
215
+ propNames: ['disabled', 'label', 'size', 'x', 'y'],
216
+ propValidity: (name: string) =>
217
+ propValidityState({ contract: propValidityContract, values: { ...props } }, name),
218
+ refs: { plane: planeElement, source: sourceElement },
219
+ state: { horizontal, vertical },
220
+ })
221
+ </script>
222
+
223
+ <template>
224
+ <span
225
+ class="ui-light-editor"
226
+ v-bind="nativeAttrs($attrs)"
227
+ data-hn
228
+ data-hn-ui-light-editor
229
+ v-prop-validity="{ contract: propValidityContract, values: { ...props } }"
230
+ :data-ui-light-editor-prop-disabled="reflectedProp('disabled', 'disabled', checkedProps.disabled, undefined, false)"
231
+ :data-ui-light-editor-prop-label="reflectedProp('label', 'label', checkedProps.label, undefined, false)"
232
+ :data-ui-light-editor-prop-size="reflectedProp('size', 'size', checkedProps.size, undefined, false)"
233
+ :data-ui-light-editor-prop-x="reflectedProp('x', 'x', checkedProps.x, undefined, false)"
234
+ :data-ui-light-editor-prop-y="reflectedProp('y', 'y', checkedProps.y, undefined, false)"
235
+ ref="root"
236
+ ><span
237
+ class="stage"
238
+ role="group"
239
+ data-hn-ui-light-editor
240
+ :aria-label="checkedProps.label ?? undefined"
241
+ ><span class="plane" data-hn-ui-light-editor ref="planeElement"><svg
242
+ viewBox="0 0 100 60"
243
+ preserveAspectRatio="none"
244
+ aria-hidden="true"
245
+ data-hn-ui-light-editor
246
+ >
247
+ <path d="M0 30 H100 M50 0 V60" data-hn-ui-light-editor></path>
248
+ </svg>{{ ' ' }}<span class="specimen" data-hn-ui-light-editor><slot /></span>{{
249
+ ' '
250
+ }}<button
251
+ type="button"
252
+ class="source"
253
+ data-hn-ui-light-editor
254
+ :disabled="checkedProps.disabled ?? undefined"
255
+ :aria-label="guarded"
256
+ ref="sourceElement"
257
+ >
258
+ </button></span></span>{{ ' ' }}<span
259
+ class="coordinates"
260
+ data-hn-ui-light-editor
261
+ ><UiNumberInput
262
+ class="ui-number-input"
263
+ data-coordinate="x"
264
+ :min="0"
265
+ :max="100"
266
+ :step="1"
267
+ unit="%"
268
+ label="Light horizontal position"
269
+ :value="horizontal"
270
+ :size="checkedProps.size"
271
+ :disabled="checkedProps.disabled"
272
+ v-bind="{
273
+ [BINDING_INPUTS_PROP]: {
274
+ size: () => ({ value: checkedProps.size, status: inputStatus('size', true) }),
275
+ disabled: () => ({ value: checkedProps.disabled, status: inputStatus('disabled', true) }),
276
+ },
277
+ }"
278
+ />{{ ' ' }}<UiNumberInput
279
+ class="ui-number-input"
280
+ data-coordinate="y"
281
+ :min="0"
282
+ :max="100"
283
+ :step="1"
284
+ unit="%"
285
+ label="Light vertical position"
286
+ :value="vertical"
287
+ :size="checkedProps.size"
288
+ :disabled="checkedProps.disabled"
289
+ v-bind="{
290
+ [BINDING_INPUTS_PROP]: {
291
+ size: () => ({ value: checkedProps.size, status: inputStatus('size', true) }),
292
+ disabled: () => ({ value: checkedProps.disabled, status: inputStatus('disabled', true) }),
293
+ },
294
+ }"
295
+ /></span>{{ ' ' }}<span class="hint" data-hn-ui-light-editor
296
+ >Drag the light, use arrow keys, or enter its position. Top centre is 50%, 0%.</span></span>
297
+ </template>
298
+
299
+ <style scoped>
300
+ @scope (.ui-light-editor) to ([data-hn]) {
301
+ :global(:scope:where([data-hn-ui-light-editor])) {
302
+ display: grid !important;
303
+ gap: var(--ui-space-3) !important;
304
+ min-inline-size: 0;
305
+ padding: 0 !important;
306
+ margin: 0 !important;
307
+ }
308
+ :global(.stage:where([data-hn-ui-light-editor])) {
309
+ box-sizing: border-box !important;
310
+ display: block !important;
311
+ block-size: 13rem;
312
+ padding: calc(var(--ui-control-min-block-size) / 2) !important;
313
+ border: var(--ui-border-width) solid var(--ui-control-border);
314
+ border-radius: var(--ui-field-radius, var(--ui-radius-md));
315
+ background: var(--ui-neutral-surface, var(--ui-surface));
316
+ }
317
+ :global(.plane:where([data-hn-ui-light-editor])) {
318
+ position: relative;
319
+ display: block !important;
320
+ inline-size: 100%;
321
+ block-size: 100%;
322
+ }
323
+ :global(svg:where([data-hn-ui-light-editor])) {
324
+ inline-size: 100%;
325
+ block-size: 100%;
326
+ overflow: visible;
327
+ }
328
+ :global(path:where([data-hn-ui-light-editor])) {
329
+ fill: none;
330
+ stroke: var(--ui-border);
331
+ stroke-width: 1;
332
+ vector-effect: non-scaling-stroke;
333
+ }
334
+ :global(.specimen:where([data-hn-ui-light-editor])) {
335
+ position: absolute;
336
+ inset: 0;
337
+ display: grid !important;
338
+ place-items: center !important;
339
+ }
340
+ :global(.source:where([data-hn-ui-light-editor])) {
341
+ position: absolute;
342
+ left: 50%;
343
+ top: 0;
344
+ translate: -50% -50%;
345
+ display: grid !important;
346
+ place-items: center !important;
347
+ inline-size: max(2rem, var(--ui-control-min-block-size));
348
+ block-size: max(2rem, var(--ui-control-min-block-size));
349
+ padding: 0 !important;
350
+ margin: 0 !important;
351
+ border: 0;
352
+ border-radius: 50%;
353
+ background: transparent;
354
+ cursor: grab;
355
+ touch-action: none;
356
+ }
357
+ :global(.source:where([data-hn-ui-light-editor])::after) {
358
+ content: "";
359
+ inline-size: 1rem;
360
+ block-size: 1rem;
361
+ border: var(--ui-border-width) solid var(--ui-accent);
362
+ border-radius: 50%;
363
+ background: var(--ui-surface-raised);
364
+ box-shadow:
365
+ 0 0 0 var(--ui-space-2) color-mix(in srgb, var(--ui-accent) 12%, transparent),
366
+ var(--ui-shadow-sm);
367
+ }
368
+ :global(.source:active:where([data-hn-ui-light-editor])) {
369
+ cursor: grabbing;
370
+ }
371
+ :global(.source:focus-visible:where([data-hn-ui-light-editor])) {
372
+ outline: var(--ui-focus-width) solid var(--ui-focus-ring);
373
+ outline-offset: var(--ui-space-1);
374
+ }
375
+ :global(.source:disabled:where([data-hn-ui-light-editor])) {
376
+ cursor: not-allowed;
377
+ }
378
+ :global(.source:disabled:where([data-hn-ui-light-editor])::after) {
379
+ background: var(--ui-disabled-surface);
380
+ border-color: var(--ui-disabled-border);
381
+ box-shadow: none;
382
+ }
383
+ :global(.coordinates:where([data-hn-ui-light-editor])) {
384
+ display: grid !important;
385
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
386
+ gap: var(--ui-space-2) !important;
387
+ }
388
+ :global(.hint:where([data-hn-ui-light-editor])) {
389
+ font-size: var(--ui-font-size-sm);
390
+ color: var(--ui-text-secondary);
391
+ }
392
+ @media (forced-colors: active) {
393
+ :global(.source:where([data-hn-ui-light-editor])::after) {
394
+ background: Canvas;
395
+ border-color: CanvasText;
396
+ }
397
+ :global(.source:focus-visible:where([data-hn-ui-light-editor])) {
398
+ outline-color: Highlight;
399
+ }
400
+ }
401
+ }
402
+ </style>
package/vue/UiMenu.vue CHANGED
@@ -451,6 +451,7 @@ useComponentHost(() => import('../components/ui-menu/ui-menu.js'), {
451
451
  transparent
452
452
  );
453
453
  background:
454
+ var(--ui-surface-highlight, linear-gradient(transparent, transparent)) padding-box,
454
455
  linear-gradient(var(--_ui-material-content-color), var(--_ui-material-content-color))
455
456
  padding-box,
456
457
  linear-gradient(var(--_ui-material-rim-color), var(--_ui-material-rim-color)) border-box,
@@ -473,6 +474,7 @@ useComponentHost(() => import('../components/ui-menu/ui-menu.js'), {
473
474
  0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 0.28) * 8px) calc(
474
475
  var(--_ui-material-separation) * var(--_ui-material-scale, 0.28) * 24px
475
476
  ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
477
+ var(--ui-material-glow, 0 0 transparent),
476
478
  inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(
477
479
  --ui-material-edge,
478
480
  var(--ui-border-strong)
@@ -1,19 +1,23 @@
1
1
  <!-- Generated by HTML Next 1.0.0-alpha.69 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
- import { computed, getCurrentInstance, ref, watchSyncEffect } from 'vue'
3
+ import { computed, getCurrentInstance, provide, ref, watchSyncEffect } from 'vue'
4
4
  import type { PropType } from 'vue'
5
5
  import {
6
6
  checkHydratedRoot,
7
7
  concat,
8
8
  createDispatch,
9
+ initializedState,
9
10
  nativeAttrs,
10
11
  useComponentHost,
11
12
  useReflectedProp,
13
+ warnUnless,
12
14
  } from './host'
15
+ import { acceptsControllerWrite } from './props'
13
16
  import {
14
17
  BINDING_INPUTS_PROP,
15
18
  checkedProp,
16
19
  checkPropInput,
20
+ copyStateSeed,
17
21
  evaluateInput,
18
22
  type InputBindings,
19
23
  inputDefault,
@@ -224,6 +228,23 @@ const emit = defineEmits<{
224
228
  }>()
225
229
 
226
230
  const root = ref<HTMLElement | null>(null)
231
+ const display = initializedState<string>(
232
+ () =>
233
+ evaluateInput(() => {
234
+ return inputRead(checkedProps.value.value, propInputs.status('value'))
235
+ }),
236
+ (next: unknown) => typeof next === 'string',
237
+ copyStateSeed,
238
+ () =>
239
+ warnUnless(false, 'initializer:display', 'State `display` does not satisfy its declared type.'),
240
+ )
241
+ provide('html-next:ui-number-input:display', display)
242
+ const declarationStatuses = { display: () => display.status() } as Record<
243
+ string,
244
+ () => typeof READY_INPUT
245
+ >
246
+ const declarationStatus = (name: string) => declarationStatuses[name]!()
247
+ display.start()
227
248
  checkHydratedRoot('span', 'ui-number-input')
228
249
 
229
250
  const isInputDetail = (
@@ -243,6 +264,12 @@ useComponentHost(() => import('../components/ui-number-input/ui-number-input.js'
243
264
  root,
244
265
  module: true,
245
266
  dispatch,
267
+ acceptsState: (name: string, keys: readonly string[], value: unknown) =>
268
+ acceptsControllerWrite(
269
+ value,
270
+ ({ display: { kind: 'terminal', name: 'string' } } as Record<string, any>)[name],
271
+ keys,
272
+ ),
246
273
  controllerSource: {
247
274
  specifier: '../components/ui-number-input/ui-number-input.js',
248
275
  definition: './components/ui-number-input/ui-number-input.html',
@@ -260,6 +287,8 @@ useComponentHost(() => import('../components/ui-number-input/ui-number-input.js'
260
287
  propNames: ['disabled', 'label', 'max', 'min', 'name', 'size', 'step', 'unit', 'value'],
261
288
  propValidity: (name: string) =>
262
289
  propValidityState({ contract: propValidityContract, values: { ...props } }, name),
290
+ state: { display },
291
+ availability: { display: () => declarationStatus('display') },
263
292
  })
264
293
  </script>
265
294
 
@@ -307,14 +336,14 @@ useComponentHost(() => import('../components/ui-number-input/ui-number-input.js'
307
336
  aria-description="Enter calculates arithmetic. Arrow keys step; Shift changes ten steps."
308
337
  :size="checkedProps.size"
309
338
  :disabled="checkedProps.disabled"
310
- :value="checkedProps.value"
339
+ :value="display"
311
340
  :aria-label="checkedProps.label"
312
341
  :name="checkedProps.name"
313
342
  v-bind="{
314
343
  [BINDING_INPUTS_PROP]: {
315
344
  size: () => ({ value: checkedProps.size, status: inputStatus('size', true) }),
316
345
  disabled: () => ({ value: checkedProps.disabled, status: inputStatus('disabled', true) }),
317
- value: () => ({ value: checkedProps.value, status: inputStatus('value', true) }),
346
+ value: () => ({ value: display, status: declarationStatus('display') }),
318
347
  },
319
348
  }"
320
349
  />{{ ' ' }}<template
package/vue/UiPopover.vue CHANGED
@@ -402,6 +402,7 @@ useComponentHost(() => import('../components/ui-popover/ui-popover.js'), {
402
402
  transparent
403
403
  );
404
404
  background:
405
+ var(--ui-surface-highlight, linear-gradient(transparent, transparent)) padding-box,
405
406
  linear-gradient(var(--_ui-material-content-color), var(--_ui-material-content-color))
406
407
  padding-box,
407
408
  linear-gradient(var(--_ui-material-rim-color), var(--_ui-material-rim-color)) border-box,
@@ -424,6 +425,7 @@ useComponentHost(() => import('../components/ui-popover/ui-popover.js'), {
424
425
  0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 0.55) * 8px) calc(
425
426
  var(--_ui-material-separation) * var(--_ui-material-scale, 0.55) * 24px
426
427
  ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
428
+ var(--ui-material-glow, 0 0 transparent),
427
429
  inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(
428
430
  --ui-material-edge,
429
431
  var(--ui-border-strong)
package/vue/UiSelect.vue CHANGED
@@ -296,7 +296,7 @@ useComponentHost(() => import('../components/ui-select/ui-select.js'), {
296
296
  var(--ui-control-surface, var(--ui-surface-elevated))
297
297
  );
298
298
  color: var(--ui-text-primary);
299
- color-scheme: light dark;
299
+ color-scheme: inherit;
300
300
  font: inherit;
301
301
  line-height: var(--ui-line-height-tight);
302
302
  box-shadow: var(--ui-select-shadow, var(--ui-shadow-sm));
@@ -306,15 +306,18 @@ useComponentHost(() => import('../components/ui-select/ui-select.js'), {
306
306
  background-color var(--ui-motion-fast) var(--ui-motion-ease);
307
307
  }
308
308
  :global(:scope:where([data-hn-ui-select])) {
309
+ --_ui-select-highlight: var(--ui-surface-highlight, linear-gradient(transparent, transparent));
309
310
  appearance: none;
310
311
  padding-inline-end: calc(var(--ui-space-3) + 1.5rem) !important;
311
- background-image: var(
312
- --ui-select-chevron,
313
- url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235c5c58' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
314
- );
312
+ background-image:
313
+ var(
314
+ --ui-select-chevron,
315
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235c5c58' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
316
+ ),
317
+ var(--_ui-select-highlight);
315
318
  background-repeat: no-repeat;
316
- background-position: right var(--ui-space-3) center;
317
- background-size: 1.125rem 1.125rem;
319
+ background-position: right var(--ui-space-3) center, center;
320
+ background-size: 1.125rem 1.125rem, 100% 100%;
318
321
  }
319
322
 
320
323
  :global(:scope:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(:is(:user-invalid, [data-user-invalid])):where([data-hn-ui-select])) {
@@ -351,6 +354,7 @@ useComponentHost(() => import('../components/ui-select/ui-select.js'), {
351
354
  }
352
355
 
353
356
  :global(:scope:disabled:where([data-hn-ui-select])) {
357
+ --_ui-select-highlight: none;
354
358
  background-color: var(--ui-control-surface-disabled, var(--ui-surface-muted));
355
359
  color: var(--ui-disabled-text);
356
360
  box-shadow: none;
@@ -362,8 +366,10 @@ useComponentHost(() => import('../components/ui-select/ui-select.js'), {
362
366
  padding: var(--ui-control-inset-block-sm, var(--ui-space-1)) var(--ui-space-2) !important;
363
367
  padding-inline-end: calc(var(--ui-space-2) + var(--ui-icon-size-sm) + var(--ui-space-1))
364
368
  !important;
365
- background-position: right var(--ui-space-2) center;
366
- background-size: var(--ui-icon-size-sm) var(--ui-icon-size-sm);
369
+ background-position: right var(--ui-space-2) center, center;
370
+ background-size:
371
+ var(--ui-icon-size-sm) var(--ui-icon-size-sm),
372
+ 100% 100%;
367
373
  font-size: var(--ui-font-size-sm);
368
374
  }
369
375
 
@@ -371,7 +377,7 @@ useComponentHost(() => import('../components/ui-select/ui-select.js'), {
371
377
  min-block-size: var(--ui-control-size-lg);
372
378
  padding: var(--ui-control-inset-block-lg, var(--ui-space-3)) var(--ui-space-4) !important;
373
379
  padding-inline-end: calc(var(--ui-space-4) + 1.5rem) !important;
374
- background-position: right var(--ui-space-4) center;
380
+ background-position: right var(--ui-space-4) center, center;
375
381
  font-size: var(--ui-font-size-md);
376
382
  }
377
383
  @media (pointer: coarse) {
package/vue/UiSwitch.vue CHANGED
@@ -375,6 +375,7 @@ useComponentHost(() => import('../components/ui-switch/ui-switch.js'), {
375
375
  border: var(--ui-border-width) solid var(--ui-control-border, var(--ui-border-strong));
376
376
  border-radius: var(--ui-radius-round);
377
377
  background-color: var(--ui-control-surface, var(--ui-surface-default));
378
+ background-image: var(--ui-surface-highlight, none);
378
379
  box-shadow: var(--ui-control-inset);
379
380
  cursor: pointer;
380
381
  transition:
@@ -400,6 +401,7 @@ useComponentHost(() => import('../components/ui-switch/ui-switch.js'), {
400
401
 
401
402
  :global(input:checked:where([data-hn-ui-switch])) {
402
403
  background-color: var(--ui-accent-solid);
404
+ background-image: var(--ui-accent-highlight, none);
403
405
  border-color: var(--ui-accent-solid);
404
406
  }
405
407
 
@@ -423,6 +425,7 @@ useComponentHost(() => import('../components/ui-switch/ui-switch.js'), {
423
425
  :global(:scope[data-ui-switch-state~="disabled"] input:where([data-hn-ui-switch])) {
424
426
  border-color: var(--ui-disabled-border);
425
427
  background-color: var(--ui-disabled-surface);
428
+ background-image: none;
426
429
  box-shadow: none;
427
430
  cursor: not-allowed;
428
431
  }
@@ -393,10 +393,11 @@ useComponentHost(() => import('../components/ui-textarea/ui-textarea.js'), {
393
393
  --ui-textarea-radius,
394
394
  var(--_ui-default-textarea-radius, var(--ui-field-radius, var(--ui-radius-md)))
395
395
  );
396
- background: var(
396
+ background-color: var(
397
397
  --ui-textarea-surface,
398
398
  var(--_ui-default-textarea-surface, var(--ui-control-surface, var(--ui-surface-elevated)))
399
399
  );
400
+ background-image: var(--_ui-default-textarea-highlight, var(--ui-surface-highlight, none));
400
401
  color: var(--ui-text-primary);
401
402
  font: inherit;
402
403
  line-height: var(--ui-line-height-md);
@@ -181,6 +181,6 @@ var UiBadge_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
181
181
  }]]);
182
182
  };
183
183
  }
184
- }), [["__scopeId", "data-v-8fcdd98a"]]);
184
+ }), [["__scopeId", "data-v-2cdbe261"]]);
185
185
  //#endregion
186
186
  export { UiBadge_default as t };
@@ -866,6 +866,6 @@ var UiButton_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
866
866
  }]]);
867
867
  };
868
868
  }
869
- }), [["__scopeId", "data-v-ec56b67f"]]);
869
+ }), [["__scopeId", "data-v-bb40b79e"]]);
870
870
  //#endregion
871
871
  export { UiButton_default as t };