@threadlabs/looma 0.17.3 → 0.18.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 (71) hide show
  1. package/components/ui-avatar/ui-avatar.html +8 -4
  2. package/components/ui-badge/ui-badge.html +28 -34
  3. package/components/ui-button/ui-button.html +5 -0
  4. package/components/ui-checkbox/ui-checkbox.html +5 -5
  5. package/components/ui-combobox/ui-combobox.html +4 -48
  6. package/components/ui-icon-button/ui-icon-button.html +7 -7
  7. package/components/ui-input/ui-input.html +1 -1
  8. package/components/ui-input-group/ui-input-group.html +2 -2
  9. package/components/ui-listbox/ui-listbox.html +88 -12
  10. package/components/ui-listbox/ui-listbox.js +162 -18
  11. package/components/ui-search-result-row/ui-search-result-row.html +2 -6
  12. package/components/ui-search-shell/ui-search-shell.html +2 -11
  13. package/components/ui-select/ui-select.html +1 -1
  14. package/components/ui-spinner/ui-spinner.html +45 -18
  15. package/components/ui-textarea/ui-textarea.html +1 -1
  16. package/components/ui-tooltip/ui-tooltip.html +2 -2
  17. package/dist/index.js +1 -1
  18. package/editor/chunks/extensions.js +5 -0
  19. package/package.json +1 -1
  20. package/styles/ui-avatar.css +6 -2
  21. package/styles/ui-badge.css +30 -40
  22. package/styles/ui-button.css +7 -0
  23. package/styles/ui-checkbox.css +6 -6
  24. package/styles/ui-combobox.css +0 -52
  25. package/styles/ui-icon-button.css +7 -7
  26. package/styles/ui-input-group.css +2 -2
  27. package/styles/ui-input.css +1 -1
  28. package/styles/ui-listbox.css +33 -8
  29. package/styles/ui-search-result-row.css +2 -6
  30. package/styles/ui-search-shell.css +2 -13
  31. package/styles/ui-select.css +1 -1
  32. package/styles/ui-spinner.css +38 -19
  33. package/styles/ui-textarea.css +1 -1
  34. package/styles/ui-tooltip.css +2 -2
  35. package/tokens.css +67 -2
  36. package/vanilla/UiCombobox.js +9 -1
  37. package/vanilla/UiListbox.d.ts +3 -1
  38. package/vanilla/UiListbox.js +65 -18
  39. package/vanilla/UiSpinner.js +19 -1
  40. package/vue/UiAvatar.vue +11 -2
  41. package/vue/UiBadge.vue +30 -36
  42. package/vue/UiButton.vue +5 -0
  43. package/vue/UiCheckbox.vue +5 -5
  44. package/vue/UiCombobox.vue +4 -49
  45. package/vue/UiIconButton.vue +10 -7
  46. package/vue/UiInput.vue +1 -1
  47. package/vue/UiInputGroup.vue +2 -2
  48. package/vue/UiListbox.d.ts +18 -5
  49. package/vue/UiListbox.vue +142 -25
  50. package/vue/UiSearchResultRow.vue +2 -6
  51. package/vue/UiSearchShell.vue +2 -13
  52. package/vue/UiSelect.vue +1 -1
  53. package/vue/UiSpinner.vue +45 -18
  54. package/vue/UiTextarea.vue +1 -1
  55. package/vue/UiTooltip.vue +2 -2
  56. package/vue/chunks/UiAvatar.js +2 -1
  57. package/vue/chunks/UiBadge.js +1 -1
  58. package/vue/chunks/UiButton.js +1 -1
  59. package/vue/chunks/UiCheckbox.js +1 -1
  60. package/vue/chunks/UiCombobox.js +11 -2
  61. package/vue/chunks/UiIconButton.js +1 -1
  62. package/vue/chunks/UiInput.js +1 -1
  63. package/vue/chunks/UiInputGroup.js +1 -1
  64. package/vue/chunks/UiListbox.js +134 -20
  65. package/vue/chunks/UiSearchResultRow.js +1 -1
  66. package/vue/chunks/UiSearchShell.js +1 -1
  67. package/vue/chunks/UiSelect.js +1 -1
  68. package/vue/chunks/UiSpinner.js +29 -3
  69. package/vue/chunks/UiTextarea.js +1 -1
  70. package/vue/chunks/UiTooltip.js +1 -1
  71. package/vue/components.css +426 -440
@@ -3,40 +3,87 @@ import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
3
3
  import * as controller from "../components/ui-listbox/ui-listbox.js";
4
4
  import "../styles/ui-listbox.css";
5
5
 
