@threadlabs/looma 0.33.1 → 0.34.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 (184) hide show
  1. package/components/shared/overlay.js +8 -2
  2. package/components/shared/surface-material.js +21 -0
  3. package/components/ui-button/ui-button.html +63 -47
  4. package/components/ui-card/ui-card.html +44 -2
  5. package/components/ui-curve-editor/curve.js +10 -0
  6. package/components/ui-curve-editor/ui-curve-editor.html +181 -0
  7. package/components/ui-curve-editor/ui-curve-editor.js +51 -0
  8. package/components/ui-dialog/ui-dialog.html +62 -1
  9. package/components/ui-dialog/ui-dialog.js +9 -1
  10. package/components/ui-icon-button/ui-icon-button.html +75 -23
  11. package/components/ui-menu/ui-menu.html +60 -0
  12. package/components/ui-menu/ui-menu.js +1 -1
  13. package/components/ui-number-input/arithmetic.js +47 -0
  14. package/components/ui-number-input/ui-number-input.html +62 -0
  15. package/components/ui-number-input/ui-number-input.js +44 -0
  16. package/components/ui-popover/ui-popover.html +64 -1
  17. package/components/ui-popover/ui-popover.js +1 -1
  18. package/components/ui-slider/ui-slider.html +84 -0
  19. package/components/ui-slider/ui-slider.js +29 -0
  20. package/components/ui-tabs/ui-tabs.html +6 -1
  21. package/components/ui-text/ui-text.html +31 -6
  22. package/components/ui-tooltip/ui-tooltip.html +60 -0
  23. package/components/ui-tooltip/ui-tooltip.js +1 -1
  24. package/dist/index.js +46 -43
  25. package/docs/README.md +34 -4
  26. package/docs/card.html +16 -2
  27. package/docs/card.js +17 -2
  28. package/docs/catalog.json +1154 -144
  29. package/docs/customizer-render.mjs +35 -9
  30. package/docs/customizer.js +83 -20
  31. package/docs/documentation.css +9 -1
  32. package/docs/index.d.ts +4 -2
  33. package/docs/index.mjs +1 -1
  34. package/docs/preview.js +2 -0
  35. package/docs/render.mjs +15 -6
  36. package/docs/site.mjs +50 -9
  37. package/docs/theme-session.mjs +51 -0
  38. package/docs/theme.mjs +134 -15
  39. package/docs/value-editors.mjs +33 -0
  40. package/package.json +2 -2
  41. package/styles/ui-button.css +45 -45
  42. package/styles/ui-card.css +47 -0
  43. package/styles/ui-curve-editor.css +109 -0
  44. package/styles/ui-dialog.css +132 -0
  45. package/styles/ui-icon-button.css +63 -20
  46. package/styles/ui-menu.css +72 -0
  47. package/styles/ui-number-input.css +34 -0
  48. package/styles/ui-popover.css +74 -0
  49. package/styles/ui-slider.css +53 -0
  50. package/styles/ui-tabs.css +6 -1
  51. package/styles/ui-text.css +23 -1
  52. package/styles/ui-tooltip.css +72 -0
  53. package/theme-system.css +26 -10
  54. package/tokens.css +8 -2
  55. package/vanilla/UiButton.d.ts +1 -0
  56. package/vanilla/UiButton.js +195 -130
  57. package/vanilla/UiCard.d.ts +1 -1
  58. package/vanilla/UiCard.js +1 -1
  59. package/vanilla/UiCurveEditor.d.ts +28 -0
  60. package/vanilla/UiCurveEditor.js +134 -0
  61. package/vanilla/UiIconButton.d.ts +2 -1
  62. package/vanilla/UiIconButton.js +58 -31
  63. package/vanilla/UiNumberInput.d.ts +33 -0
  64. package/vanilla/UiNumberInput.js +79 -0
  65. package/vanilla/UiSlider.d.ts +33 -0
  66. package/vanilla/UiSlider.js +125 -0
  67. package/vanilla/UiText.d.ts +2 -2
  68. package/vanilla/UiText.js +45 -11
  69. package/vanilla/index.js +3 -0
  70. package/vue/UiButton.d.ts +6 -0
  71. package/vue/UiButton.vue +155 -56
  72. package/vue/UiCard.d.ts +2 -2
  73. package/vue/UiCard.vue +55 -5
  74. package/vue/UiCurveEditor.d.ts +53 -0
  75. package/vue/UiCurveEditor.js +2 -0
  76. package/vue/UiCurveEditor.vue +427 -0
  77. package/vue/UiDialog.vue +177 -1
  78. package/vue/UiIconButton.d.ts +8 -2
  79. package/vue/UiIconButton.vue +164 -34
  80. package/vue/UiMenu.vue +89 -0
  81. package/vue/UiNumberInput.d.ts +83 -0
  82. package/vue/UiNumberInput.js +2 -0
  83. package/vue/UiNumberInput.vue +370 -0
  84. package/vue/UiPopover.vue +92 -1
  85. package/vue/UiSlider.d.ts +83 -0
  86. package/vue/UiSlider.js +2 -0
  87. package/vue/UiSlider.vue +412 -0
  88. package/vue/UiTabs.vue +6 -1
  89. package/vue/UiText.d.ts +7 -5
  90. package/vue/UiText.vue +104 -22
  91. package/vue/UiTooltip.vue +89 -0
  92. package/vue/chunks/UiActionBar.js +1 -1
  93. package/vue/chunks/UiAffordanceScope.js +2 -2
  94. package/vue/chunks/UiAvatar.js +2 -2
  95. package/vue/chunks/UiAvatarGroup.js +2 -2
  96. package/vue/chunks/UiBadge.js +2 -2
  97. package/vue/chunks/UiBreadcrumbItem.js +2 -2
  98. package/vue/chunks/UiBreadcrumbs.js +2 -2
  99. package/vue/chunks/UiButton.js +94 -11
  100. package/vue/chunks/UiCallout.js +2 -2
  101. package/vue/chunks/UiCard.js +6 -5
  102. package/vue/chunks/UiCheckbox.js +2 -2
  103. package/vue/chunks/UiCluster.js +2 -2
  104. package/vue/chunks/UiColorPicker.js +2 -2
  105. package/vue/chunks/UiCombobox.js +2 -2
  106. package/vue/chunks/UiContainer.js +2 -2
  107. package/vue/chunks/UiContextMenu.js +2 -2
  108. package/vue/chunks/UiCover.js +2 -2
  109. package/vue/chunks/UiCurveEditor.js +413 -0
  110. package/vue/chunks/UiDescriptionItem.js +2 -2
  111. package/vue/chunks/UiDescriptionList.js +2 -2
  112. package/vue/chunks/UiDialog.js +4 -4
  113. package/vue/chunks/UiDisclosure.js +2 -2
  114. package/vue/chunks/UiEditable.js +2 -2
  115. package/vue/chunks/UiEditorBlockActions.js +2 -2
  116. package/vue/chunks/UiEditorChipEditor.js +2 -2
  117. package/vue/chunks/UiEditorCodeLanguage.js +2 -2
  118. package/vue/chunks/UiEditorFootnoteEditor.js +2 -2
  119. package/vue/chunks/UiEditorImageToolbar.js +3 -3
  120. package/vue/chunks/UiEditorInsertTableGrid.js +2 -2
  121. package/vue/chunks/UiEditorLinkCard.js +3 -3
  122. package/vue/chunks/UiEditorLinkForm.js +3 -3
  123. package/vue/chunks/UiEditorMentionMenu.js +2 -2
  124. package/vue/chunks/UiEditorMentionMenuItem.js +2 -2
  125. package/vue/chunks/UiEditorSlashMenu.js +2 -2
  126. package/vue/chunks/UiEditorSlashMenuGroup.js +2 -2
  127. package/vue/chunks/UiEditorSlashMenuItem.js +2 -2
  128. package/vue/chunks/UiEditorSurface.js +2 -2
  129. package/vue/chunks/UiEditorTableContextMenu.js +2 -2
  130. package/vue/chunks/UiEditorTableOverlay.js +2 -2
  131. package/vue/chunks/UiEditorTableToolbar.js +2 -2
  132. package/vue/chunks/UiEditorToolbar.js +2 -2
  133. package/vue/chunks/UiFloatingActionButton.js +2 -2
  134. package/vue/chunks/UiFormField.js +2 -2
  135. package/vue/chunks/UiGrid.js +2 -2
  136. package/vue/chunks/UiIcon.js +2 -2
  137. package/vue/chunks/UiIconButton.js +77 -11
  138. package/vue/chunks/UiImage.js +2 -2
  139. package/vue/chunks/UiInput.js +2 -2
  140. package/vue/chunks/UiInputGroup.js +2 -2
  141. package/vue/chunks/UiList.js +2 -2
  142. package/vue/chunks/UiListItem.js +2 -2
  143. package/vue/chunks/UiListbox.js +2 -2
  144. package/vue/chunks/UiMenu.js +3 -3
  145. package/vue/chunks/UiMenuGroup.js +2 -2
  146. package/vue/chunks/UiMenuItem.js +2 -2
  147. package/vue/chunks/UiMeter.js +2 -2
  148. package/vue/chunks/UiNavItem.js +2 -2
  149. package/vue/chunks/UiNumberInput.js +390 -0
  150. package/vue/chunks/UiPageHeader.js +2 -2
  151. package/vue/chunks/UiPopover.js +4 -4
  152. package/vue/chunks/UiRadio.js +2 -2
  153. package/vue/chunks/UiRadioGroup.js +2 -2
  154. package/vue/chunks/UiReel.js +2 -2
  155. package/vue/chunks/UiScrollArea.js +2 -2
  156. package/vue/chunks/UiSearchResultRow.js +2 -2
  157. package/vue/chunks/UiSearchShell.js +2 -2
  158. package/vue/chunks/UiSection.js +2 -2
  159. package/vue/chunks/UiSelect.js +2 -2
  160. package/vue/chunks/UiSeparator.js +2 -2
  161. package/vue/chunks/UiSidebar.js +2 -2
  162. package/vue/chunks/UiSlider.js +396 -0
  163. package/vue/chunks/UiSpinner.js +2 -2
  164. package/vue/chunks/UiStack.js +2 -2
  165. package/vue/chunks/UiStatusMessage.js +2 -2
  166. package/vue/chunks/UiSwatch.js +2 -2
  167. package/vue/chunks/UiSwitch.js +2 -2
  168. package/vue/chunks/UiSwitcher.js +2 -2
  169. package/vue/chunks/UiTable.js +2 -2
  170. package/vue/chunks/UiTabs.js +3 -3
  171. package/vue/chunks/UiText.js +53 -12
  172. package/vue/chunks/UiTextarea.js +2 -2
  173. package/vue/chunks/UiToast.js +2 -2
  174. package/vue/chunks/UiToastRegion.js +2 -2
  175. package/vue/chunks/UiTooltip.js +3 -3
  176. package/vue/chunks/UiTopBar.js +1 -1
  177. package/vue/chunks/UiTree.js +2 -2
  178. package/vue/chunks/UiTreeItem.js +2 -2
  179. package/vue/chunks/_plugin-vue_export-helper.js +68 -2
  180. package/vue/chunks/props.js +24 -1
  181. package/vue/components.css +2215 -1203
  182. package/vue/editor/index.js +623 -211
  183. package/vue/index.d.ts +3 -0
  184. package/vue/index.js +5 -2
