@threadlabs/looma 0.6.4 → 0.7.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 (259) hide show
  1. package/components/ui-button/ui-button.html +103 -81
  2. package/components/ui-checkbox/ui-checkbox.html +4 -1
  3. package/components/ui-combobox/ui-combobox.html +40 -3
  4. package/components/ui-combobox/ui-combobox.js +27 -5
  5. package/components/ui-select/ui-select.html +3 -5
  6. package/dist/index.js +1 -1
  7. package/package.json +2 -2
  8. package/styles/ui-affordance-scope.css +1 -1
  9. package/styles/ui-avatar-group.css +1 -1
  10. package/styles/ui-avatar.css +1 -1
  11. package/styles/ui-badge.css +1 -1
  12. package/styles/ui-button.css +112 -80
  13. package/styles/ui-callout.css +1 -1
  14. package/styles/ui-checkbox.css +5 -2
  15. package/styles/ui-cluster.css +1 -1
  16. package/styles/ui-combobox.css +38 -1
  17. package/styles/ui-container.css +1 -1
  18. package/styles/ui-context-menu.css +1 -1
  19. package/styles/ui-dialog.css +1 -1
  20. package/styles/ui-disclosure.css +1 -1
  21. package/styles/ui-editable.css +1 -1
  22. package/styles/ui-editor-insert-table-grid.css +1 -1
  23. package/styles/ui-editor-mention-menu.css +1 -1
  24. package/styles/ui-editor-slash-menu.css +1 -1
  25. package/styles/ui-editor-table-context-menu.css +1 -1
  26. package/styles/ui-editor-table-overlay.css +1 -1
  27. package/styles/ui-editor-table-toolbar.css +1 -1
  28. package/styles/ui-editor-toolbar.css +1 -1
  29. package/styles/ui-floating-action-button.css +1 -1
  30. package/styles/ui-form-field.css +1 -1
  31. package/styles/ui-grid.css +1 -1
  32. package/styles/ui-icon-button.css +1 -1
  33. package/styles/ui-icon.css +1 -1
  34. package/styles/ui-input.css +1 -1
  35. package/styles/ui-menu-item.css +1 -1
  36. package/styles/ui-menu.css +1 -1
  37. package/styles/ui-popover.css +1 -1
  38. package/styles/ui-radio-group.css +1 -1
  39. package/styles/ui-radio.css +1 -1
  40. package/styles/ui-reel.css +1 -1
  41. package/styles/ui-search-result-row.css +1 -1
  42. package/styles/ui-search-shell.css +1 -1
  43. package/styles/ui-select.css +4 -5
  44. package/styles/ui-separator.css +1 -1
  45. package/styles/ui-sidebar.css +1 -1
  46. package/styles/ui-stack.css +1 -1
  47. package/styles/ui-switch.css +1 -1
  48. package/styles/ui-switcher.css +1 -1
  49. package/styles/ui-tabs.css +1 -1
  50. package/styles/ui-textarea.css +1 -1
  51. package/styles/ui-toast-region.css +1 -1
  52. package/styles/ui-tooltip.css +1 -1
  53. package/styles/ui-top-bar.css +1 -1
  54. package/styles/ui-tree-item.css +1 -1
  55. package/styles/ui-tree.css +1 -1
  56. package/theme-dark.css +6 -30
  57. package/theme-high-contrast.css +30 -20
  58. package/theme-light.css +2 -16
  59. package/tokens.css +33 -24
  60. package/vanilla/UiAffordanceScope.d.ts +1 -1
  61. package/vanilla/UiAffordanceScope.js +1 -1
  62. package/vanilla/UiAvatar.d.ts +1 -1
  63. package/vanilla/UiAvatar.js +1 -1
  64. package/vanilla/UiAvatarGroup.d.ts +1 -1
  65. package/vanilla/UiAvatarGroup.js +1 -1
  66. package/vanilla/UiBadge.d.ts +1 -1
  67. package/vanilla/UiBadge.js +1 -1
  68. package/vanilla/UiButton.d.ts +2 -2
  69. package/vanilla/UiButton.js +2 -2
  70. package/vanilla/UiCallout.d.ts +1 -1
  71. package/vanilla/UiCallout.js +1 -1
  72. package/vanilla/UiCheckbox.d.ts +1 -1
  73. package/vanilla/UiCheckbox.js +1 -1
  74. package/vanilla/UiCluster.d.ts +1 -1
  75. package/vanilla/UiCluster.js +1 -1
  76. package/vanilla/UiCombobox.d.ts +1 -1
  77. package/vanilla/UiCombobox.js +2 -2
  78. package/vanilla/UiContainer.d.ts +1 -1
  79. package/vanilla/UiContainer.js +1 -1
  80. package/vanilla/UiContextMenu.d.ts +1 -1
  81. package/vanilla/UiContextMenu.js +1 -1
  82. package/vanilla/UiDialog.d.ts +1 -1
  83. package/vanilla/UiDialog.js +1 -1
  84. package/vanilla/UiDisclosure.d.ts +1 -1
  85. package/vanilla/UiDisclosure.js +1 -1
  86. package/vanilla/UiEditable.d.ts +1 -1
  87. package/vanilla/UiEditable.js +1 -1
  88. package/vanilla/UiEditorInsertTableGrid.d.ts +1 -1
  89. package/vanilla/UiEditorInsertTableGrid.js +1 -1
  90. package/vanilla/UiEditorMentionMenu.d.ts +1 -1
  91. package/vanilla/UiEditorMentionMenu.js +1 -1
  92. package/vanilla/UiEditorSlashMenu.d.ts +1 -1
  93. package/vanilla/UiEditorSlashMenu.js +1 -1
  94. package/vanilla/UiEditorTableContextMenu.d.ts +1 -1
  95. package/vanilla/UiEditorTableContextMenu.js +1 -1
  96. package/vanilla/UiEditorTableOverlay.d.ts +1 -1
  97. package/vanilla/UiEditorTableOverlay.js +1 -1
  98. package/vanilla/UiEditorTableToolbar.d.ts +1 -1
  99. package/vanilla/UiEditorTableToolbar.js +1 -1
  100. package/vanilla/UiEditorToolbar.d.ts +1 -1
  101. package/vanilla/UiEditorToolbar.js +1 -1
  102. package/vanilla/UiFloatingActionButton.d.ts +1 -1
  103. package/vanilla/UiFloatingActionButton.js +1 -1
  104. package/vanilla/UiFormField.d.ts +1 -1
  105. package/vanilla/UiFormField.js +1 -1
  106. package/vanilla/UiGrid.d.ts +1 -1
  107. package/vanilla/UiGrid.js +1 -1
  108. package/vanilla/UiIcon.d.ts +1 -1
  109. package/vanilla/UiIcon.js +1 -1
  110. package/vanilla/UiIconButton.d.ts +1 -1
  111. package/vanilla/UiIconButton.js +1 -1
  112. package/vanilla/UiInput.d.ts +1 -1
  113. package/vanilla/UiInput.js +1 -1
  114. package/vanilla/UiMenu.d.ts +1 -1
  115. package/vanilla/UiMenu.js +1 -1
  116. package/vanilla/UiMenuItem.d.ts +1 -1
  117. package/vanilla/UiMenuItem.js +1 -1
  118. package/vanilla/UiPopover.d.ts +1 -1
  119. package/vanilla/UiPopover.js +1 -1
  120. package/vanilla/UiRadio.d.ts +1 -1
  121. package/vanilla/UiRadio.js +1 -1
  122. package/vanilla/UiRadioGroup.d.ts +1 -1
  123. package/vanilla/UiRadioGroup.js +1 -1
  124. package/vanilla/UiReel.d.ts +1 -1
  125. package/vanilla/UiReel.js +1 -1
  126. package/vanilla/UiSearchResultRow.d.ts +1 -1
  127. package/vanilla/UiSearchResultRow.js +1 -1
  128. package/vanilla/UiSearchShell.d.ts +1 -1
  129. package/vanilla/UiSearchShell.js +1 -1
  130. package/vanilla/UiSelect.d.ts +1 -2
  131. package/vanilla/UiSelect.js +4 -7
  132. package/vanilla/UiSeparator.d.ts +1 -1
  133. package/vanilla/UiSeparator.js +1 -1
  134. package/vanilla/UiSidebar.d.ts +1 -1
  135. package/vanilla/UiSidebar.js +1 -1
  136. package/vanilla/UiStack.d.ts +1 -1
  137. package/vanilla/UiStack.js +1 -1
  138. package/vanilla/UiSwitch.d.ts +1 -1
  139. package/vanilla/UiSwitch.js +1 -1
  140. package/vanilla/UiSwitcher.d.ts +1 -1
  141. package/vanilla/UiSwitcher.js +1 -1
  142. package/vanilla/UiTabs.d.ts +1 -1
  143. package/vanilla/UiTabs.js +1 -1
  144. package/vanilla/UiTextarea.d.ts +1 -1
  145. package/vanilla/UiTextarea.js +1 -1
  146. package/vanilla/UiToastRegion.d.ts +1 -1
  147. package/vanilla/UiToastRegion.js +1 -1
  148. package/vanilla/UiTooltip.d.ts +1 -1
  149. package/vanilla/UiTooltip.js +1 -1
  150. package/vanilla/UiTopBar.d.ts +1 -1
  151. package/vanilla/UiTopBar.js +1 -1
  152. package/vanilla/UiTree.d.ts +1 -1
  153. package/vanilla/UiTree.js +1 -1
  154. package/vanilla/UiTreeItem.d.ts +1 -1
  155. package/vanilla/UiTreeItem.js +1 -1
  156. package/vue/UiAffordanceScope.vue +8 -84
  157. package/vue/UiAvatar.d.ts +1 -1
  158. package/vue/UiAvatar.vue +9 -106
  159. package/vue/UiAvatarGroup.vue +9 -96
  160. package/vue/UiBadge.vue +1 -1
  161. package/vue/UiButton.d.ts +2 -2
  162. package/vue/UiButton.vue +104 -97
  163. package/vue/UiCallout.vue +1 -1
  164. package/vue/UiCheckbox.vue +26 -124
  165. package/vue/UiCluster.vue +1 -1
  166. package/vue/UiCombobox.d.ts +1 -1
  167. package/vue/UiCombobox.vue +270 -479
  168. package/vue/UiContainer.vue +1 -1
  169. package/vue/UiContextMenu.vue +34 -141
  170. package/vue/UiDialog.vue +25 -126
  171. package/vue/UiDisclosure.vue +26 -138
  172. package/vue/UiEditable.vue +46 -158
  173. package/vue/UiEditorInsertTableGrid.vue +19 -138
  174. package/vue/UiEditorMentionMenu.vue +17 -124
  175. package/vue/UiEditorSlashMenu.vue +17 -109
  176. package/vue/UiEditorTableContextMenu.vue +34 -122
  177. package/vue/UiEditorTableOverlay.vue +61 -167
  178. package/vue/UiEditorTableToolbar.vue +34 -142
  179. package/vue/UiEditorToolbar.vue +1 -1
  180. package/vue/UiFloatingActionButton.vue +1 -1
  181. package/vue/UiFormField.vue +7 -79
  182. package/vue/UiGrid.vue +1 -1
  183. package/vue/UiIcon.vue +8 -84
  184. package/vue/UiIconButton.vue +7 -79
  185. package/vue/UiInput.vue +7 -79
  186. package/vue/UiMenu.vue +42 -127
  187. package/vue/UiMenuItem.vue +1 -1
  188. package/vue/UiPopover.vue +25 -121
  189. package/vue/UiRadio.vue +22 -123
  190. package/vue/UiRadioGroup.d.ts +1 -1
  191. package/vue/UiRadioGroup.vue +32 -119
  192. package/vue/UiReel.vue +1 -1
  193. package/vue/UiSearchResultRow.vue +1 -1
  194. package/vue/UiSearchShell.vue +25 -121
  195. package/vue/UiSelect.d.ts +0 -2
  196. package/vue/UiSelect.vue +9 -85
  197. package/vue/UiSeparator.vue +1 -1
  198. package/vue/UiSidebar.vue +32 -133
  199. package/vue/UiStack.vue +1 -1
  200. package/vue/UiSwitch.vue +22 -123
  201. package/vue/UiSwitcher.vue +1 -1
  202. package/vue/UiTabs.vue +24 -128
  203. package/vue/UiTextarea.vue +7 -79
  204. package/vue/UiToastRegion.vue +37 -126
  205. package/vue/UiTooltip.vue +25 -124
  206. package/vue/UiTopBar.vue +1 -1
  207. package/vue/UiTree.vue +56 -137
  208. package/vue/UiTreeItem.vue +28 -144
  209. package/vue/chunks/UiAffordanceScope.js +9 -78
  210. package/vue/chunks/UiAvatar.js +13 -89
  211. package/vue/chunks/UiAvatarGroup.js +10 -80
  212. package/vue/chunks/UiBadge.js +1 -1
  213. package/vue/chunks/UiButton.js +3 -3
  214. package/vue/chunks/UiCallout.js +1 -1
  215. package/vue/chunks/UiCheckbox.js +17 -95
  216. package/vue/chunks/UiCluster.js +1 -1
  217. package/vue/chunks/UiCombobox.js +115 -326
  218. package/vue/chunks/UiContainer.js +1 -1
  219. package/vue/chunks/UiContextMenu.js +21 -113
  220. package/vue/chunks/UiDialog.js +20 -99
  221. package/vue/chunks/UiDisclosure.js +22 -112
  222. package/vue/chunks/UiEditable.js +32 -128
  223. package/vue/chunks/UiEditorInsertTableGrid.js +22 -116
  224. package/vue/chunks/UiEditorMentionMenu.js +18 -116
  225. package/vue/chunks/UiEditorSlashMenu.js +15 -101
  226. package/vue/chunks/UiEditorTableContextMenu.js +12 -91
  227. package/vue/chunks/UiEditorTableOverlay.js +29 -120
  228. package/vue/chunks/UiEditorTableToolbar.js +16 -111
  229. package/vue/chunks/UiEditorToolbar.js +1 -1
  230. package/vue/chunks/UiFloatingActionButton.js +1 -1
  231. package/vue/chunks/UiFormField.js +8 -73
  232. package/vue/chunks/UiGrid.js +1 -1
  233. package/vue/chunks/UiIcon.js +9 -78
  234. package/vue/chunks/UiIconButton.js +8 -73
  235. package/vue/chunks/UiInput.js +8 -73
  236. package/vue/chunks/UiMenu.js +25 -111
  237. package/vue/chunks/UiMenuItem.js +1 -1
  238. package/vue/chunks/UiPopover.js +18 -105
  239. package/vue/chunks/UiRadio.js +17 -95
  240. package/vue/chunks/UiRadioGroup.js +23 -105
  241. package/vue/chunks/UiReel.js +1 -1
  242. package/vue/chunks/UiSearchResultRow.js +1 -1
  243. package/vue/chunks/UiSearchShell.js +17 -94
  244. package/vue/chunks/UiSelect.js +8 -79
  245. package/vue/chunks/UiSeparator.js +1 -1
  246. package/vue/chunks/UiSidebar.js +23 -108
  247. package/vue/chunks/UiStack.js +1 -1
  248. package/vue/chunks/UiSwitch.js +17 -95
  249. package/vue/chunks/UiSwitcher.js +1 -1
  250. package/vue/chunks/UiTabs.js +22 -103
  251. package/vue/chunks/UiTextarea.js +8 -73
  252. package/vue/chunks/UiToastRegion.js +27 -112
  253. package/vue/chunks/UiTooltip.js +18 -106
  254. package/vue/chunks/UiTopBar.js +1 -1
  255. package/vue/chunks/UiTree.js +31 -109
  256. package/vue/chunks/UiTreeItem.js +29 -119
  257. package/vue/chunks/host.js +101 -0
  258. package/vue/components.css +844 -797
  259. package/vue/host.ts +154 -0