6
- const definition = {...{"contract":{"tag":"ui-listbox","props":{"disabled":{"type":"boolean","required":false,"target":{"attribute":"disabled"},"default":false},"invalid":{"type":"boolean","required":false,"target":{"attribute":"aria-invalid"},"default":false},"multiple":{"type":"boolean","required":false,"target":{"attribute":"multiple"},"default":false},"required":{"type":"boolean","required":false,"target":{"attribute":"required"},"default":false},"rows":{"type":"number","required":false,"target":{"attribute":"size"},"default":5},"value":{"type":"string","required":false,"target":{"attribute":"value"}},"values":{"type":{"kind":"list","item":{"kind":"terminal","name":"string"}},"required":false,"target":{"attribute":"values"}}}},"template":{"kind":"element","name":"select","attributes":[{"kind":"attribute","name":"size","expression":"rows","expressionPlan":{"source":"rows","ast":{"kind":"id","name":"rows"},"dependencies":["rows"]}},{"kind":"attribute","name":"multiple","expression":"multiple","expressionPlan":{"source":"multiple","ast":{"kind":"id","name":"multiple"},"dependencies":["multiple"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"aria-invalid","expression":"invalid","expressionPlan":{"source":"invalid","ast":{"kind":"id","name":"invalid"},"dependencies":["invalid"]}}],"children":[{"kind":"slot"}]},"declarations":[],"root":{"kind":"native","element":"select","choices":["select"]}},source:{file:import.meta.url},css:""};
6
+ const definition = {...{"contract":{"tag":"ui-listbox","props":{"disabled":{"type":"boolean","required":false,"target":{"attribute":"aria-disabled"},"default":false},"invalid":{"type":"boolean","required":false,"target":{"attribute":"invalid"},"default":false},"multiple":{"type":"boolean","required":false,"target":{"attribute":"aria-multiselectable"},"default":false},"name":{"type":"string","required":false,"target":{"attribute":"name"},"default":""},"required":{"type":"boolean","required":false,"target":{"attribute":"aria-required"},"default":false},"rows":{"type":"number","required":false,"target":{"attribute":"size"},"default":5},"value":{"type":"string","required":false,"target":{"attribute":"value"}},"values":{"type":{"kind":"list","item":{"kind":"terminal","name":"string"}},"required":false,"target":{"attribute":"values"}}}},"template":{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"role","value":"listbox"},{"kind":"attribute","name":"tabindex","expression":"{ true: '-1', false: '0' }[format('%s', disabled)]","expressionPlan":{"source":"{ true: '-1', false: '0' }[format('%s', disabled)]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"literal","value":"-1"}},{"key":"false","value":{"kind":"literal","value":"0"}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"id","name":"disabled"}]}},"dependencies":["disabled"]}},{"kind":"attribute","name":"aria-multiselectable","expression":"multiple","expressionPlan":{"source":"multiple","ast":{"kind":"id","name":"multiple"},"dependencies":["multiple"]}},{"kind":"attribute","name":"aria-disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"aria-required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"aria-invalid","expression":"invalid or validationError","expressionPlan":{"source":"invalid or validationError","ast":{"kind":"binary","op":"or","left":{"kind":"id","name":"invalid"},"right":{"kind":"id","name":"validationError"}},"dependencies":["invalid","validationError"]}},{"kind":"attribute","name":"--_rows","expression":"rows","expressionPlan":{"source":"rows","ast":{"kind":"id","name":"rows"},"dependencies":["rows"]},"target":"style"}],"children":[{"kind":"element","name":"ui-scroll-area","attributes":[{"kind":"literal","name":"class","value":"choices"}],"children":[{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"class","value":"option"},{"kind":"attribute","name":"active","expression":"choice.index = active","expressionPlan":{"source":"choice.index = active","ast":{"kind":"binary","op":"=","left":{"kind":"member","object":{"kind":"id","name":"choice"},"key":"index"},"right":{"kind":"id","name":"active"}},"dependencies":["active","choice.index"]},"target":"class"},{"kind":"literal","name":"role","value":"option"},{"kind":"literal","name":"data-ui-choice-option","value":""},{"kind":"attribute","name":"data-multiple","expression":"multiple","expressionPlan":{"source":"multiple","ast":{"kind":"id","name":"multiple"},"dependencies":["multiple"]}},{"kind":"attribute","name":"id","expression":"format('%s-option-%s', uid, choice.index)","expressionPlan":{"source":"format('%s-option-%s', uid, choice.index)","ast":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s-option-%s"},{"kind":"id","name":"uid"},{"kind":"member","object":{"kind":"id","name":"choice"},"key":"index"}]},"dependencies":["choice.index","uid"]}},{"kind":"attribute","name":"data-index","expression":"choice.index","expressionPlan":{"source":"choice.index","ast":{"kind":"member","object":{"kind":"id","name":"choice"},"key":"index"},"dependencies":["choice.index"]}},{"kind":"attribute","name":"aria-selected","expression":"choice.selected","expressionPlan":{"source":"choice.selected","ast":{"kind":"member","object":{"kind":"id","name":"choice"},"key":"selected"},"dependencies":["choice.selected"]}},{"kind":"attribute","name":"aria-disabled","expression":"choice.disabled","expressionPlan":{"source":"choice.disabled","ast":{"kind":"member","object":{"kind":"id","name":"choice"},"key":"disabled"},"dependencies":["choice.disabled"]}}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"directive","name":"value","expression":"choice.label","expressionPlan":{"source":"choice.label","ast":{"kind":"member","object":{"kind":"id","name":"choice"},"key":"label"},"dependencies":["choice.label"]}}],"children":[]}],"flow":{"kind":"each","item":"choice","list":"choices","listPlan":{"source":"choices","ast":{"kind":"id","name":"choices"},"dependencies":["choices"]},"key":"choice.index","keyPlan":{"source":"choice.index","ast":{"kind":"member","object":{"kind":"id","name":"choice"},"key":"index"},"dependencies":["choice.index"]}}}],"ref":"choices"},{"kind":"element","name":"select","attributes":[{"kind":"literal","name":"class","value":"fallback"},{"kind":"attribute","name":"size","expression":"rows","expressionPlan":{"source":"rows","ast":{"kind":"id","name":"rows"},"dependencies":["rows"]}},{"kind":"attribute","name":"multiple","expression":"multiple","expressionPlan":{"source":"multiple","ast":{"kind":"id","name":"multiple"},"dependencies":["multiple"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"attribute","name":"required","expression":"required","expressionPlan":{"source":"required","ast":{"kind":"id","name":"required"},"dependencies":["required"]}},{"kind":"attribute","name":"name","expression":"name","expressionPlan":{"source":"name","ast":{"kind":"id","name":"name"},"dependencies":["name"]}},{"kind":"literal","name":"aria-label","value":"Choices"}],"children":[{"kind":"slot"}],"ref":"options"},{"kind":"element","name":"template","attributes":[],"children":[{"kind":"element","name":"input","attributes":[{"kind":"literal","name":"type","value":"hidden"},{"kind":"attribute","name":"name","expression":"name","expressionPlan":{"source":"name","ast":{"kind":"id","name":"name"},"dependencies":["name"]}},{"kind":"attribute","name":"value","expression":"selected","expressionPlan":{"source":"selected","ast":{"kind":"id","name":"selected"},"dependencies":["selected"]}}],"children":[],"flow":{"kind":"each","item":"selected","list":"selection","listPlan":{"source":"selection","ast":{"kind":"id","name":"selection"},"dependencies":["selection"]}}}],"flow":{"kind":"if","test":"name and not disabled","testPlan":{"source":"name and not disabled","ast":{"kind":"binary","op":"and","left":{"kind":"id","name":"name"},"right":{"kind":"unary","op":"not","operand":{"kind":"id","name":"disabled"}}},"dependencies":["disabled","name"]}}},{"kind":"element","name":"input","attributes":[{"kind":"literal","name":"class","value":"validity"},{"kind":"literal","name":"type","value":"checkbox"},{"kind":"attribute","name":"required","expression":"required and not hasSelection and not disabled","expressionPlan":{"source":"required and not hasSelection and not disabled","ast":{"kind":"binary","op":"and","left":{"kind":"binary","op":"and","left":{"kind":"id","name":"required"},"right":{"kind":"unary","op":"not","operand":{"kind":"id","name":"hasSelection"}}},"right":{"kind":"unary","op":"not","operand":{"kind":"id","name":"disabled"}}},"dependencies":["disabled","hasSelection","required"]}},{"kind":"attribute","name":"checked","expression":"hasSelection","expressionPlan":{"source":"hasSelection","ast":{"kind":"id","name":"hasSelection"},"dependencies":["hasSelection"]}},{"kind":"attribute","name":"disabled","expression":"disabled","expressionPlan":{"source":"disabled","ast":{"kind":"id","name":"disabled"},"dependencies":["disabled"]}},{"kind":"literal","name":"aria-hidden","value":"true"},{"kind":"literal","name":"tabindex","value":"-1"}],"children":[],"ref":"validity"}]},"declarations":[{"kind":"state","name":"choices","type":"list(object({ value: string, label: string, disabled: boolean, selected: boolean, index: integer }))","expression":{"source":"[]","ast":{"kind":"array","items":[]},"dependencies":[]}},{"kind":"state","name":"selection","type":"list(string)","expression":{"source":"[]","ast":{"kind":"array","items":[]},"dependencies":[]}},{"kind":"state","name":"hasSelection","type":"boolean","expression":{"source":"false","ast":{"kind":"literal","value":false},"dependencies":[]}},{"kind":"state","name":"active","type":"integer","expression":{"source":"-1","ast":{"kind":"unary","op":"-","operand":{"kind":"literal","value":1}},"dependencies":[]}},{"kind":"state","name":"uid","type":"string","expression":{"source":"''","ast":{"kind":"literal","value":""},"dependencies":[]}},{"kind":"state","name":"validationError","type":"boolean","expression":{"source":"false","ast":{"kind":"literal","value":false},"dependencies":[]}},{"kind":"event","name":"change","type":"object({ value: string | null, values: list(string), trigger: keyboard | pointer })","bubbles":true,"composed":true,"cancelable":false}],"root":{"kind":"native","element":"div","choices":["div"]}},source:{file:import.meta.url},css:""};
7
7
 