@@ -7,6 +7,7 @@ import {
7
7
  checkHydratedRoot,
8
8
  concat,
9
9
  createDispatch,
10
+ cycleCheckedComputed,
10
11
  nativeAttrs,
11
12
  useComponentHost,
12
13
  useReflectedProp,
@@ -16,6 +17,9 @@ import {
16
17
  checkedProp,
17
18
  checkPropInput,
18
19
  type InputBindings,
20
+ inputComputed,
21
+ inputDefault,
22
+ inputRead,
19
23
  propValidityState,
20
24
  READY_INPUT,
21
25
  requireInputStatus,
@@ -83,6 +87,15 @@ const propValidityContract = {
83
87
  description: 'Deprecated alias for loading.',
84
88
  default: false,
85
89
  },
90
+ preset: {
91
+ type: { kind: 'terminal', name: 'keyword' },
92
+ required: false,
93
+ target: { attribute: 'preset' },
94
+ description:
95
+ 'The same shortcuts as Button: primary is solid accent, standard is solid neutral, quiet is ghost neutral, and destructive is solid danger. A named preset\n wins over variant and tone; none preserves independent choices.',
96
+ values: ['none', 'primary', 'standard', 'quiet', 'destructive'],
97
+ default: 'none',
98
+ },
86
99
  revealed: {
87
100
  type: { kind: 'terminal', name: 'boolean' },
88
101
  required: false,
@@ -112,8 +125,9 @@ const propValidityContract = {
112
125
  required: false,
113
126
  target: { attribute: 'tone' },
114
127
  description:
115
- 'Colour for every variant. When omitted, ghost and outline keep their neutral appearance and solid keeps its accent fill. Set accent for a coloured action\n or neutral for a quiet one. Native aria-pressed="true" keeps an outline toggle visibly pressed; the consumer owns that state.',
116
- values: ['accent', 'neutral'],
128
+ 'Colour for every variant. auto preserves the existing variant-specific default. When omitted, ghost and outline keep their neutral appearance and solid\n keeps its accent fill. Set accent for a coloured action or neutral for a quiet one. danger, success, warning and info share Button\'s intent colors. Native\n aria-pressed="true" keeps an outline toggle visibly pressed; the consumer owns that state.',
129
+ values: ['auto', 'accent', 'neutral', 'danger', 'success', 'warning', 'info'],
130
+ default: 'auto',
117
131
  },
118
132
  variant: {
119
133
  type: { kind: 'terminal', name: 'keyword' },
@@ -134,10 +148,19 @@ const props = defineProps({
134
148
  loading: { type: null as unknown as PropType<boolean | null> },
135
149
  matchButton: { type: null as unknown as PropType<boolean | null> },
136
150
  pending: { type: null as unknown as PropType<boolean | null> },
151
+ preset: {
152
+ type: null as unknown as PropType<
153
+ 'none' | 'primary' | 'standard' | 'quiet' | 'destructive' | null
154
+ >,
155
+ },
137
156
  revealed: { type: null as unknown as PropType<boolean | null> },
138
157
  round: { type: null as unknown as PropType<boolean | null> },
139
158
  size: { type: null as unknown as PropType<'sm' | 'md' | 'lg' | null> },
140
- tone: { type: null as unknown as PropType<'accent' | 'neutral' | null> },
159
+ tone: {
160
+ type: null as unknown as PropType<
161
+ 'auto' | 'accent' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' | null
162
+ >,
163
+ },
141
164
  variant: { type: null as unknown as PropType<'ghost' | 'outline' | 'solid' | null> },
142
165
  [BINDING_INPUTS_PROP]: { type: null as unknown as PropType<InputBindings> },
143
166
  })
@@ -149,10 +172,11 @@ const acceptedProps: Record<string, unknown> = {
149
172
  loading: false,
150
173
  matchButton: false,
151
174
  pending: false,
175
+ preset: 'none',
152
176
  revealed: false,
153
177
  round: false,
154
178
  size: 'md',
155
- tone: null,
179
+ tone: 'auto',
156
180
  variant: 'ghost',
157
181
  }
158
182
  const inputTracking = vuePropInputs(acceptedProps, {
@@ -162,9 +186,11 @@ const inputTracking = vuePropInputs(acceptedProps, {
162
186
  loading: false,
163
187
  matchButton: false,
164
188
  pending: false,
189
+ preset: 'none',
165
190
  revealed: false,
166
191
  round: false,
167
192
  size: 'md',
193
+ tone: 'auto',
168
194
  variant: 'ghost',
169
195
  }, (name, value, source, values) => {
170
196
  switch (name) {
@@ -182,6 +208,8 @@ const inputTracking = vuePropInputs(acceptedProps, {
182
208
  return checkPropInput(value, { kind: 'terminal', name: 'boolean' }, true, true)
183
209
  case 'pending':
184
210
  return checkPropInput(value, { kind: 'terminal', name: 'boolean' }, true, true)
211
+ case 'preset':
212
+ return checkPropInput(value, { kind: 'terminal', name: 'keyword' }, true, true)
185
213
  case 'revealed':
186
214
  return checkPropInput(value, { kind: 'terminal', name: 'boolean' }, true, true)
187
215
  case 'round':
@@ -233,6 +261,11 @@ const checkedProps = computed(() => {
233
261
  props['pending'],
234
262
  props[BINDING_INPUTS_PROP],
235
263
  ) as boolean,
264
+ preset: propInputs.value<'none' | 'primary' | 'standard' | 'quiet' | 'destructive' | null>(
265
+ 'preset',
266
+ props['preset'],
267
+ props[BINDING_INPUTS_PROP],
268
+ ) as 'none' | 'primary' | 'standard' | 'quiet' | 'destructive',
236
269
  revealed: propInputs.value<boolean | null>(
237
270
  'revealed',
238
271
  props['revealed'],
@@ -248,11 +281,16 @@ const checkedProps = computed(() => {
248
281
  props['size'],
249
282
  props[BINDING_INPUTS_PROP],
250
283
  ) as 'sm' | 'md' | 'lg',
251
- tone: propInputs.value<'accent' | 'neutral' | null>(
252
- 'tone',
253
- props['tone'],
254
- props[BINDING_INPUTS_PROP],
255
- ),
284
+ tone: propInputs.value<
285
+ 'auto' | 'accent' | 'neutral' | 'danger' | 'success' | 'warning' | 'info' | null
286
+ >('tone', props['tone'], props[BINDING_INPUTS_PROP]) as
287
+ | 'auto'
288
+ | 'accent'
289
+ | 'neutral'
290
+ | 'danger'
291
+ | 'success'
292
+ | 'warning'
293
+ | 'info',
256
294
  variant: propInputs.value<'ghost' | 'outline' | 'solid' | null>(
257
295
  'variant',
258
296
  props['variant'],
@@ -273,6 +311,38 @@ const reflectedProp = useReflectedProp()
273
311
  const hydrationInstance = getCurrentInstance()
274
312
 
275
313
  const root = ref<HTMLElement | null>(null)
314
+ const appearance: ReturnType<typeof inputComputed<any>> = inputComputed<any>(
315
+ () =>
316
+ (({
317
+ none: inputRead(checkedProps.value.variant, propInputs.status('variant')),
318
+ primary: 'solid',
319
+ standard: 'solid',
320
+ quiet: 'ghost',
321
+ destructive: 'solid',
322
+ }) as Record<string, any>)[
323
+ inputRead(checkedProps.value.preset, propInputs.status('preset'))
324
+ ] as any,
325
+ (read) => {
326
+ const value = cycleCheckedComputed(read)
327
+ return { get: () => value.value }
328
+ },
329
+ )
330
+ const colorTone: ReturnType<typeof inputComputed<any>> = inputComputed<any>(
331
+ () =>
332
+ (({
333
+ none: inputRead(checkedProps.value.tone, propInputs.status('tone')),
334
+ primary: 'accent',
335
+ standard: 'neutral',
336
+ quiet: 'neutral',
337
+ destructive: 'danger',
338
+ }) as Record<string, any>)[
339
+ inputRead(checkedProps.value.preset, propInputs.status('preset'))
340
+ ] as any,
341
+ (read) => {
342
+ const value = cycleCheckedComputed(read)
343
+ return { get: () => value.value }
344
+ },
345
+ )
276
346
  let guardedPrevious: any = null
277
347
  const guarded = computed(() => {
278
348
  const candidate: any = ((objectValue: any, indexValue: any) =>
@@ -316,15 +386,10 @@ checkHydratedRoot('button', 'ui-icon-button')
316
386
  /** The values the styles' :host() and :state() rules test. */
317
387
  const hostState = computed(() =>
318
388
  [
319
- checkedProps.value.tone && 'tone',
320
- checkedProps.value.tone != null && `tone=${encodeURIComponent(checkedProps.value.tone)}`,
321
389
  checkedProps.value.anticipatory && 'anticipatory',
322
390
  checkedProps.value.guide && 'guide',
323
391
  checkedProps.value.guide != null && `guide=${encodeURIComponent(checkedProps.value.guide)}`,
324
392
  checkedProps.value.revealed && 'revealed',
325
- checkedProps.value.variant && 'variant',
326
- checkedProps.value.variant != null
327
- && `variant=${encodeURIComponent(checkedProps.value.variant)}`,
328
393
  checkedProps.value.loading && 'loading',
329
394
  checkedProps.value.pending && 'pending',
330
395
  checkedProps.value.size && 'size',
@@ -362,6 +427,7 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
362
427
  'loading',
363
428
  'matchButton',
364
429
  'pending',
430
+ 'preset',
365
431
  'revealed',
366
432
  'round',
367
433
  'size',
@@ -370,6 +436,8 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
370
436
  ],
371
437
  propValidity: (name: string) =>
372
438
  propValidityState({ contract: propValidityContract, values: { ...props } }, name),
439
+ computed: { appearance, colorTone },
440
+ availability: { appearance: () => appearance.status(), colorTone: () => colorTone.status() },
373
441
  })
374
442
  </script>
375
443
 
@@ -380,6 +448,8 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
380
448
  v-bind="nativeAttrs($attrs)"
381
449
  data-hn
382
450
  data-hn-ui-icon-button
451
+ :data-appearance="attribute(appearance.get())"
452
+ :data-tone="attribute(colorTone.get())"
383
453
  :disabled="checkedProps.disabled ?? undefined"
384
454
  :aria-label="checkedProps.label ?? undefined"
385
455
  :data-match-button="checkedProps.matchButton ? '' : undefined"
@@ -393,6 +463,7 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
393
463
  :data-ui-icon-button-prop-loading="reflectedProp('loading', 'loading', checkedProps.loading, undefined, false)"
394
464
  :data-ui-icon-button-prop-match-button="reflectedProp('matchButton', 'match-button', checkedProps.matchButton, undefined, false)"
395
465
  :data-ui-icon-button-prop-pending="reflectedProp('pending', 'pending', checkedProps.pending, undefined, false)"
466
+ :data-ui-icon-button-prop-preset="reflectedProp('preset', 'preset', checkedProps.preset, undefined, false)"
396
467
  :data-ui-icon-button-prop-revealed="reflectedProp('revealed', 'revealed', checkedProps.revealed, undefined, false)"
397
468
  :data-ui-icon-button-prop-round="reflectedProp('round', 'round', checkedProps.round, undefined, false)"
398
469
  :data-ui-icon-button-prop-size="reflectedProp('size', 'size', checkedProps.size, undefined, false)"
@@ -534,7 +605,7 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
534
605
  --_ui-action-start: -1px;
535
606
  }
536
607
 
537
- :global(:scope[data-ui-icon-button-state~="tone=accent"]:where([data-hn-ui-icon-button])) {
608
+ :global(:scope[data-tone="accent"]:where([data-hn-ui-icon-button])) {
538
609
  --_icon-button-border: var(--ui-accent-solid);
539
610
  --_icon-button-hover-border: var(--ui-accent-hover);
540
611
  --_icon-button-text: var(--ui-accent-subtle-text, var(--ui-accent-solid));
@@ -545,7 +616,67 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
545
616
  );
546
617
  color: var(--_icon-button-text);
547
618
  }
548
- :global(:scope[data-ui-icon-button-state~="tone=neutral"]:where([data-hn-ui-icon-button])) {
619
+
620
+ :global(:scope[data-tone="danger"]:where([data-hn-ui-icon-button])) {
621
+ --_icon-button-border: var(--ui-danger);
622
+ --_icon-button-hover-border: var(--ui-danger-hover);
623
+ --_icon-button-text: var(--ui-danger-subtle-text);
624
+ --_icon-button-outline-surface: color-mix(
625
+ in srgb,
626
+ var(--ui-danger) 5%,
627
+ var(--ui-surface-default)
628
+ );
629
+ --_icon-button-solid-surface: var(--ui-danger);
630
+ --_icon-button-solid-hover-surface: var(--ui-danger-hover);
631
+ --_icon-button-solid-text: var(--ui-danger-on-strong);
632
+ color: var(--_icon-button-text);
633
+ }
634
+
635
+ :global(:scope[data-tone="success"]:where([data-hn-ui-icon-button])) {
636
+ --_icon-button-border: var(--ui-success);
637
+ --_icon-button-hover-border: var(--ui-success-hover);
638
+ --_icon-button-text: var(--ui-success-subtle-text);
639
+ --_icon-button-outline-surface: color-mix(
640
+ in srgb,
641
+ var(--ui-success) 5%,
642
+ var(--ui-surface-default)
643
+ );
644
+ --_icon-button-solid-surface: var(--ui-success);
645
+ --_icon-button-solid-hover-surface: var(--ui-success-hover);
646
+ --_icon-button-solid-text: var(--ui-success-on-strong);
647
+ color: var(--_icon-button-text);
648
+ }
649
+
650
+ :global(:scope[data-tone="warning"]:where([data-hn-ui-icon-button])) {
651
+ --_icon-button-border: var(--ui-warning);
652
+ --_icon-button-hover-border: var(--ui-warning-hover);
653
+ --_icon-button-text: var(--ui-warning-subtle-text);
654
+ --_icon-button-outline-surface: color-mix(
655
+ in srgb,
656
+ var(--ui-warning) 5%,
657
+ var(--ui-surface-default)
658
+ );
659
+ --_icon-button-solid-surface: var(--ui-warning);
660
+ --_icon-button-solid-hover-surface: var(--ui-warning-hover);
661
+ --_icon-button-solid-text: var(--ui-warning-on-strong);
662
+ color: var(--_icon-button-text);
663
+ }
664
+
665
+ :global(:scope[data-tone="info"]:where([data-hn-ui-icon-button])) {
666
+ --_icon-button-border: var(--ui-info);
667
+ --_icon-button-hover-border: var(--ui-info-hover);
668
+ --_icon-button-text: var(--ui-info-subtle-text);
669
+ --_icon-button-outline-surface: color-mix(
670
+ in srgb,
671
+ var(--ui-info) 5%,
672
+ var(--ui-surface-default)
673
+ );
674
+ --_icon-button-solid-surface: var(--ui-info);
675
+ --_icon-button-solid-hover-surface: var(--ui-info-hover);
676
+ --_icon-button-solid-text: var(--ui-info-on-strong);
677
+ color: var(--_icon-button-text);
678
+ }
679
+ :global(:scope[data-tone="neutral"]:where([data-hn-ui-icon-button])) {
549
680
  --_icon-button-solid-surface: var(--ui-text-primary);
550
681
  --_icon-button-solid-hover-surface: var(--ui-text-primary);
551
682
  --_icon-button-solid-text: var(--ui-surface-default);
@@ -590,13 +721,13 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
590
721
  }
591
722
 
592
723
  @media (hover: hover) and (pointer: fine) {
593
- :global(:scope[data-ui-icon-button-state~="anticipatory"]:not(:scope[data-ui-icon-button-state~="revealed"], :scope[data-ui-icon-button-state~="variant=solid"], :scope[data-ui-icon-button-state~="loading"], :scope[data-ui-icon-button-state~="pending"], :disabled, [aria-pressed="true"], [data-ui-proximity="near"], :hover, :focus-visible):where([data-hn-ui-icon-button])) {
724
+ :global(:scope[data-ui-icon-button-state~="anticipatory"]:not(:scope[data-ui-icon-button-state~="revealed"], :scope[data-appearance="solid"], :scope[data-ui-icon-button-state~="loading"], :scope[data-ui-icon-button-state~="pending"], :disabled, [aria-pressed="true"], [data-ui-proximity="near"], :hover, :focus-visible):where([data-hn-ui-icon-button])) {
594
725
  border-color: transparent;
595
726
  background-color: transparent;
596
727
  box-shadow: none;
597
728
  }
598
729
 
599
- :global(:scope[data-ui-icon-button-state~="anticipatory"]:not(:scope[data-ui-icon-button-state~="revealed"], :scope[data-ui-icon-button-state~="variant=solid"], :scope[data-ui-icon-button-state~="loading"], :scope[data-ui-icon-button-state~="pending"], :disabled, [aria-pressed="true"], [data-ui-proximity="near"], :hover, :focus-visible)
730
+ :global(:scope[data-ui-icon-button-state~="anticipatory"]:not(:scope[data-ui-icon-button-state~="revealed"], :scope[data-appearance="solid"], :scope[data-ui-icon-button-state~="loading"], :scope[data-ui-icon-button-state~="pending"], :disabled, [aria-pressed="true"], [data-ui-proximity="near"], :hover, :focus-visible)
600
731
  .content:where([data-hn-ui-icon-button])) {
601
732
  opacity: 0;
602
733
  transform: scale(0.65);
@@ -604,7 +735,7 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
604
735
  }
605
736
  }
606
737
 
607
- :global(:scope[data-ui-icon-button-state~="anticipatory"]:is(:scope[data-ui-icon-button-state~="revealed"], :scope[data-ui-icon-button-state~="variant=solid"], [aria-pressed="true"], [data-ui-proximity="near"], :hover, :focus-visible):where([data-hn-ui-icon-button])::before) {
738
+ :global(:scope[data-ui-icon-button-state~="anticipatory"]:is(:scope[data-ui-icon-button-state~="revealed"], :scope[data-appearance="solid"], [aria-pressed="true"], [data-ui-proximity="near"], :hover, :focus-visible):where([data-hn-ui-icon-button])::before) {
608
739
  opacity: 0;
609
740
  }
610
741
 
@@ -714,7 +845,7 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
714
845
  border-radius: var(--ui-radius-round);
715
846
  }
716
847
 
717
- :global(:scope[data-ui-icon-button-state~="variant=outline"]:where([data-hn-ui-icon-button])) {
848
+ :global(:scope[data-appearance="outline"]:where([data-hn-ui-icon-button])) {
718
849
  border-color: var(
719
850
  --ui-icon-button-border,
720
851
  var(--_icon-button-border, var(--ui-control-border, var(--ui-border-strong)))
@@ -728,7 +859,7 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
728
859
  box-shadow: var(--ui-action-shadow, var(--_ui-action-shadow));
729
860
  }
730
861
 
731
- :global(:scope[data-ui-icon-button-state~="variant=solid"]:where([data-hn-ui-icon-button])) {
862
+ :global(:scope[data-appearance="solid"]:where([data-hn-ui-icon-button])) {
732
863
  background-color: var(
733
864
  --ui-icon-button-solid-surface,
734
865
  var(--_icon-button-solid-surface, var(--ui-accent-solid))
@@ -753,29 +884,28 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
753
884
  color: var(--ui-icon-button-hover-text, var(--_icon-button-text, var(--ui-text-primary)));
754
885
  }
755
886
 
756
- :global(:scope[data-ui-icon-button-state~="variant=outline"]:hover:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])) {
887
+ :global(:scope[data-appearance="outline"]:hover:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])) {
757
888
  border-color: var(
758
889
  --ui-icon-button-hover-border,
759
890
  var(--_icon-button-hover-border, var(--ui-control-border-hover, var(--ui-control-border)))
760
891
  );
761
892
  }
762
893
 
763
- :global(:scope[data-ui-icon-button-state~="variant=solid"]:hover:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])) {
894
+ :global(:scope[data-appearance="solid"]:hover:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])) {
764
895
  background-color: var(
765
896
  --ui-icon-button-solid-hover-surface,
766
897
  var(--_icon-button-solid-hover-surface, var(--ui-accent-hover))
767
898
  );
768
899
  }
769
900
 
770
- :global(:scope[data-ui-icon-button-state~="variant=solid"][data-ui-icon-button-state~="tone=accent"]:hover:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])),
771
- :global(:scope[data-ui-icon-button-state~="variant=solid"][data-ui-icon-button-state~="tone=neutral"]:hover:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])) {
901
+ :global(:scope[data-appearance="solid"]:hover:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])) {
772
902
  color: var(
773
903
  --ui-icon-button-solid-text,
774
904
  var(--_icon-button-solid-text, var(--ui-text-on-accent))
775
905
  );
776
906
  }
777
907
 
778
- :global(:scope[data-ui-icon-button-state~="variant=ghost"][aria-expanded="true"]:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])) {
908
+ :global(:scope[data-appearance="ghost"][aria-expanded="true"]:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])) {
779
909
  background-color: var(
780
910
  --ui-icon-button-active-surface,
781
911
  color-mix(in srgb, currentColor 16%, transparent)
@@ -783,7 +913,7 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
783
913
  color: var(--ui-icon-button-hover-text, var(--_icon-button-text, var(--ui-text-primary)));
784
914
  box-shadow: var(--ui-pressed);
785
915
  }
786
- :global(:scope[data-ui-icon-button-state~="variant=outline"][aria-pressed="true"]:enabled:where([data-hn-ui-icon-button])) {
916
+ :global(:scope[data-appearance="outline"][aria-pressed="true"]:enabled:where([data-hn-ui-icon-button])) {
787
917
  background-color: var(
788
918
  --ui-icon-button-active-surface,
789
919
  color-mix(in srgb, currentColor 12%, var(--ui-surface-default))
@@ -823,35 +953,35 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
823
953
  box-shadow: var(--ui-focus-halo);
824
954
  }
825
955
 
826
- :global(:scope[data-ui-icon-button-state~="variant=outline"]:focus-visible:where([data-hn-ui-icon-button])) {
956
+ :global(:scope[data-appearance="outline"]:focus-visible:where([data-hn-ui-icon-button])) {
827
957
  box-shadow:
828
958
  var(--ui-action-shadow, var(--_ui-action-shadow)),
829
959
  var(--ui-focus-halo);
830
960
  }
831
961
 
832
- :global(:scope[data-ui-icon-button-state~="variant=solid"]:focus-visible:where([data-hn-ui-icon-button])) {
962
+ :global(:scope[data-appearance="solid"]:focus-visible:where([data-hn-ui-icon-button])) {
833
963
  box-shadow:
834
964
  var(--ui-icon-button-solid-shadow, var(--ui-action-shadow, var(--_ui-action-shadow))),
835
965
  var(--ui-focus-halo);
836
966
  }
837
967
 
838
- :global(:scope:is(:scope[data-ui-icon-button-state~="variant=outline"], :scope[data-ui-icon-button-state~="variant=solid"]):active:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])) {
968
+ :global(:scope:is(:scope[data-appearance="outline"], :scope[data-appearance="solid"]):active:enabled:not([aria-disabled="true"]):where([data-hn-ui-icon-button])) {
839
969
  background-image: none;
840
970
  box-shadow: var(--ui-pressed);
841
971
  }
842
972
 
843
- :global(:scope[data-ui-icon-button-state~="variant=outline"][aria-pressed="true"]:enabled:focus-visible:where([data-hn-ui-icon-button])) {
973
+ :global(:scope[data-appearance="outline"][aria-pressed="true"]:enabled:focus-visible:where([data-hn-ui-icon-button])) {
844
974
  box-shadow:
845
975
  var(--ui-pressed),
846
976
  var(--ui-focus-halo);
847
977
  }
848
978
 
849
979
  @media (forced-colors: active) {
850
- :global(:scope[data-ui-icon-button-state~="variant=outline"][aria-pressed="true"]:enabled:where([data-hn-ui-icon-button])) {
980
+ :global(:scope[data-appearance="outline"][aria-pressed="true"]:enabled:where([data-hn-ui-icon-button])) {
851
981
  outline: var(--ui-focus-width) solid Highlight;
852
982
  outline-offset: calc(-2 * var(--ui-focus-width));
853
983
  }
854
- :global(:scope[data-ui-icon-button-state~="variant=outline"][aria-pressed="true"]:enabled:focus-visible:where([data-hn-ui-icon-button])) {
984
+ :global(:scope[data-appearance="outline"][aria-pressed="true"]:enabled:focus-visible:where([data-hn-ui-icon-button])) {
855
985
  outline-offset: var(--ui-focus-width);
856
986
  }
857
987
  }
@@ -866,7 +996,7 @@ useComponentHost(() => import('../components/ui-icon-button/ui-icon-button.js'),
866
996
  background-image: none;
867
997
  }
868
998
 
869
- :global(:scope[data-ui-icon-button-state~="variant=ghost"]:disabled:where([data-hn-ui-icon-button])) {
999
+ :global(:scope[data-appearance="ghost"]:disabled:where([data-hn-ui-icon-button])) {
870
1000
  border-color: transparent;
871
1001
  }
872
1002
 
package/vue/UiMenu.vue CHANGED
@@ -352,6 +352,9 @@ useComponentHost(() => import('../components/ui-menu/ui-menu.js'), {
352
352
 
353
353
  /* Small follows its trigger down: an item's own hooks still win. */
354
354
 
355
+ /* Purpose and measured size shape one shared separation lever. The content plane stays opaque;
356
+ transparency, saturation and edge light belong to the surrounding decorative material. */
357
+
355
358
  @scope (.ui-menu) to ([data-hn]) {
356
359
  :global(:scope[data-ui-menu-state~="density=compact"]:where([data-hn-ui-menu])) {
357
360
  --_ui-default-menu-item-padding-block: var(--ui-space-1);
@@ -378,6 +381,7 @@ useComponentHost(() => import('../components/ui-menu/ui-menu.js'), {
378
381
  }
379
382
 
380
383
  :global(:scope:where([data-hn-ui-menu])) {
384
+ --_ui-material-scale: 0.28;
381
385
  --_ui-menu-surface: var(--ui-menu-surface);
382
386
  --_ui-menu-border: var(--ui-menu-border);
383
387
  --_ui-menu-radius: var(--ui-menu-radius);
@@ -422,6 +426,91 @@ useComponentHost(() => import('../components/ui-menu/ui-menu.js'), {
422
426
  max-block-size: inherit;
423
427
  overflow: auto;
424
428
  }
429
+ :global(:scope:not([inline]) .surface:where([data-hn-ui-menu])) {
430
+ --_ui-material-glassiness: clamp(0, var(--ui-glassiness, 0), 1);
431
+ --_ui-material-separation: clamp(0, var(--ui-surface-separation, 0), 1);
432
+ --_ui-material-active: clamp(
433
+ 0,
434
+ calc((var(--_ui-material-glassiness) + var(--_ui-material-separation)) * 100000),
435
+ 1
436
+ );
437
+ --_ui-material-rim: calc(
438
+ (
439
+ var(--_ui-material-separation) + var(--_ui-material-glassiness) * 0.25
440
+ ) *
441
+ var(--_ui-material-scale, 0.28) *
442
+ 8px
443
+ );
444
+ --_ui-material-content-color: var(
445
+ --_ui-menu-surface,
446
+ var(--ui-material-content, var(--ui-overlay-surface, var(--ui-surface-elevated)))
447
+ );
448
+ --_ui-material-rim-color: color-mix(
449
+ in srgb,
450
+ var(--ui-material-glass, var(--ui-surface-elevated)) calc(var(--_ui-material-active) * 100%),
451
+ transparent
452
+ );
453
+ background:
454
+ linear-gradient(var(--_ui-material-content-color), var(--_ui-material-content-color))
455
+ padding-box,
456
+ linear-gradient(var(--_ui-material-rim-color), var(--_ui-material-rim-color)) border-box,
457
+ var(--_ui-material-content-color) padding-box;
458
+ border-width: calc(var(--ui-border-width) + var(--_ui-material-rim));
459
+ border-color: var(
460
+ --_ui-menu-border,
461
+ color-mix(
462
+ in oklab,
463
+ var(--ui-overlay-border, var(--ui-border-strong)) calc(
464
+ (1 - var(--_ui-material-active)) * 100%
465
+ ),
466
+ transparent
467
+ )
468
+ );
469
+ box-shadow: var(
470
+ --_ui-menu-shadow,
471
+ var(
472
+ --ui-overlay-shadow,
473
+ 0 calc(var(--_ui-material-separation) * var(--_ui-material-scale, 0.28) * 8px) calc(
474
+ var(--_ui-material-separation) * var(--_ui-material-scale, 0.28) * 24px
475
+ ) color-mix(in oklab, var(--ui-text-primary) 15%, transparent),
476
+ inset 0 0 0 calc(var(--_ui-material-active) * var(--ui-border-width)) var(
477
+ --ui-material-edge,
478
+ var(--ui-border-strong)
479
+ ),
480
+ var(--ui-elevation-overlay, var(--ui-shadow-lg))
481
+ )
482
+ );
483
+ backdrop-filter: blur(
484
+ calc(var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.28) * 18px)
485
+ ) saturate(calc(1 + var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.28) * 0.18));
486
+ -webkit-backdrop-filter: blur(
487
+ calc(var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.28) * 18px)
488
+ ) saturate(calc(1 + var(--_ui-material-glassiness) * var(--_ui-material-scale, 0.28) * 0.18));
489
+ }
490
+
491
+ @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
492
+ :global(:scope:not([inline]) .surface:where([data-hn-ui-menu])) {
493
+ background: var(--_ui-material-content-color);
494
+ }
495
+ }
496
+
497
+ @media (prefers-reduced-transparency: reduce) {
498
+ :global(:scope:not([inline]) .surface:where([data-hn-ui-menu])) {
499
+ background: var(--_ui-material-content-color);
500
+ backdrop-filter: none;
501
+ -webkit-backdrop-filter: none;
502
+ }
503
+ }
504
+
505
+ @media (forced-colors: active) {
506
+ :global(:scope:not([inline]) .surface:where([data-hn-ui-menu])) {
507
+ background: Canvas;
508
+ border-color: CanvasText;
509
+ box-shadow: none;
510
+ backdrop-filter: none;
511
+ -webkit-backdrop-filter: none;
512
+ }
513
+ }
425
514
 
426
515
  :global(.surface hr:where([data-hn-ui-menu])) {
427
516
  align-self: stretch;
@@ -0,0 +1,83 @@
1
+ import type { PropType } from 'vue';
2
+ import { type InputBindings } from './props.js';
3
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ disabled: {
5
+ type: PropType<boolean | null>;
6
+ };
7
+ label: {
8
+ type: PropType<string | null>;
9
+ };
10
+ max: {
11
+ type: PropType<number | null>;
12
+ };
13
+ min: {
14
+ type: PropType<number | null>;
15
+ };
16
+ name: {
17
+ type: PropType<string | null>;
18
+ };
19
+ size: {
20
+ type: PropType<"sm" | "md" | "lg" | null>;
21
+ };
22
+ step: {
23
+ type: PropType<number | null>;
24
+ };
25
+ unit: {
26
+ type: PropType<string | null>;
27
+ };
28
+ value: {
29
+ type: PropType<string | null>;
30
+ };
31
+ "\0htmlNextInputStatus": {
32
+ type: PropType<InputBindings>;
33
+ };
34
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
35
+ input: (event: CustomEvent<{
36
+ readonly value: string;
37
+ }>) => any;
38
+ change: (event: CustomEvent<{
39
+ readonly value: string;
40
+ }>) => any;
41
+ "update:value": (value: string) => any;
42
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
43
+ disabled: {
44
+ type: PropType<boolean | null>;
45
+ };
46
+ label: {
47
+ type: PropType<string | null>;
48
+ };
49
+ max: {
50
+ type: PropType<number | null>;
51
+ };
52
+ min: {
53
+ type: PropType<number | null>;
54
+ };
55
+ name: {
56
+ type: PropType<string | null>;
57
+ };
58
+ size: {
59
+ type: PropType<"sm" | "md" | "lg" | null>;
60
+ };
61
+ step: {
62
+ type: PropType<number | null>;
63
+ };
64
+ unit: {
65
+ type: PropType<string | null>;
66
+ };
67
+ value: {
68
+ type: PropType<string | null>;
69
+ };
70
+ "\0htmlNextInputStatus": {
71
+ type: PropType<InputBindings>;
72
+ };
73
+ }>> & Readonly<{
74
+ onInput?: ((event: CustomEvent<{
75
+ readonly value: string;
76
+ }>) => any) | undefined;
77
+ onChange?: ((event: CustomEvent<{
78
+ readonly value: string;
79
+ }>) => any) | undefined;
80
+ "onUpdate:value"?: ((value: string) => any) | undefined;
81
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
82
+ declare const _default: typeof __VLS_export;
83
+ export default _default;
@@ -0,0 +1,2 @@
1
+ import { t as UiNumberInput_default } from "./chunks/UiNumberInput.js";
2
+ export { UiNumberInput_default as default };