@@ -1,15 +1,8 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
- import {
4
- computed,
5
- onBeforeUnmount,
6
- onMounted,
7
- ref,
8
- shallowRef,
9
- useTemplateRef,
10
- watchEffect,
11
- } from 'vue'
3
+ import { ref, useTemplateRef } from 'vue'
12
4
  import * as controllerModule from '../components/ui-avatar-group/ui-avatar-group.js'
5
+ import { createDispatch, useComponentHost } from './host'
13
6
 
14
7
  defineOptions({ inheritAttrs: false })
15
8
 
@@ -28,94 +21,14 @@ const root = ref<HTMLElement | null>(null)
28
21
  const overflowElement = useTemplateRef<HTMLElement>('overflow')
29
22
  const overflowCount = ref(0)
30
23
 
31
- /** Dispatches a component event to Vue listeners and, for controllers and page code, on the root. */
32
- function dispatch(name: string, detail?: unknown): boolean {
33
- return root.value?.dispatchEvent(new CustomEvent(name, { bubbles: true, composed: true, detail }))
34
- ?? true
35
- }
36
-
37
- function read(name: string): unknown {
38
- if (name === 'overflowCount') return overflowCount.value
39
- return (props as Record<string, unknown>)[name]
40
- }
24
+ const dispatch = createDispatch(root)
41
25
 