8
8
  export function createUiListbox(options = {}) {
9
9
  const { attributes = {}, children = [], slots = {}, ...componentProps } = options;
10
10
  const projected = [];
11
- const element = document.createElement("select");
11
+ const element = document.createElement("div");
12
12
  for (const [name, value] of Object.entries(attributes)) {
13
13
  if (value === null || value === undefined || value === false) continue;
14
14
  element.setAttribute(name, value === true ? "" : String(value));
15
15
  }
16
- const value0 = componentProps["rows"] === undefined ? 5 : componentProps["rows"];
17
- if (value0 === null || value0 === undefined) element.removeAttribute("size");
18
- else element.setAttribute("size", String(value0));
19
- const value1 = componentProps["multiple"] === undefined ? false : componentProps["multiple"];
20
- if (value1 === true) element.setAttribute("multiple", "");
21
- else element.removeAttribute("multiple");
22
- const value2 = componentProps["disabled"] === undefined ? false : componentProps["disabled"];
23
- if (value2 === true) element.setAttribute("disabled", "");
24
- else element.removeAttribute("disabled");
25
- const value3 = componentProps["required"] === undefined ? false : componentProps["required"];
26
- if (value3 === true) element.setAttribute("required", "");
27
- else element.removeAttribute("required");
28
- const value4 = componentProps["invalid"] === undefined ? false : componentProps["invalid"];
29
- if (typeof value4 === "boolean") element.setAttribute("aria-invalid", String(value4));
30
- else element.removeAttribute("aria-invalid");
16
+ if (!element.hasAttribute("role")) element.setAttribute("role", "listbox");
17
+ const value0 = componentProps["multiple"] === undefined ? false : componentProps["multiple"];
18
+ if (typeof value0 === "boolean") element.setAttribute("aria-multiselectable", String(value0));
19
+ else element.removeAttribute("aria-multiselectable");
20
+ const value1 = componentProps["disabled"] === undefined ? false : componentProps["disabled"];
21
+ if (typeof value1 === "boolean") element.setAttribute("aria-disabled", String(value1));
22
+ else element.removeAttribute("aria-disabled");
23
+ const value2 = componentProps["required"] === undefined ? false : componentProps["required"];
24
+ if (typeof value2 === "boolean") element.setAttribute("aria-required", String(value2));
25
+ else element.removeAttribute("aria-required");
26
+ const value3 = componentProps["rows"] === undefined ? 5 : componentProps["rows"];
27
+ if (value3 === null || value3 === undefined) element.removeAttribute("--_rows");
28
+ else element.setAttribute("--_rows", String(value3));
31
29
  element.setAttribute("data-component", "ui-listbox");
30
+ const element0 = document.createElement("ui-scroll-area");
31
+ element0.setAttribute("class", "choices");
32
+ const element1 = document.createElement("div");
33
+ element1.setAttribute("class", "option");
34
+ element1.setAttribute("role", "option");
35
+ element1.setAttribute("data-ui-choice-option", "");
36
+ const value4 = componentProps["multiple"] === undefined ? false : componentProps["multiple"];
37
+ if (value4 === true) element1.setAttribute("data-multiple", "");
38
+ else element1.removeAttribute("data-multiple");
39
+ const element2 = document.createElement("span");
40
+ element1.append(element2);
41
+ element0.append(element1);
42
+ element.append(element0);
43
+ const element3 = document.createElement("select");
44
+ element3.setAttribute("class", "fallback");
45
+ const value5 = componentProps["rows"] === undefined ? 5 : componentProps["rows"];
46
+ if (value5 === null || value5 === undefined) element3.removeAttribute("size");
47
+ else element3.setAttribute("size", String(value5));
48
+ const value6 = componentProps["multiple"] === undefined ? false : componentProps["multiple"];
49
+ if (value6 === true) element3.setAttribute("multiple", "");
50
+ else element3.removeAttribute("multiple");
51
+ const value7 = componentProps["disabled"] === undefined ? false : componentProps["disabled"];
52
+ if (value7 === true) element3.setAttribute("disabled", "");
53
+ else element3.removeAttribute("disabled");
54
+ const value8 = componentProps["required"] === undefined ? false : componentProps["required"];
55
+ if (value8 === true) element3.setAttribute("required", "");
56
+ else element3.removeAttribute("required");
57
+ const value9 = componentProps["name"] === undefined ? "" : componentProps["name"];
58
+ if (value9 === null || value9 === undefined) element3.removeAttribute("name");
59
+ else element3.setAttribute("name", String(value9));
60
+ element3.setAttribute("aria-label", "Choices");
32
61
  if (children.length > 0) {
33
62
  for (const child of children) {
34
63
  const node = typeof child === "string" ? document.createTextNode(child) : child;
35
64
  projected.push([node, ""]);
36
- element.append(node);
65
+ element3.append(node);
37
66
  }
38
67
  } else {
39
68
  }
69
+ element.append(element3);
70
+ const element4 = document.createElement("template");
71
+ const element5 = document.createElement("input");
72
+ element5.setAttribute("type", "hidden");
73
+ const value10 = componentProps["name"] === undefined ? "" : componentProps["name"];
74
+ if (value10 === null || value10 === undefined) element5.removeAttribute("name");
75
+ else element5.setAttribute("name", String(value10));
76
+ element4.append(element5);
77
+ element.append(element4);
78
+ const element6 = document.createElement("input");
79
+ element6.setAttribute("class", "validity");
80
+ element6.setAttribute("type", "checkbox");
81
+ const value11 = componentProps["disabled"] === undefined ? false : componentProps["disabled"];
82
+ if (value11 === true) element6.setAttribute("disabled", "");
83
+ else element6.removeAttribute("disabled");
84
+ element6.setAttribute("aria-hidden", "true");
85
+ element6.setAttribute("tabindex", "-1");
86
+ element.append(element6);
40
87
  manageComponentLifecycle(element, definition, { props: componentProps, projected,
41
88
  controller,
42
89
  });
@@ -2,7 +2,7 @@
2
2
  import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
3
3
  import "../styles/ui-spinner.css";
4
4
 
5
- const definition = {...{"contract":{"tag":"ui-spinner","props":{"label":{"type":"string","required":false,"target":{"attribute":"label"},"default":""},"size":{"type":{"enum":["sm","md"]},"required":false,"target":{"attribute":"size"},"default":"md"}}},"template":{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"ring"},{"kind":"attribute","name":"role","expression":"{ true: 'status', false: null }[format('%s', label != '')]","expressionPlan":{"source":"{ true: 'status', false: null }[format('%s', label != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"literal","value":"status"}},{"key":"false","value":{"kind":"literal","value":null}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"label"},"right":{"kind":"literal","value":""}}]}},"dependencies":["label"]}},{"kind":"attribute","name":"aria-label","expression":"{ true: label, false: null }[format('%s', label != '')]","expressionPlan":{"source":"{ true: label, false: null }[format('%s', label != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"id","name":"label"}},{"key":"false","value":{"kind":"literal","value":null}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"label"},"right":{"kind":"literal","value":""}}]}},"dependencies":["label"]}},{"kind":"attribute","name":"aria-hidden","expression":"{ true: null, false: 'true' }[format('%s', label != '')]","expressionPlan":{"source":"{ true: null, false: 'true' }[format('%s', label != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"literal","value":null}},{"key":"false","value":{"kind":"literal","value":"true"}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"label"},"right":{"kind":"literal","value":""}}]}},"dependencies":["label"]}}],"children":[]},"declarations":[],"root":{"kind":"native","element":"span","choices":["span"]}},source:{file:import.meta.url},css:""};
5
+ const definition = {...{"contract":{"tag":"ui-spinner","props":{"label":{"type":"string","required":false,"target":{"attribute":"label"},"default":""},"size":{"type":{"enum":["sm","md"]},"required":false,"target":{"attribute":"size"},"default":"md"}}},"template":{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"ring"},{"kind":"attribute","name":"role","expression":"{ true: 'status', false: null }[format('%s', label != '')]","expressionPlan":{"source":"{ true: 'status', false: null }[format('%s', label != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"literal","value":"status"}},{"key":"false","value":{"kind":"literal","value":null}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"label"},"right":{"kind":"literal","value":""}}]}},"dependencies":["label"]}},{"kind":"attribute","name":"aria-label","expression":"{ true: label, false: null }[format('%s', label != '')]","expressionPlan":{"source":"{ true: label, false: null }[format('%s', label != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"id","name":"label"}},{"key":"false","value":{"kind":"literal","value":null}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"label"},"right":{"kind":"literal","value":""}}]}},"dependencies":["label"]}},{"kind":"attribute","name":"aria-live","expression":"{ true: 'polite', false: null }[format('%s', label != '')]","expressionPlan":{"source":"{ true: 'polite', false: null }[format('%s', label != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"literal","value":"polite"}},{"key":"false","value":{"kind":"literal","value":null}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"label"},"right":{"kind":"literal","value":""}}]}},"dependencies":["label"]}},{"kind":"attribute","name":"aria-hidden","expression":"{ true: null, false: 'true' }[format('%s', label != '')]","expressionPlan":{"source":"{ true: null, false: 'true' }[format('%s', label != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"literal","value":null}},{"key":"false","value":{"kind":"literal","value":"true"}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"label"},"right":{"kind":"literal","value":""}}]}},"dependencies":["label"]}}],"children":[{"kind":"element","name":"svg","attributes":[{"kind":"literal","name":"viewBox","value":"0 0 50 50"},{"kind":"literal","name":"fill","value":"none"},{"kind":"literal","name":"aria-hidden","value":"true"},{"kind":"literal","name":"focusable","value":"false"}],"children":[{"kind":"element","name":"circle","attributes":[{"kind":"literal","name":"class","value":"track"},{"kind":"literal","name":"cx","value":"25"},{"kind":"literal","name":"cy","value":"25"},{"kind":"literal","name":"r","value":"20"}],"children":[]},{"kind":"element","name":"circle","attributes":[{"kind":"literal","name":"class","value":"arc"},{"kind":"literal","name":"cx","value":"25"},{"kind":"literal","name":"cy","value":"25"},{"kind":"literal","name":"r","value":"20"}],"children":[]}]}]},"declarations":[],"root":{"kind":"native","element":"span","choices":["span"]}},source:{file:import.meta.url},css:""};
6
6
 
7
7
  export function createUiSpinner(options = {}) {
8
8
  const { attributes = {}, children = [], slots = {}, ...componentProps } = options;
@@ -14,6 +14,24 @@ export function createUiSpinner(options = {}) {
14
14
  }
15
15
  element.setAttribute("class", ["ring", element.getAttribute("class")].filter(Boolean).join(" "));
16
16
  element.setAttribute("data-component", "ui-spinner");
17
+ const element0 = document.createElementNS("http://www.w3.org/2000/svg", "svg");
18
+ element0.setAttribute("viewBox", "0 0 50 50");
19
+ element0.setAttribute("fill", "none");
20
+ element0.setAttribute("aria-hidden", "true");
21
+ element0.setAttribute("focusable", "false");
22
+ const element1 = document.createElementNS("http://www.w3.org/2000/svg", "circle");
23
+ element1.setAttribute("class", "track");
24
+ element1.setAttribute("cx", "25");
25
+ element1.setAttribute("cy", "25");
26
+ element1.setAttribute("r", "20");
27
+ element0.append(element1);
28
+ const element2 = document.createElementNS("http://www.w3.org/2000/svg", "circle");
29
+ element2.setAttribute("class", "arc");
30
+ element2.setAttribute("cx", "25");
31
+ element2.setAttribute("cy", "25");
32
+ element2.setAttribute("r", "20");
33
+ element0.append(element2);
34
+ element.append(element0);
17
35
  manageComponentLifecycle(element, definition, { props: componentProps, projected,
18
36
  });
19
37
  return element;
package/vue/UiAvatar.vue CHANGED
@@ -40,6 +40,7 @@ const showGlyph = ref(false)
40
40
  const hostState = computed(() =>
41
41
  [
42
42
  props.size && `size size=${props.size}`,
43
+ hasImage.value && 'hasImage',
43
44
  props.shape && `shape shape=${props.shape}`,
44
45
  props.active && 'active',
45
46
  ].filter(Boolean).join(' ')
@@ -108,7 +109,11 @@ useComponentHost(controllerModule.default, {
108
109
  inline-size: 2.5rem;
109
110
  block-size: 2.5rem;
110
111
  border-radius: var(--ui-avatar-radius, var(--ui-radius-round));
111
- border: 1px solid var(--ui-avatar-border, var(--ui-border-strong));
112
+ border: 1px solid
113
+ var(
114
+ --ui-avatar-border,
115
+ color-mix(in srgb, var(--ui-accent-solid) 35%, var(--ui-surface-default))
116
+ );
112
117
  background: var(--ui-avatar-surface, var(--ui-accent-soft));
113
118
  color: var(--ui-avatar-text, var(--ui-accent-subtle-text, var(--ui-accent-solid)));
114
119
  font: inherit;
@@ -124,6 +129,10 @@ useComponentHost(controllerModule.default, {
124
129
  font-size: var(--ui-font-size-xs);
125
130
  }
126
131
 
132
+ [data-component~="ui-avatar"][data-ui-avatar-state~="hasImage"] {
133
+ border-color: var(--ui-avatar-border, var(--ui-border-strong));
134
+ }
135
+
127
136
  [data-component~="ui-avatar"][data-ui-avatar-state~="shape=square"] {
128
137
  border-radius: var(--ui-avatar-radius, var(--ui-radius-md));
129
138
  }
@@ -131,7 +140,7 @@ useComponentHost(controllerModule.default, {
131
140
  /* An outline, not a dot: the avatar clips its content to the circle, and an outline is drawn
132
141
  outside the box, so the ring survives the clip and the overlap in an Avatar Group. */
133
142
  [data-component~="ui-avatar"][data-ui-avatar-state~="active"] {
134
- outline: 2px solid var(--ui-avatar-active-ring, var(--ui-success));
143
+ outline: 1px solid var(--ui-avatar-active-ring, var(--ui-accent-solid));
135
144
  outline-offset: 1px;
136
145
  }
137
146
 
package/vue/UiBadge.vue CHANGED
@@ -83,7 +83,7 @@ const hostState = computed(() =>
83
83
  }
84
84
 
85
85
  [data-component~="ui-badge"] {
86
- --_badge-surface: var(--ui-surface-subtle);
86
+ --_badge-surface: color-mix(in srgb, var(--ui-surface-subtle) 80%, var(--ui-surface-default));
87
87
  --_badge-text: var(--ui-text-secondary);
88
88
  /* Sit between the soft fill and its stronger theme ink. */
89
89
  --_badge-border: color-mix(in srgb, var(--_badge-surface) 75%, var(--_badge-text) 25%);
@@ -91,7 +91,7 @@ const hostState = computed(() =>
91
91
  surface, text, and border hooks. Each value is invalid, so the tone shows, while it is unset. The
92
92
  mixes keep 4.5:1 text contrast for any colour, in a light theme or a dark one. */
93
93
  --_ui-badge-color: var(--ui-badge-color);
94
- --_badge-color-wash: color-mix(in oklab, var(--_ui-badge-color) 15%, var(--ui-surface-default));
94
+ --_badge-color-wash: color-mix(in oklab, var(--_ui-badge-color) 13%, var(--ui-surface-default));
95
95
  --_badge-color-ink: color-mix(in oklab, var(--_ui-badge-color) 35%, var(--ui-text-primary));
96
96
  --_badge-color-surface: var(--_badge-color-wash);
97
97
  --_badge-color-border: color-mix(
@@ -167,32 +167,32 @@ const hostState = computed(() =>
167
167
  }
168
168
 
169
169
  [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=accent"] {
170
- --_badge-surface: color-mix(in srgb, var(--ui-accent-solid) 22%, var(--ui-surface-default));
171
- --_badge-border: color-mix(in srgb, var(--ui-accent-solid) 28%, var(--ui-surface-default));
170
+ --_badge-surface: color-mix(in srgb, var(--ui-accent-solid) 19%, var(--ui-surface-default));
171
+ --_badge-border: color-mix(in srgb, var(--ui-accent-solid) 25%, var(--ui-surface-default));
172
172
  --_badge-text: var(--ui-accent-subtle-text, var(--ui-accent-solid));
173
173
  }
174
174
 
175
175
  [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=danger"] {
176
- --_badge-surface: color-mix(in srgb, var(--ui-danger-solid) 22%, var(--ui-surface-default));
177
- --_badge-border: color-mix(in srgb, var(--ui-danger-solid) 28%, var(--ui-surface-default));
176
+ --_badge-surface: color-mix(in srgb, var(--ui-danger-solid) 19%, var(--ui-surface-default));
177
+ --_badge-border: color-mix(in srgb, var(--ui-danger-solid) 25%, var(--ui-surface-default));
178
178
  --_badge-text: var(--ui-danger-subtle-text, var(--ui-danger-solid));
179
179
  }
180
180
 
181
181
  [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=success"] {
182
- --_badge-surface: color-mix(in srgb, var(--ui-success-solid) 22%, var(--ui-surface-default));
183
- --_badge-border: color-mix(in srgb, var(--ui-success-solid) 28%, var(--ui-surface-default));
182
+ --_badge-surface: color-mix(in srgb, var(--ui-success-solid) 19%, var(--ui-surface-default));
183
+ --_badge-border: color-mix(in srgb, var(--ui-success-solid) 25%, var(--ui-surface-default));
184
184
  --_badge-text: var(--ui-success-subtle-text, var(--ui-success-solid));
185
185
  }
186
186
 
187
187
  [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=warning"] {
188
- --_badge-surface: color-mix(in srgb, var(--ui-warning-solid) 22%, var(--ui-surface-default));
189
- --_badge-border: color-mix(in srgb, var(--ui-warning-solid) 28%, var(--ui-surface-default));
188
+ --_badge-surface: color-mix(in srgb, var(--ui-warning-solid) 19%, var(--ui-surface-default));
189
+ --_badge-border: color-mix(in srgb, var(--ui-warning-solid) 25%, var(--ui-surface-default));
190
190
  --_badge-text: var(--ui-warning-subtle-text, var(--ui-warning-solid));
191
191
  }
192
192
 
193
193
  [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=info"] {
194
- --_badge-surface: color-mix(in srgb, var(--ui-info-solid) 22%, var(--ui-surface-default));
195
- --_badge-border: color-mix(in srgb, var(--ui-info-solid) 28%, var(--ui-surface-default));
194
+ --_badge-surface: color-mix(in srgb, var(--ui-info-solid) 19%, var(--ui-surface-default));
195
+ --_badge-border: color-mix(in srgb, var(--ui-info-solid) 25%, var(--ui-surface-default));
196
196
  --_badge-text: var(--ui-info-subtle-text, var(--ui-info-solid));
197
197
  }
198
198
 
@@ -238,14 +238,16 @@ const hostState = computed(() =>
238
238
  --_badge-text: var(--ui-info-subtle-text, var(--ui-info-solid));
239
239
  }
240
240
 
241
- /* A tag is flat at the start and points at the end. The point is cut from the box, so it has no
242
- outline to follow: the edge is the fill. The end padding grows by the point's depth so the
243
- label never runs into it. */
241
+ /* The clipped point needs an inset surface over an outer border-colour shape: a native border
242
+ has no diagonal edge to follow. Keep this edge for every tag variant. */
244
243
  [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"] {
245
244
  --_badge-point: var(--ui-badge-point, var(--ui-space-2));
246
245
 
246
+ position: relative;
247
+ isolation: isolate;
247
248
  border-color: transparent;
248
249
  border-radius: 0;
250
+ background: var(--ui-badge-border, var(--_badge-color-border, var(--_badge-border)));
249
251
  padding-inline-end: calc(var(--ui-badge-padding-inline, var(--ui-space-2)) + var(--_badge-point));
250
252
  clip-path: polygon(
251
253
  0 0,
@@ -256,26 +258,10 @@ const hostState = computed(() =>
256
258
  );
257
259
  }
258
260
 
259
- [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"]:dir(rtl) {
260
- padding-inline-end: var(--ui-badge-padding-inline, var(--ui-space-2));
261
- padding-inline-start: calc(
262
- var(--ui-badge-padding-inline, var(--ui-space-2)) + var(--_badge-point)
263
- );
264
- clip-path: polygon(var(--_badge-point) 0, 100% 0, 100% 100%, var(--_badge-point) 100%, 0 50%);
265
- }
266
-
267
- /* The tag clips its point, including the native border. Draw the outer shape in the border
268
- colour and an inset inner shape in the surface colour to keep a visible diagonal edge. */
269
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="shape=tag"] {
270
- position: relative;
271
- isolation: isolate;
272
- background: var(--ui-badge-border, var(--_badge-color-border, var(--_badge-border)));
273
- }
274
-
275
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="shape=tag"]::before {
261
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"]::before {
276
262
  content: "";
277
263
  position: absolute;
278
- inset: 1px;
264
+ inset: 0;
279
265
  z-index: -1;
280
266
  background: var(--_badge-outline-surface);
281
267
  clip-path: polygon(
@@ -288,18 +274,26 @@ const hostState = computed(() =>
288
274
  pointer-events: none;
289
275
  }
290
276
 
291
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="shape=tag"]:dir(rtl)::before {
277
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"]:dir(rtl) {
278
+ padding-inline-end: var(--ui-badge-padding-inline, var(--ui-space-2));
279
+ padding-inline-start: calc(
280
+ var(--ui-badge-padding-inline, var(--ui-space-2)) + var(--_badge-point)
281
+ );
282
+ clip-path: polygon(var(--_badge-point) 0, 100% 0, 100% 100%, var(--_badge-point) 100%, 0 50%);
283
+ }
284
+
285
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"]:dir(rtl)::before {
292
286
  clip-path: polygon(var(--_badge-point) 0, 100% 0, 100% 100%, var(--_badge-point) 100%, 0 50%);
293
287
  }
294
288
 
295
289
  @media (forced-colors: active) {
296
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="shape=tag"] {
290
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"] {
297
291
  forced-color-adjust: none;
298
292
  background: CanvasText;
299
293
  color: CanvasText;
300
294
  }
301
295
 
302
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="shape=tag"]::before {
296
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"]::before {
303
297
  background: Canvas;
304
298
  }
305
299
  }
package/vue/UiButton.vue CHANGED
@@ -241,6 +241,11 @@ function attribute(value: unknown): any {
241
241
  margin: 0;
242
242
  }
243
243
 
244
+ /* An anchor keeps the boxed button look even when an app styles ordinary links. */
245
+ [data-component~="ui-button"]:is(a) {
246
+ text-decoration: none;
247
+ }
248
+
244
249
  .spinner-wrap {
245
250
  display: inline-flex;
246
251
  flex: none;
@@ -188,7 +188,7 @@ input:indeterminate {
188
188
  box-shadow: var(--ui-control-inset);
189
189
  }
190
190
 
191
- /* Centre the tick's box, then lift it: a rotated L's ink sits ~0.11rem below its box centre. */
191
+ /* Centre the tick's box and lift it slightly for the rotated check shape. */
192
192
  input:checked::after {
193
193
  inset: 0;
194
194
  margin: auto;
@@ -200,7 +200,7 @@ input:checked::after {
200
200
  border-color: var(--ui-text-on-accent);
201
201
  border-width: 0 0.125rem 0.125rem 0;
202
202
  opacity: 1;
203
- transform: translate(0.07rem, -0.09rem) rotate(45deg);
203
+ transform: translateY(-0.09rem) rotate(45deg);
204
204
  animation: ui-checkbox-check 180ms var(--ui-motion-ease-out, cubic-bezier(0.16, 1, 0.3, 1)) both;
205
205
  }
206
206
 
@@ -216,15 +216,15 @@ input:indeterminate::after {
216
216
  @keyframes ui-checkbox-check {
217
217
  from {
218
218
  opacity: 0;
219
- transform: translate(0.07rem, -0.09rem) rotate(45deg) scale(0.55);
219
+ transform: translateY(-0.09rem) rotate(45deg) scale(0.55);
220
220
  }
221
221
  65% {
222
222
  opacity: 1;
223
- transform: translate(0.07rem, -0.09rem) rotate(45deg) scale(1.12);
223
+ transform: translateY(-0.09rem) rotate(45deg) scale(1.12);
224
224
  }
225
225
  to {
226
226
  opacity: 1;
227
- transform: translate(0.07rem, -0.09rem) rotate(45deg) scale(1);
227
+ transform: translateY(-0.09rem) rotate(45deg) scale(1);
228
228
  }
229
229
  }
230
230
 
@@ -937,6 +937,8 @@ defineExpose({
937
937
  :key="row.id"
938
938
  class="option"
939
939
  role="option"
940
+ data-ui-choice-option=""
941
+ :data-multiple="multiple ? '' : undefined"
940
942
  :id="`${uid}-option-${row.index}`"
941
943
  :aria-selected="row.selected"
942
944
  :aria-disabled="row.disabled"
@@ -969,7 +971,9 @@ defineExpose({
969
971
  v-if="creatable"
970
972
  class="option"
971
973
  role="option"
974
+ data-ui-choice-option=""
972
975
  aria-selected="false"
976
+ :data-multiple="multiple ? '' : undefined"
973
977
  :id="`${uid}-option-${createIndex}`"
974
978
  :data-index="createIndex"
975
979
  :class="{ active: createIndex === active }"
@@ -1260,21 +1264,6 @@ input:disabled, button:disabled {
1260
1264
  [hidden] {
1261
1265
  display: none;
1262
1266
  }
1263
- .option {
1264
- padding: var(--ui-space-2) var(--ui-space-3);
1265
- border-radius: var(--ui-radius-sm);
1266
- cursor: pointer;
1267
- overflow-wrap: anywhere;
1268
- }
1269
- .option:hover, .option.active {
1270
- background: var(--ui-surface-muted);
1271
- outline: 1px solid var(--ui-focus-ring);
1272
- outline-offset: -1px;
1273
- }
1274
- .option[aria-disabled="true"] {
1275
- color: var(--ui-disabled-text);
1276
- cursor: not-allowed;
1277
- }
1278
1267
  .option[aria-disabled="true"] .primary,
1279
1268
  .option[aria-disabled="true"] .secondary {
1280
1269
  color: var(--ui-disabled-text);
@@ -1342,40 +1331,6 @@ input:disabled, button:disabled {
1342
1331
  }
1343
1332
  }
1344
1333
 
1345
- /* Multiple mode: every option carries a checkbox, so a list that takes several answers says so
1346
- before anything is picked. aria-selected is the state; this draws it.
1347
- Longhands, not shorthands: lowering drops a shorthand that omits a component. */
1348
- [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"] .option {
1349
- display: flex;
1350
- gap: var(--ui-space-2);
1351
- align-items: center;
1352
- }
1353
-
1354
- [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"] .option::before {
1355
- content: "";
1356
- flex: none;
1357
- inline-size: 1.125rem;
1358
- block-size: 1.125rem;
1359
- border-style: solid;
1360
- border-width: 1px;
1361
- border-color: var(--ui-control-border-hover, var(--ui-border-strong));
1362
- border-radius: var(--ui-radius-sm);
1363
- background-color: var(--ui-control-surface, var(--ui-surface-default));
1364
- background-repeat: no-repeat;
1365
- background-position: center;
1366
- background-size: 0.7rem;
1367
- }
1368
-
1369
- [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"]
1370
- .option[aria-selected="true"]::before {
1371
- border-color: var(--ui-accent-solid);
1372
- background-color: var(--ui-accent-solid);
1373
- background-image: var(
1374
- --_ui-combobox-option-check,
1375
- url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12 5 5 9-9'/%3E%3C/svg%3E")
1376
- );
1377
- }
1378
-
1379
1334
  /* Multiple mode keeps one control height. Chips and the input scroll within the entry,
1380
1335
  while the clear and disclosure controls remain fixed at the end of the field. */
1381
1336
  [data-component~="ui-combobox"][data-ui-combobox-state~="multiple"] .field {
@@ -204,7 +204,7 @@ function attribute(value: unknown): any {
204
204
  @media (hover: hover) and (pointer: fine) {
205
205
  [data-component~="ui-icon-button"][data-ui-icon-button-state~="anticipatory"]:not([data-component~="ui-icon-button"][data-ui-icon-button-state~="variant=solid"], [data-component~="ui-icon-button"][data-ui-icon-button-state~="loading"], [data-component~="ui-icon-button"][data-ui-icon-button-state~="pending"], :disabled, [data-ui-proximity="near"], :hover, :focus-visible) {
206
206
  border-color: transparent;
207
- background: transparent;
207
+ background-color: transparent;
208
208
  box-shadow: none;
209
209
  }
210
210
 
@@ -232,7 +232,7 @@ function attribute(value: unknown): any {
232
232
  margin: 0;
233
233
  border: 1px solid transparent;
234
234
  border-radius: var(--ui-icon-button-radius, var(--ui-action-radius, var(--ui-radius-md)));
235
- background: var(--ui-icon-button-surface, transparent);
235
+ background-color: var(--ui-icon-button-surface, transparent);
236
236
  color: inherit;
237
237
  display: inline-flex;
238
238
  align-items: center;
@@ -304,14 +304,14 @@ html[data-ui-input-modality="touch"] [data-component~="ui-icon-button"]::after {
304
304
 
305
305
  [data-component~="ui-icon-button"][data-ui-icon-button-state~="variant=outline"] {
306
306
  border-color: var(--ui-icon-button-border, var(--ui-control-border, var(--ui-border-strong)));
307
- background: var(--ui-icon-button-outline-surface, var(--ui-surface-default));
307
+ background-color: var(--ui-icon-button-outline-surface, var(--ui-surface-default));
308
308
  background-image: var(--ui-raised-highlight);
309
309
  color: var(--ui-icon-button-outline-text, var(--ui-text-primary));
310
310
  box-shadow: var(--ui-raised);
311
311
  }
312
312
 
313
313
  [data-component~="ui-icon-button"][data-ui-icon-button-state~="variant=solid"] {
314
- background: var(--ui-icon-button-solid-surface, var(--ui-accent-solid));
314
+ background-color: var(--ui-icon-button-solid-surface, var(--ui-accent-solid));
315
315
  background-image: var(--ui-raised-highlight);
316
316
  color: var(--ui-icon-button-solid-text, var(--ui-text-on-accent));
317
317
  box-shadow: var(
@@ -322,7 +322,10 @@ html[data-ui-input-modality="touch"] [data-component~="ui-icon-button"]::after {
322
322
 
323
323
  [data-component~="ui-icon-button"]:hover:enabled:not([aria-disabled="true"]) {
324
324
  /* Derived from the text colour so the ghost hover shows on any background. */
325
- background: var(--ui-icon-button-hover-surface, color-mix(in srgb, currentColor 8%, transparent));
325
+ background-color: var(
326
+ --ui-icon-button-hover-surface,
327
+ color-mix(in srgb, currentColor 8%, transparent)
328
+ );
326
329
  color: var(--ui-icon-button-hover-text, var(--ui-text-primary));
327
330
  }
328
331
 
@@ -334,7 +337,7 @@ html[data-ui-input-modality="touch"] [data-component~="ui-icon-button"]::after {
334
337
  }
335
338
 
336
339
  [data-component~="ui-icon-button"][data-ui-icon-button-state~="variant=solid"]:hover:enabled:not([aria-disabled="true"]) {
337
- background: var(--ui-icon-button-solid-hover-surface, var(--ui-accent-hover));
340
+ background-color: var(--ui-icon-button-solid-hover-surface, var(--ui-accent-hover));
338
341
  }
339
342
 
340
343
  [data-component~="ui-icon-button"]:active:enabled:not([aria-disabled="true"]) {
@@ -370,7 +373,7 @@ html[data-ui-input-modality="touch"] [data-component~="ui-icon-button"]::after {
370
373
  /* Disabled is a flat neutral control, independent of the active variant's tone. */
371
374
  [data-component~="ui-icon-button"]:disabled {
372
375
  border-color: var(--ui-disabled-border);
373
- background: var(--ui-disabled-surface);
376
+ background-color: var(--ui-disabled-surface);
374
377
  color: var(--ui-disabled-text);
375
378
  filter: var(--ui-icon-button-disabled-filter, var(--ui-disabled-filter));
376
379
  opacity: var(--ui-icon-button-disabled-opacity, 1);
package/vue/UiInput.vue CHANGED
@@ -197,7 +197,7 @@ useComponentHost(controllerModule.default, {
197
197
  opacity: 1;
198
198
  }
199
199
 
200
- [data-component~="ui-input"]:hover:not(:focus):not(:disabled) {
200
+ [data-component~="ui-input"]:hover:not(:focus):not(:disabled):not(:read-only):not([aria-invalid="true"]):not(:is(:user-invalid, [data-user-invalid])) {
201
201
  border-color: var(
202
202
  --ui-input-border-hover,
203
203
  var(--_ui-default-input-border-hover, var(--ui-control-border-hover, var(--ui-text-secondary)))
@@ -118,7 +118,7 @@ useComponentHost(controllerModule.default, {
118
118
  }
119
119
  }
120
120
 
121
- [data-component~="ui-input-group"]:hover:not(:has(input:focus, input:disabled)) {
121
+ [data-component~="ui-input-group"]:hover:not(:has(input:focus, input:disabled, input:read-only, [aria-invalid="true"], :is(:user-invalid, [data-user-invalid]))) {
122
122
  border-color: var(--ui-control-border-hover, var(--ui-text-secondary));
123
123
  }
124
124
 
@@ -176,7 +176,7 @@ useComponentHost(controllerModule.default, {
176
176
  flex: none;
177
177
  align-items: center;
178
178
  padding-inline: var(--ui-space-2);
179
- color: var(--ui-text-secondary);
179
+ color: var(--ui-text-muted);
180
180
  font-family: var(--ui-font-family-mono);
181
181
  font-size: var(--ui-font-size-sm);
182
182
  white-space: nowrap;