42
- function write(name: string, value: unknown): boolean {
43
- if (name === 'overflowCount') {
44
- overflowCount.value = value as never
45
- return true
46
- }
47
- throw new TypeError(`Only declared state is writable; \`${name}\` is not.`)
48
- }
49
-
50
- const stops: Array<() => void> = []
51
- const host = {
52
- get element(): Element {
53
- return root.value as Element
54
- },
55
- state: new Proxy({} as Record<string, unknown>, {
56
- get: (_target, name) => typeof name === 'string' ? read(name) : undefined,
57
- set: (_target, name, value) => typeof name === 'string' && write(name, value),
58
- }),
59
- refs: {
60
- get overflow(): Element {
61
- return overflowElement.value as Element
62
- },
63
- } as Readonly<Record<string, Element>>,
64
- elements: new Proxy({} as Record<string, Element | RadioNodeList | undefined>, {
65
- get: (_target, name) => {
66
- if (typeof name !== 'string' || root.value === null) return undefined
67
- const form = root.value instanceof HTMLFormElement
68
- ? root.value
69
- : root.value.querySelector('form')
70
- return form?.elements.namedItem(name)
71
- ?? root.value.querySelector(`[name="${CSS.escape(name)}"]`) ?? undefined
72
- },
73
- }),
74
- signal<T>(initialValue: T) {
75
- const value = shallowRef(initialValue)
76
- return {
77
- get: (): T => value.value,
78
- set: (next: T): void => {
79
- if (!Object.is(value.value, next)) value.value = next
80
- },
81
- update: (next: (current: T) => T): void => {
82
- const updated = next(value.value)
83
- if (!Object.is(value.value, updated)) value.value = updated
84
- },
85
- }
86
- },
87
- computed<T>(compute: () => T) {
88
- const value = computed(compute)
89
- return { get: (): T => value.value }
90
- },
91
- effect(run: () => void | (() => void)): () => void {
92
- const stop = watchEffect((onCleanup) => {
93
- const cleanup = run()
94
- if (typeof cleanup === 'function') onCleanup(cleanup)
95
- }, { flush: 'post' })
96
- stops.push(stop)
97
- return stop
98
- },
99
- on(event: string, listener: EventListener): () => void {
100
- const element = root.value
101
- element?.addEventListener(event, listener)
102
- const off = (): void => element?.removeEventListener(event, listener)
103
- stops.push(off)
104
- return off
105
- },
26
+ useComponentHost(controllerModule.default, {
27
+ root,
106
28
  dispatch,
107
- }
108
-
109
- let cleanup: void | (() => void)
110
- let ready: Promise<void> | undefined
111
- onMounted(() => {
112
- ready = Promise.resolve(controllerModule.default(host as never)).then((result) => {
113
- cleanup = result
114
- })
115
- })
116
- onBeforeUnmount(() => {
117
- for (const stop of stops.splice(0)) stop()
118
- if (typeof cleanup === 'function') cleanup()
29
+ props,
30
+ refs: { overflow: overflowElement },
31
+ state: { overflowCount },
119
32
  })
120
33
  </script>
121
34
 
package/vue/UiBadge.vue CHANGED
@@ -1,4 +1,4 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
3
  import { computed } from 'vue'
4
4
 
package/vue/UiButton.d.ts CHANGED
@@ -3,7 +3,7 @@ type __VLS_Props = {
3
3
  disabled?: boolean;
4
4
  size?: 'sm' | 'md' | 'lg';
5
5
  stretch?: boolean;
6
- tone?: 'neutral' | 'accent';
6
+ tone?: 'accent' | 'neutral' | 'danger' | 'success' | 'warning' | 'info';
7
7
  variant?: 'outline' | 'solid' | 'danger' | 'ghost' | 'link';
8
8
  };
9
9
  declare var __VLS_1: {};
@@ -11,7 +11,7 @@ type __VLS_Slots = {} & {
11
11
  default?: (props: typeof __VLS_1) => any;
12
12
  };
13
13
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
14
- tone: "neutral" | "accent";
14
+ tone: "accent" | "neutral" | "danger" | "success" | "warning" | "info";
15
15
  variant: "outline" | "solid" | "danger" | "ghost" | "link";
16
16
  align: "center" | "start";
17
17
  disabled: boolean;
package/vue/UiButton.vue CHANGED
@@ -1,4 +1,4 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
3
  import { computed } from 'vue'
4
4
 
@@ -10,7 +10,7 @@ const props = withDefaults(
10
10
  disabled?: boolean
11
11
  size?: 'sm' | 'md' | 'lg'
12
12
  stretch?: boolean
13
- tone?: 'neutral' | 'accent'
13
+ tone?: 'accent' | 'neutral' | 'danger' | 'success' | 'warning' | 'info'
14
14
  variant?: 'outline' | 'solid' | 'danger' | 'ghost' | 'link'
15
15
  }>(),
16
16
  {
@@ -18,7 +18,7 @@ const props = withDefaults(
18
18
  disabled: false,
19
19
  size: 'md',
20
20
  stretch: false,
21
- tone: 'neutral',
21
+ tone: 'accent',
22
22
  variant: 'outline',
23
23
  },
24
24
  )
@@ -26,10 +26,10 @@ const props = withDefaults(
26
26
  /** The values the styles' :host-state() rules test. */
27
27
  const hostState = computed(() =>
28
28
  [
29
- props.variant && `variant variant=${props.variant}`,
30
29
  props.align && `align align=${props.align}`,
31
30
  props.stretch && 'stretch',
32
31
  props.size && `size size=${props.size}`,
32
+ props.variant && `variant variant=${props.variant}`,
33
33
  props.tone && `tone tone=${props.tone}`,
34
34
  ].filter(Boolean).join(' ')
35
35
  )
@@ -49,15 +49,23 @@ const hostState = computed(() =>
49
49
 
50
50
  <style scoped>
51
51
  [data-component~="ui-button"] {
52
+ --_hue: var(--ui-accent);
53
+ --_hue-hover: var(--ui-accent-hover);
54
+ --_hue-active: var(--ui-accent-active);
55
+ --_hue-text: var(--ui-accent-active);
56
+ --_on-hue: var(--ui-on-accent);
57
+
58
+ --_tone: var(--_hue);
59
+ --_tone-hover: var(--_hue-hover);
60
+ --_tone-active: var(--_hue-active);
61
+ --_tone-text: var(--_hue-text);
62
+ --_on-tone: var(--_on-hue);
63
+
52
64
  display: inline-flex;
53
65
  align-items: center;
54
66
  justify-content: center;
55
67
  vertical-align: middle;
56
- color: var(--ui-action-secondary-text, var(--ui-text-primary));
57
- }
58
-
59
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"] {
60
- color: var(--ui-action-primary-text, var(--ui-text-on-accent));
68
+ color: var(--_tone-text);
61
69
  }
62
70
 
63
71
  [data-component~="ui-button"][data-ui-button-state~="align=start"] {
@@ -73,23 +81,24 @@ const hostState = computed(() =>
73
81
  [data-component~="ui-button"] {
74
82
  appearance: none;
75
83
  min-block-size: var(--ui-button-min-block-size, var(--ui-control-size-md));
76
- border: 1px solid var(--ui-button-border, var(--ui-border-strong));
84
+ border-style: solid;
85
+ border-width: 1px;
86
+ border-color: var(--ui-button-border, var(--_tone));
77
87
  border-radius: var(--ui-button-radius, var(--ui-radius-md));
78
- background: var(--ui-button-surface, var(--ui-surface-elevated));
79
- color: var(--ui-button-text, var(--ui-action-secondary-text, var(--ui-text-primary)));
88
+ background-color: var(--ui-button-surface, oklch(from var(--_tone) l c h / 5%));
89
+ color: var(--ui-button-text, var(--_tone-text));
80
90
  font: inherit;
81
91
  font-weight: var(--ui-font-medium);
82
92
  line-height: var(--ui-line-height-tight);
83
93
  padding: var(--ui-space-2) var(--ui-space-3);
84
94
  background-image: var(--ui-raised-highlight);
85
- box-shadow: var(--ui-button-shadow, var(--ui-raised, var(--ui-shadow-xs)));
95
+ box-shadow: var(--ui-button-shadow, var(--ui-raised));
86
96
  cursor: pointer;
87
97
  transition:
88
98
  background-color var(--ui-motion-fast) var(--ui-motion-ease),
89
99
  border-color var(--ui-motion-fast) var(--ui-motion-ease),
90
100
  color var(--ui-motion-fast) var(--ui-motion-ease),
91
- box-shadow var(--ui-motion-fast) var(--ui-motion-ease),
92
- transform var(--ui-motion-fast) var(--ui-motion-ease);
101
+ box-shadow var(--ui-motion-fast) var(--ui-motion-ease);
93
102
  margin: 0;
94
103
  }
95
104
 
@@ -113,145 +122,143 @@ const hostState = computed(() =>
113
122
  }
114
123
 
115
124
  [data-component~="ui-button"]:hover:enabled {
116
- background: var(--ui-action-secondary-surface-hover, var(--ui-surface-muted));
117
- border-color: var(--ui-control-border-hover, var(--ui-border-strong));
125
+ border-color: var(--ui-button-hover-border, var(--_tone-hover));
126
+ background-color: var(--ui-button-hover-surface, oklch(from var(--_tone) l c h / 12%));
118
127
  }
119
128
 
129
+ /* Pressed reads as pressed in: the highlight goes, and the shadow moves inside. */
120
130
  [data-component~="ui-button"]:active:enabled {
121
- background: var(--ui-surface-subtle);
131
+ background-color: var(--ui-button-active-surface, oklch(from var(--_tone) l c h / 18%));
132
+ background-image: none;
133
+ box-shadow: var(--ui-pressed);
122
134
  }
123
135
 
124
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"] {
125
- border-color: var(--ui-action-primary-surface, var(--ui-accent-solid));
126
- background: var(--ui-action-primary-surface, var(--ui-accent-solid));
127
- color: var(--ui-action-primary-text, var(--ui-text-on-accent));
128
- box-shadow: var(
129
- --ui-button-solid-shadow,
130
- inset 0 1px rgb(255 255 255 / 0.16),
131
- var(--ui-shadow-xs)
132
- );
133
- }
134
-
135
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"]:hover:enabled {
136
- background: var(--ui-action-primary-surface-hover, var(--ui-accent-hover));
137
- border-color: var(--ui-action-primary-surface-hover, var(--ui-accent-hover));
138
- box-shadow: var(--ui-shadow-sm);
139
- transform: translateY(-1px);
136
+ [data-component~="ui-button"][data-ui-button-state~="variant=solid"],
137
+ [data-component~="ui-button"][data-ui-button-state~="variant=danger"] {
138
+ border-color: var(--_tone);
139
+ background-color: var(--_tone);
140
+ color: var(--_on-tone);
141
+ box-shadow: var(--ui-button-solid-shadow, var(--ui-raised));
140
142
  }
141
143
 
142
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"]:active:enabled {
143
- background: var(--ui-action-primary-surface-active, var(--ui-accent-active));
144
- border-color: var(--ui-action-primary-surface-active, var(--ui-accent-active));
145
- box-shadow: none;
146
- transform: translateY(0);
144
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"], [data-component~="ui-button"][data-ui-button-state~="variant=danger"]):hover:enabled {
145
+ background-color: var(--_tone-hover);
146
+ border-color: var(--_tone-hover);
147
147
  }
148
148
 
149
- [data-component~="ui-button"][data-ui-button-state~="variant=danger"] {
150
- border-color: var(--ui-danger-solid);
151
- background: var(--ui-danger-solid);
152
- color: var(--ui-text-on-accent);
153
- box-shadow: var(
154
- --ui-button-danger-shadow,
155
- inset 0 1px rgb(255 255 255 / 0.14),
156
- var(--ui-shadow-xs)
157
- );
149
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"], [data-component~="ui-button"][data-ui-button-state~="variant=danger"]):active:enabled {
150
+ background-color: var(--_tone-active);
151
+ border-color: var(--_tone-active);
152
+ background-image: none;
153
+ box-shadow: var(--ui-pressed);
158
154
  }
159
155
 
160
- [data-component~="ui-button"][data-ui-button-state~="variant=danger"]:hover:enabled {
161
- background: var(--ui-danger-hover);
162
- border-color: var(--ui-danger-hover);
163
- box-shadow: var(--ui-shadow-sm);
164
- transform: translateY(-1px);
156
+ /* Tone is the colour, variant is the volume. Every pairing works: a danger outline is a
157
+ destructive secondary action, not a special case. */
158
+ [data-component~="ui-button"][data-ui-button-state~="tone=neutral"] {
159
+ --_hue: var(--ui-text);
160
+ --_hue-hover: var(--ui-text);
161
+ --_hue-active: var(--ui-text);
162
+ --_hue-text: var(--ui-text);
163
+ --_on-hue: var(--ui-surface);
165
164
  }
166
165
 
167
- [data-component~="ui-button"][data-ui-button-state~="variant=danger"]:active:enabled {
168
- box-shadow: none;
169
- transform: translateY(0);
166
+ [data-component~="ui-button"][data-ui-button-state~="tone=danger"],
167
+ [data-component~="ui-button"][data-ui-button-state~="variant=danger"] {
168
+ --_hue: var(--ui-danger);
169
+ --_hue-hover: var(--ui-danger-hover);
170
+ --_hue-active: var(--ui-danger-hover);
171
+ --_hue-text: var(--ui-danger-hover);
172
+ --_on-hue: var(--ui-on-danger);
170
173
  }
171
174
 
172
- /* An accent-tinted secondary action: the outline, ghost, and link variants in the accent colour.
173
- Solid and danger already carry their own intent. */
174
- [data-component~="ui-button"][data-ui-button-state~="tone=accent"][data-component~="ui-button"][data-ui-button-state~="variant=outline"] {
175
- border-color: var(--ui-button-accent-border, var(--ui-accent));
176
- background: var(--ui-button-accent-surface, var(--ui-accent-subtle));
177
- color: var(--ui-button-accent-text, var(--ui-accent-active));
175
+ [data-component~="ui-button"][data-ui-button-state~="tone=success"] {
176
+ --_hue: var(--ui-success);
177
+ --_hue-hover: color-mix(in srgb, var(--ui-success) 85%, #000);
178
+ --_hue-active: color-mix(in srgb, var(--ui-success) 75%, #000);
179
+ --_hue-text: color-mix(in srgb, var(--ui-success) 85%, #000);
180
+ --_on-hue: var(--ui-surface);
178
181
  }
179
182
 
180
- [data-component~="ui-button"][data-ui-button-state~="tone=accent"][data-component~="ui-button"][data-ui-button-state~="variant=outline"]:hover:enabled {
181
- border-color: var(--ui-button-accent-hover-border, var(--ui-accent-hover));
182
- background: var(
183
- --ui-button-accent-hover-surface,
184
- color-mix(in srgb, var(--ui-accent) 16%, var(--ui-surface))
185
- );
183
+ [data-component~="ui-button"][data-ui-button-state~="tone=warning"] {
184
+ --_hue: var(--ui-warning);
185
+ --_hue-hover: color-mix(in srgb, var(--ui-warning) 85%, #000);
186
+ --_hue-active: color-mix(in srgb, var(--ui-warning) 75%, #000);
187
+ --_hue-text: color-mix(in srgb, var(--ui-warning) 85%, #000);
188
+ --_on-hue: var(--ui-surface);
186
189
  }
187
190
 
188
- [data-component~="ui-button"][data-ui-button-state~="tone=accent"]:is([data-component~="ui-button"][data-ui-button-state~="variant=ghost"], [data-component~="ui-button"][data-ui-button-state~="variant=link"]) {
189
- color: var(--ui-button-accent-text, var(--ui-accent-active));
191
+ [data-component~="ui-button"][data-ui-button-state~="tone=info"] {
192
+ --_hue: var(--ui-info);
193
+ --_hue-hover: color-mix(in srgb, var(--ui-info) 85%, #000);
194
+ --_hue-active: color-mix(in srgb, var(--ui-info) 75%, #000);
195
+ --_hue-text: color-mix(in srgb, var(--ui-info) 85%, #000);
196
+ --_on-hue: var(--ui-surface);
190
197
  }
191
198
 
192
199
  [data-component~="ui-button"][data-ui-button-state~="variant=ghost"] {
193
200
  border-color: transparent;
194
201
  background: transparent;
195
202
  box-shadow: none;
196
- color: var(--ui-button-ghost-text, var(--ui-action-secondary-text, var(--ui-text-primary)));
203
+ color: var(--ui-button-ghost-text, var(--_tone-text));
197
204
  }
198
205
 
199
206
  /* Tints derive from the text colour, not a surface token, so hover and press show on any background. */
200
207
  [data-component~="ui-button"][data-ui-button-state~="variant=ghost"]:hover:enabled {
201
- background: var(
202
- --ui-button-ghost-hover-surface,
203
- color-mix(in srgb, currentColor 8%, transparent)
204
- );
208
+ background-color: var(--ui-button-ghost-hover-surface, oklch(from var(--_tone) l c h / 10%));
205
209
  border-color: transparent;
206
210
  }
207
211
 
208
212
  [data-component~="ui-button"][data-ui-button-state~="variant=ghost"]:active:enabled {
209
- background: var(
210
- --ui-button-ghost-active-surface,
211
- color-mix(in srgb, currentColor 14%, transparent)
212
- );
213
+ background-color: var(--ui-button-ghost-active-surface, oklch(from var(--_tone) l c h / 16%));
213
214
  border-color: transparent;
215
+ box-shadow: var(--ui-pressed);
214
216
  }
215
217
 
216
218
  /* A soft halo that transitions in with the other box-shadow changes, layered on the raised shadow. */
217
219
  [data-component~="ui-button"]:focus-visible {
218
220
  outline: none;
219
221
  box-shadow:
220
- var(--ui-button-shadow, var(--ui-raised, var(--ui-shadow-xs))),
222
+ var(--ui-button-shadow, var(--ui-raised)),
221
223
  var(--ui-focus-halo);
222
224
  }
223
225
 
224
226
  [data-component~="ui-button"][data-ui-button-state~="variant=solid"]:focus-visible {
225
227
  box-shadow:
226
- var(--ui-button-solid-shadow, inset 0 1px rgb(255 255 255 / 0.16), var(--ui-shadow-xs)),
228
+ var(--ui-button-solid-shadow, var(--ui-raised)),
227
229
  var(--ui-focus-halo);
228
230
  }
229
231
 
230
- /* Disabled colours fall back to each variant's own; ghost and link take only the text colour. */
232
+ /* Disabled drops the tone rather than the shape: every variant keeps its own structure, so a
233
+ disabled outline is still an outline and a disabled ghost is still text. Intent goes — an
234
+ unavailable action says "unavailable", not "unavailable, and destructive" — and so do the
235
+ highlight and shadow, because nothing that cannot be pressed should look raised. */
231
236
  [data-component~="ui-button"]:disabled {
237
+ /* Derived from the tone itself, not from a separate disabled palette: faded toward the
238
+ page, which desaturates it at the same time. Toward the page rather than toward white,
239
+ so a dark theme dims where a light one lightens — the same rule, both schemes. */
240
+ --_tone: color-mix(in oklab, var(--_hue) 58%, var(--ui-surface));
241
+ --_tone-hover: var(--_tone);
242
+ --_tone-active: var(--_tone);
243
+ --_tone-text: color-mix(in oklab, var(--_hue-text) 72%, var(--ui-surface));
244
+ --_on-tone: var(--_tone-text);
245
+
232
246
  opacity: var(--ui-button-disabled-opacity, 1);
233
247
  cursor: not-allowed;
234
- border-color: var(--ui-button-disabled-border, var(--ui-border));
235
- background-color: var(--ui-button-disabled-surface, var(--ui-disabled-surface));
236
- color: var(--ui-button-disabled-text, var(--ui-disabled-text));
237
- }
238
-
239
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"]:disabled {
240
- border-color: var(--ui-button-disabled-border, var(--ui-border));
241
- background-color: var(--ui-button-disabled-surface, var(--ui-disabled-surface));
242
- color: var(--ui-button-disabled-text, var(--ui-disabled-text));
248
+ background-image: none;
249
+ box-shadow: none;
243
250
  }
244
251
 
245
- [data-component~="ui-button"][data-ui-button-state~="variant=danger"]:disabled {
246
- border-color: var(--ui-button-disabled-border, var(--ui-border));
247
- background-color: var(--ui-button-disabled-surface, var(--ui-disabled-surface));
248
- color: var(--ui-button-disabled-text, var(--ui-disabled-text));
252
+ /* A filled button keeps its fill, faded further so its text stays legible on it. */
253
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"], [data-component~="ui-button"][data-ui-button-state~="variant=danger"]):disabled {
254
+ --_tone: color-mix(in oklab, var(--_hue) 34%, var(--ui-surface));
249
255
  }
250
256
 
257
+ /* Ghost has no hover to fall back on, so disabled is the one state it has to state outright:
258
+ it takes a surface, light and tinted, rather than reading as plain text. */
251
259
  [data-component~="ui-button"][data-ui-button-state~="variant=ghost"]:disabled {
252
- background: none;
260
+ background-color: color-mix(in oklab, var(--_hue) 28%, var(--ui-surface));
253
261
  border-color: transparent;
254
- color: var(--ui-button-disabled-text, var(--ui-disabled-text));
255
262
  }
256
263
 
257
264
  @media (pointer: coarse) {
package/vue/UiCallout.vue CHANGED
@@ -1,4 +1,4 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
3
  import { computed } from 'vue'
4
4
 
@@ -1,15 +1,8 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
- import {
4
- computed,
5
- onBeforeUnmount,
6
- onMounted,
7
- ref,
8
- shallowRef,
9
- useTemplateRef,
10
- watchEffect,
11
- } from 'vue'
3
+ import { computed, ref, useTemplateRef } from 'vue'
12
4
  import * as controllerModule from '../components/ui-checkbox/ui-checkbox.js'
5
+ import { createDispatch, useComponentHost } from './host'
13
6
 
14
7
  defineOptions({ inheritAttrs: false })
15
8
 
@@ -52,121 +45,27 @@ const hostState = computed(() =>
52
45
  ].filter(Boolean).join(' ')
53
46
  )
54
47
 
55
- /** Dispatches a component event to Vue listeners and, for controllers and page code, on the root. */
56
- function dispatch(name: string, detail?: unknown): boolean {
57
- const declared: Record<string, EventInit | undefined> = {
58
- change: { bubbles: true, composed: true, cancelable: false },
59
- }
60
- const checks: Record<string, (detail: unknown) => boolean> = {
61
- change: (detail) =>
62
- detail !== null && typeof detail === 'object' && !Array.isArray(detail)
63
- && typeof (detail as Record<string, unknown>)['checked'] === 'boolean'
64
- && typeof (detail as Record<string, unknown>)['value'] === 'string'
65
- && ((detail as Record<string, unknown>)['trigger'] === 'keyboard'
66
- || (detail as Record<string, unknown>)['trigger'] === 'pointer'
67
- || (detail as Record<string, unknown>)['trigger'] === 'programmatic')
68
- && Object.keys(detail as object).every((key) =>
69
- ['checked', 'value', 'trigger'].includes(key)
70
- ),
71
- }
72
- if (detail !== undefined && checks[name] !== undefined && !checks[name]!(detail)) {
73
- throw new TypeError(`HR002: Event \`${name}\` detail does not satisfy its declared type.`)
74
- }
75
- const emitEvent = emit as (name: string, detail: unknown) => void
76
- emitEvent(name, detail)
77
- if (detail !== null && typeof detail === 'object' && 'checked' in detail) {
78
- emitEvent('update:checked', (detail as Record<string, unknown>)['checked'])
79
- }
80
- if (detail !== null && typeof detail === 'object' && 'value' in detail) {
81
- emitEvent('update:value', (detail as Record<string, unknown>)['value'])
82
- }
83
- return root.value?.dispatchEvent(
84
- new CustomEvent(name, { bubbles: true, composed: true, ...declared[name], detail }),
85
- ) ?? true
86
- }
87
-
88
- function read(name: string): unknown {
89
- if (name === 'internalChecked') return internalChecked.value
90
- return (props as Record<string, unknown>)[name]
91
- }
92
-
93
- function write(name: string, value: unknown): boolean {
94
- if (name === 'internalChecked') {
95
- internalChecked.value = value as never
96
- return true
97
- }
98
- throw new TypeError(`Only declared state is writable; \`${name}\` is not.`)
99
- }
100
-
101
- const stops: Array<() => void> = []
102
- const host = {
103
- get element(): Element {
104
- return root.value as Element
105
- },
106
- state: new Proxy({} as Record<string, unknown>, {
107
- get: (_target, name) => typeof name === 'string' ? read(name) : undefined,
108
- set: (_target, name, value) => typeof name === 'string' && write(name, value),
109
- }),
110
- refs: {
111
- get input(): Element {
112
- return inputElement.value as Element
113
- },
114
- } as Readonly<Record<string, Element>>,
115
- elements: new Proxy({} as Record<string, Element | RadioNodeList | undefined>, {
116
- get: (_target, name) => {
117
- if (typeof name !== 'string' || root.value === null) return undefined
118
- const form = root.value instanceof HTMLFormElement
119
- ? root.value
120
- : root.value.querySelector('form')
121
- return form?.elements.namedItem(name)
122
- ?? root.value.querySelector(`[name="${CSS.escape(name)}"]`) ?? undefined
123
- },
124
- }),
125
- signal<T>(initialValue: T) {
126
- const value = shallowRef(initialValue)
127
- return {
128
- get: (): T => value.value,
129
- set: (next: T): void => {
130
- if (!Object.is(value.value, next)) value.value = next
131
- },
132
- update: (next: (current: T) => T): void => {
133
- const updated = next(value.value)
134
- if (!Object.is(value.value, updated)) value.value = updated
135
- },
136
- }
48
+ const isChangeDetail = (detail: unknown): boolean =>
49
+ detail !== null && typeof detail === 'object' && !Array.isArray(detail)
50
+ && typeof (detail as Record<string, unknown>)['checked'] === 'boolean'
51
+ && typeof (detail as Record<string, unknown>)['value'] === 'string'
52
+ && ((detail as Record<string, unknown>)['trigger'] === 'keyboard'
53
+ || (detail as Record<string, unknown>)['trigger'] === 'pointer'
54
+ || (detail as Record<string, unknown>)['trigger'] === 'programmatic')
55
+ && Object.keys(detail as object).every((key) => ['checked', 'value', 'trigger'].includes(key))
56
+ const dispatch = createDispatch(root, emit as (name: string, detail: unknown) => void, {
57
+ checks: {
58
+ change: isChangeDetail,
137
59
  },
138
- computed<T>(compute: () => T) {
139
- const value = computed(compute)
140
- return { get: (): T => value.value }
141
- },
142
- effect(run: () => void | (() => void)): () => void {
143
- const stop = watchEffect((onCleanup) => {
144
- const cleanup = run()
145
- if (typeof cleanup === 'function') onCleanup(cleanup)
146
- }, { flush: 'post' })
147
- stops.push(stop)
148
- return stop
149
- },
150
- on(event: string, listener: EventListener): () => void {
151
- const element = root.value
152
- element?.addEventListener(event, listener)
153
- const off = (): void => element?.removeEventListener(event, listener)
154
- stops.push(off)
155
- return off
156
- },
157
- dispatch,
158
- }
159
-
160
- let cleanup: void | (() => void)
161
- let ready: Promise<void> | undefined
162
- onMounted(() => {
163
- ready = Promise.resolve(controllerModule.default(host as never)).then((result) => {
164
- cleanup = result
165
- })
60
+ modeled: ['checked', 'value'],
166
61
  })
167
- onBeforeUnmount(() => {
168
- for (const stop of stops.splice(0)) stop()
169
- if (typeof cleanup === 'function') cleanup()
62
+
63
+ useComponentHost(controllerModule.default, {
64
+ root,
65
+ dispatch,
66
+ props,
67
+ refs: { input: inputElement },
68
+ state: { internalChecked },
170
69
  })
171
70
  </script>
172
71
 
@@ -241,7 +140,10 @@ input:checked::after {
241
140
  margin: auto;
242
141
  inline-size: 0.32rem;
243
142
  block-size: 0.58rem;
244
- border: solid var(--ui-text-on-accent);
143
+ /* Longhands, not `border: solid var(...)`: lowering drops a shorthand that omits a
144
+ component, which left the tick with no style and no colour. */
145
+ border-style: solid;
146
+ border-color: var(--ui-text-on-accent);
245
147
  border-width: 0 0.125rem 0.125rem 0;
246
148
  opacity: 1;
247
149
  transform: translateY(-0.11rem) rotate(45deg);
package/vue/UiCluster.vue CHANGED
@@ -1,4 +1,4 @@
1
- <!-- Generated by HTML Next 1.0.0-alpha.2 for Vue 3.5. Do not edit. -->
1
+ <!-- Generated by HTML Next 1.0.0-alpha.3 for Vue 3.5. Do not edit. -->
2
2
  <script setup lang="ts">
3
3
  import { computed } from 'vue'
4
4
 
@@ -234,8 +234,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
234
234
  readonly trigger: "keyboard" | "pointer" | "programmatic";
235
235
  }) => any) | undefined;
236
236
  }>, {
237
- label: string;
238
237
  name: string;
238
+ label: string;
239
239
  disabled: boolean;
240
240
  size: "sm" | "md";
241
241
  required: boolean;