@threadlabs/looma 0.21.5 → 0.21.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@threadlabs/looma",
3
- "version": "0.21.5",
3
+ "version": "0.21.6",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -58,6 +58,15 @@
58
58
  block-size: 1.75rem;
59
59
  }
60
60
 
61
+ :scope[data-ui-avatar-group-state~="size=xs"] .overflow {
62
+ margin-inline-start: -0.1875rem;
63
+ inline-size: 1.25rem;
64
+ block-size: 1.25rem;
65
+ /* Matches the xs Avatar's initials. */
66
+ font-size: calc(var(--ui-font-size) * 0.5625);
67
+ line-height: 1;
68
+ }
69
+
61
70
  .overflow[hidden] {
62
71
  display: none;
63
72
  }
@@ -152,7 +152,7 @@
152
152
 
153
153
  :scope[data-ui-button-state~="size=xs"] {
154
154
  min-block-size: 1.5rem;
155
- padding: var(--ui-space-0-5) calc(var(--ui-space-1) * 1.5);
155
+ padding: calc(var(--ui-space-0-5) * 1.5) calc(var(--ui-space-1) * 1.25);
156
156
  font-size: var(--ui-font-size-xs);
157
157
  }
158
158
 
@@ -307,7 +307,7 @@
307
307
 
308
308
  /* The size states outrank :host, so sm is named too, or a small button would stay small. */
309
309
  @media (pointer: coarse) {
310
- :scope, :scope[data-ui-button-state~="size=sm"], :scope[data-ui-button-state~="size=xs"] {
310
+ :scope, :scope[data-ui-button-state~="size=sm"] {
311
311
  min-block-size: var(--ui-control-min-block-size);
312
312
  }
313
313
  }
@@ -315,12 +315,15 @@
315
315
  /* Under touch, a link-style button's press lands within the control minimum: an invisible hit
316
316
  area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
317
317
  a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
318
- area. Only the link needs it: a hit area on every button would reach over its neighbours. */
319
- html[data-ui-input-modality="touch"] :scope[data-ui-button-state~="variant=link"] {
318
+ area. xs sits inside a line of text the same way, so it keeps its size and gains the area too.
319
+ Only these need it: a hit area on every button would reach over its neighbours. */
320
+ html[data-ui-input-modality="touch"] :scope[data-ui-button-state~="variant=link"],
321
+ html[data-ui-input-modality="touch"] :scope[data-ui-button-state~="size=xs"] {
320
322
  position: relative;
321
323
  }
322
324
 
323
- html[data-ui-input-modality="touch"] :scope[data-ui-button-state~="variant=link"]::after {
325
+ html[data-ui-input-modality="touch"] :scope[data-ui-button-state~="variant=link"]::after,
326
+ html[data-ui-input-modality="touch"] :scope[data-ui-button-state~="size=xs"]::after {
324
327
  position: absolute;
325
328
  inset: 50% auto auto 50%;
326
329
  inline-size: max(100%, var(--ui-control-min-block-size));
@@ -412,7 +415,8 @@
412
415
  /* Under touch, a link-style button's press lands within the control minimum: an invisible hit
413
416
  area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
414
417
  a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
415
- area. Only the link needs it: a hit area on every button would reach over its neighbours. */
418
+ area. xs sits inside a line of text the same way, so it keeps its size and gains the area too.
419
+ Only these need it: a hit area on every button would reach over its neighbours. */
416
420
 
417
421
  /* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
418
422
  WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
@@ -9,7 +9,7 @@ export interface UiAvatarGroupProps {
9
9
  label?: string | null;
10
10
  max?: number | null;
11
11
  overflowLabel?: string | null;
12
- size?: "sm" | "md" | null;
12
+ size?: "xs" | "sm" | "md" | null;
13
13
  total?: number | null;
14
14
  attributes?: Readonly<Record<string, string | number | boolean | null | undefined>>;
15
15
  children?: readonly (string | Node)[];
@@ -3,7 +3,7 @@ import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
3
3
  import * as controller from "../components/ui-avatar-group/ui-avatar-group.js";
4
4
  import "../styles/ui-avatar-group.css";
5
5
 
6
- const definition = {...{"contract":{"tag":"ui-avatar-group","props":{"label":{"type":"string","required":false,"target":{"attribute":"aria-label"},"default":"People"},"max":{"type":"number","required":false,"target":{"attribute":"max"},"default":5},"overflowLabel":{"type":"string","required":false,"target":{"attribute":"overflowlabel"},"default":"{count} more"},"size":{"type":{"kind":"terminal","name":"keyword"},"values":["sm","md"],"required":false,"target":{"attribute":"size"},"default":"md"},"total":{"type":{"kind":"terminal","name":"integer"},"required":false,"target":{"attribute":"total"}}}},"template":{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"role","value":"group"},{"kind":"attribute","name":"aria-label","expression":"label","expressionPlan":{"source":"label","ast":{"kind":"id","name":"label"},"dependencies":["label"]}}],"children":[{"kind":"slot"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"overflow"},{"kind":"literal","name":"role","value":"img"},{"kind":"attribute","name":"hidden","expression":"not overflowCount","expressionPlan":{"source":"not overflowCount","ast":{"kind":"unary","op":"not","operand":{"kind":"id","name":"overflowCount"}},"dependencies":["overflowCount"]}},{"kind":"attribute","name":"aria-label","expression":"overflowAnnouncement","expressionPlan":{"source":"overflowAnnouncement","ast":{"kind":"id","name":"overflowAnnouncement"},"dependencies":["overflowAnnouncement"]}}],"children":[{"kind":"element","name":"template","attributes":[{"kind":"directive","name":"value","expression":"format('+%s', overflowCount)","expressionPlan":{"source":"format('+%s', overflowCount)","ast":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"+%s"},{"kind":"id","name":"overflowCount"}]},"dependencies":["overflowCount"]}}],"children":[]}],"ref":"overflow"}]},"declarations":[{"kind":"state","name":"overflowCount","expression":{"source":"0","ast":{"kind":"literal","value":0},"dependencies":[]}},{"kind":"state","name":"overflowAnnouncement","expression":{"source":"''","ast":{"kind":"literal","value":""},"dependencies":[]}}],"root":{"kind":"native","element":"div","choices":["div"]}},source:{file:import.meta.url},css:""};
6
+ const definition = {...{"contract":{"tag":"ui-avatar-group","props":{"label":{"type":"string","required":false,"target":{"attribute":"aria-label"},"default":"People"},"max":{"type":"number","required":false,"target":{"attribute":"max"},"default":5},"overflowLabel":{"type":"string","required":false,"target":{"attribute":"overflowlabel"},"default":"{count} more"},"size":{"type":{"kind":"terminal","name":"keyword"},"values":["xs","sm","md"],"required":false,"target":{"attribute":"size"},"default":"md"},"total":{"type":{"kind":"terminal","name":"integer"},"required":false,"target":{"attribute":"total"}}}},"template":{"kind":"element","name":"div","attributes":[{"kind":"literal","name":"role","value":"group"},{"kind":"attribute","name":"aria-label","expression":"label","expressionPlan":{"source":"label","ast":{"kind":"id","name":"label"},"dependencies":["label"]}}],"children":[{"kind":"slot"},{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"overflow"},{"kind":"literal","name":"role","value":"img"},{"kind":"attribute","name":"hidden","expression":"not overflowCount","expressionPlan":{"source":"not overflowCount","ast":{"kind":"unary","op":"not","operand":{"kind":"id","name":"overflowCount"}},"dependencies":["overflowCount"]}},{"kind":"attribute","name":"aria-label","expression":"overflowAnnouncement","expressionPlan":{"source":"overflowAnnouncement","ast":{"kind":"id","name":"overflowAnnouncement"},"dependencies":["overflowAnnouncement"]}}],"children":[{"kind":"element","name":"template","attributes":[{"kind":"directive","name":"value","expression":"format('+%s', overflowCount)","expressionPlan":{"source":"format('+%s', overflowCount)","ast":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"+%s"},{"kind":"id","name":"overflowCount"}]},"dependencies":["overflowCount"]}}],"children":[]}],"ref":"overflow"}]},"declarations":[{"kind":"state","name":"overflowCount","expression":{"source":"0","ast":{"kind":"literal","value":0},"dependencies":[]}},{"kind":"state","name":"overflowAnnouncement","expression":{"source":"''","ast":{"kind":"literal","value":""},"dependencies":[]}}],"root":{"kind":"native","element":"div","choices":["div"]}},source:{file:import.meta.url},css:""};
7
7
 
8
8
  export function createUiAvatarGroup(options = {}) {
9
9
  const { attributes = {}, children = [], slots = {}, ...componentProps } = options;
@@ -17,7 +17,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
17
17
  default: string;
18
18
  };
19
19
  size: {
20
- type: PropType<"sm" | "md" | null>;
20
+ type: PropType<"xs" | "sm" | "md" | null>;
21
21
  default: string;
22
22
  };
23
23
  total: {
@@ -38,7 +38,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
38
38
  default: string;
39
39
  };
40
40
  size: {
41
- type: PropType<"sm" | "md" | null>;
41
+ type: PropType<"xs" | "sm" | "md" | null>;
42
42
  default: string;
43
43
  };
44
44
  total: {
@@ -47,7 +47,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
47
47
  };
48
48
  }>> & Readonly<{}>, {
49
49
  label: string | null;
50
- size: "sm" | "md" | null;
50
+ size: "xs" | "sm" | "md" | null;
51
51
  max: number | null;
52
52
  overflowLabel: string | null;
53
53
  total: number | null;
@@ -11,7 +11,7 @@ const props = defineProps({
11
11
  label: { type: null as unknown as PropType<string | null>, default: 'People' },
12
12
  max: { type: null as unknown as PropType<number | null>, default: 5 },
13
13
  overflowLabel: { type: null as unknown as PropType<string | null>, default: '{count} more' },
14
- size: { type: null as unknown as PropType<'sm' | 'md' | null>, default: 'md' },
14
+ size: { type: null as unknown as PropType<'xs' | 'sm' | 'md' | null>, default: 'md' },
15
15
  total: { type: null as unknown as PropType<number | null>, default: null },
16
16
  })
17
17
  const checkedProps = computed(() => ({
@@ -39,14 +39,14 @@ const checkedProps = computed(() => ({
39
39
  undefined,
40
40
  undefined,
41
41
  ) as string,
42
- size: checkedProp<'sm' | 'md'>(
42
+ size: checkedProp<'xs' | 'sm' | 'md'>(
43
43
  props['size'],
44
44
  { kind: 'terminal', name: 'keyword' },
45
45
  false,
46
46
  'size',
47
47
  undefined,
48
- ['sm', 'md'],
49
- ) as 'sm' | 'md',
48
+ ['xs', 'sm', 'md'],
49
+ ) as 'xs' | 'sm' | 'md',
50
50
  total: checkedProp<number>(
51
51
  props['total'],
52
52
  { kind: 'terminal', name: 'integer' },
@@ -230,6 +230,15 @@ useComponentHost(() => import('../components/ui-avatar-group/ui-avatar-group.js'
230
230
  block-size: 1.75rem;
231
231
  }
232
232
 
233
+ [data-component~="ui-avatar-group"][data-ui-avatar-group-state~="size=xs"] .overflow {
234
+ margin-inline-start: -0.1875rem;
235
+ inline-size: 1.25rem;
236
+ block-size: 1.25rem;
237
+ /* Matches the xs Avatar's initials. */
238
+ font-size: calc(var(--ui-font-size) * 0.5625);
239
+ line-height: 1;
240
+ }
241
+
233
242
  .overflow[hidden] {
234
243
  display: none;
235
244
  }
package/vue/UiButton.vue CHANGED
@@ -499,7 +499,7 @@ function attribute(value: unknown): any {
499
499
 
500
500
  [data-component~="ui-button"][data-ui-button-state~="size=xs"] {
501
501
  min-block-size: 1.5rem;
502
- padding: var(--ui-space-0-5) calc(var(--ui-space-1) * 1.5);
502
+ padding: calc(var(--ui-space-0-5) * 1.5) calc(var(--ui-space-1) * 1.25);
503
503
  font-size: var(--ui-font-size-xs);
504
504
  }
505
505
 
@@ -661,9 +661,7 @@ function attribute(value: unknown): any {
661
661
 
662
662
  /* The size states outrank :host, so sm is named too, or a small button would stay small. */
663
663
  @media (pointer: coarse) {
664
- [data-component~="ui-button"],
665
- [data-component~="ui-button"][data-ui-button-state~="size=sm"],
666
- [data-component~="ui-button"][data-ui-button-state~="size=xs"] {
664
+ [data-component~="ui-button"], [data-component~="ui-button"][data-ui-button-state~="size=sm"] {
667
665
  min-block-size: var(--ui-control-min-block-size);
668
666
  }
669
667
  }
@@ -671,14 +669,19 @@ function attribute(value: unknown): any {
671
669
  /* Under touch, a link-style button's press lands within the control minimum: an invisible hit
672
670
  area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
673
671
  a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
674
- area. Only the link needs it: a hit area on every button would reach over its neighbours. */
672
+ area. xs sits inside a line of text the same way, so it keeps its size and gains the area too.
673
+ Only these need it: a hit area on every button would reach over its neighbours. */
674
+ html[data-ui-input-modality="touch"]
675
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"],
675
676
  html[data-ui-input-modality="touch"]
676
- [data-component~="ui-button"][data-ui-button-state~="variant=link"] {
677
+ [data-component~="ui-button"][data-ui-button-state~="size=xs"] {
677
678
  position: relative;
678
679
  }
679
680
 
680
681
  html[data-ui-input-modality="touch"]
681
- [data-component~="ui-button"][data-ui-button-state~="variant=link"]::after {
682
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"]::after,
683
+ html[data-ui-input-modality="touch"]
684
+ [data-component~="ui-button"][data-ui-button-state~="size=xs"]::after {
682
685
  position: absolute;
683
686
  inset: 50% auto auto 50%;
684
687
  inline-size: max(100%, var(--ui-control-min-block-size));
@@ -58,7 +58,11 @@ var UiAvatarGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
58
58
  size: checkedProp(props["size"], {
59
59
  kind: "terminal",
60
60
  name: "keyword"
61
- }, false, "size", void 0, ["sm", "md"]),
61
+ }, false, "size", void 0, [
62
+ "xs",
63
+ "sm",
64
+ "md"
65
+ ]),
62
66
  total: checkedProp(props["total"], {
63
67
  kind: "terminal",
64
68
  name: "integer"
@@ -133,6 +137,6 @@ var UiAvatarGroup_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @
133
137
  }, toDisplayString(`+${overflowCount.value}`), 9, _hoisted_2)], 16, _hoisted_1);
134
138
  };
135
139
  }
136
- }), [["__scopeId", "data-v-04c833c9"]]);
140
+ }), [["__scopeId", "data-v-94b7399c"]]);
137
141
  //#endregion
138
142
  export { UiAvatarGroup_default as t };
@@ -340,6 +340,6 @@ var UiButton_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PUR
340
340
  }), [checkedProps.value.loading || checkedProps.value.pending ? (openBlock(), createElementBlock("span", _hoisted_4, [createVNode(UiSpinner_default, { size: "sm" })])) : createCommentVNode("v-if", true), renderSlot(_ctx.$slots, "default", {}, void 0, true)], 16, _hoisted_3));
341
341
  };
342
342
  }
343
- }), [["__scopeId", "data-v-7a112b47"]]);
343
+ }), [["__scopeId", "data-v-ffb3d8ad"]]);
344
344
  //#endregion
345
345
  export { UiButton_default as t };
@@ -228,7 +228,7 @@ svg[data-v-3b79b619] {
228
228
  syntax: "*";
229
229
  inherits: false;
230
230
  }
231
- [data-component~="ui-avatar-group"][data-v-04c833c9] {
231
+ [data-component~="ui-avatar-group"][data-v-94b7399c] {
232
232
  /* Hooks do not inherit, so the parts below read them from the root. */
233
233
  --_ui-avatar-group-overlap-shadow: var(--ui-avatar-group-overlap-shadow);
234
234
  --_ui-avatar-group-edge-ring: var(--ui-avatar-group-edge-ring);
@@ -245,17 +245,17 @@ svg[data-v-3b79b619] {
245
245
 
246
246
  /* Overlap and the separating shadow are applied by the controller: slotted avatars are nested
247
247
  * component roots outside this style scope. */
248
- [data-v-04c833c9-s] {
248
+ [data-v-94b7399c-s] {
249
249
  margin-inline-start: -0.5rem;
250
250
  flex-shrink: 0;
251
251
  }
252
- [data-v-04c833c9-s]:first-child {
252
+ [data-v-94b7399c-s]:first-child {
253
253
  margin-inline-start: 0;
254
254
  }
255
- [data-component~="ui-avatar-group"][data-v-04c833c9]:dir(rtl) {
255
+ [data-component~="ui-avatar-group"][data-v-94b7399c]:dir(rtl) {
256
256
  --_overlap-shadow: 1px 0 0 0 rgb(0 0 0 / 0.22);
257
257
  }
258
- .overflow[data-v-04c833c9] {
258
+ .overflow[data-v-94b7399c] {
259
259
  margin-inline-start: -0.5rem;
260
260
  display: inline-flex;
261
261
  align-items: center;
@@ -274,17 +274,25 @@ svg[data-v-3b79b619] {
274
274
  font-weight: 600;
275
275
  flex-shrink: 0;
276
276
  }
277
- [data-component~="ui-avatar-group"][data-ui-avatar-group-state~="size=sm"] .overflow[data-v-04c833c9] {
277
+ [data-component~="ui-avatar-group"][data-ui-avatar-group-state~="size=sm"] .overflow[data-v-94b7399c] {
278
278
  margin-inline-start: -0.25rem;
279
279
  inline-size: 1.75rem;
280
280
  block-size: 1.75rem;
281
281
  }
282
- .overflow[hidden][data-v-04c833c9] {
282
+ [data-component~="ui-avatar-group"][data-ui-avatar-group-state~="size=xs"] .overflow[data-v-94b7399c] {
283
+ margin-inline-start: -0.1875rem;
284
+ inline-size: 1.25rem;
285
+ block-size: 1.25rem;
286
+ /* Matches the xs Avatar's initials. */
287
+ font-size: calc(var(--ui-font-size) * 0.5625);
288
+ line-height: 1;
289
+ }
290
+ .overflow[hidden][data-v-94b7399c] {
283
291
  display: none;
284
292
  }
285
293
 
286
294
  /* A component that sets its display still honours the hidden attribute on its root. */
287
- [data-component~="ui-avatar-group"][hidden][data-v-04c833c9] {
295
+ [data-component~="ui-avatar-group"][hidden][data-v-94b7399c] {
288
296
  display: none;
289
297
  }
290
298
 
@@ -739,7 +747,7 @@ svg[data-v-8de2921b],
739
747
  syntax: "*";
740
748
  inherits: false;
741
749
  }
742
- [data-component~="ui-button"][data-v-7a112b47] {
750
+ [data-component~="ui-button"][data-v-ffb3d8ad] {
743
751
  --_hue: var(--ui-accent);
744
752
  --_hue-hover: var(--ui-accent-hover);
745
753
  --_hue-active: var(--ui-accent-active);
@@ -758,15 +766,15 @@ svg[data-v-8de2921b],
758
766
  vertical-align: middle;
759
767
  color: var(--_tone-text);
760
768
  }
761
- [data-component~="ui-button"][data-ui-button-state~="align=start"][data-v-7a112b47] {
769
+ [data-component~="ui-button"][data-ui-button-state~="align=start"][data-v-ffb3d8ad] {
762
770
  justify-content: flex-start;
763
771
  text-align: start;
764
772
  }
765
- [data-component~="ui-button"][data-ui-button-state~="stretch"][data-v-7a112b47] {
773
+ [data-component~="ui-button"][data-ui-button-state~="stretch"][data-v-ffb3d8ad] {
766
774
  display: flex;
767
775
  inline-size: 100%;
768
776
  }
769
- [data-component~="ui-button"][data-v-7a112b47] {
777
+ [data-component~="ui-button"][data-v-ffb3d8ad] {
770
778
  appearance: none;
771
779
  min-block-size: var(--ui-button-min-block-size, var(--ui-control-size-md));
772
780
  border-style: solid;
@@ -794,35 +802,35 @@ svg[data-v-8de2921b],
794
802
  }
795
803
 
796
804
  /* An anchor keeps the boxed button look even when an app styles ordinary links. */
797
- [data-component~="ui-button"][data-v-7a112b47]:is(a) {
805
+ [data-component~="ui-button"][data-v-ffb3d8ad]:is(a) {
798
806
  text-decoration: none;
799
807
  }
800
- .spinner-wrap[data-v-7a112b47] {
808
+ .spinner-wrap[data-v-ffb3d8ad] {
801
809
  display: inline-flex;
802
810
  flex: none;
803
811
  margin-inline-end: var(--ui-space-2);
804
812
  vertical-align: middle;
805
813
  }
806
814
  @supports (text-box-trim: trim-both) {
807
- [data-component~="ui-button"][data-v-7a112b47] {
815
+ [data-component~="ui-button"][data-v-ffb3d8ad] {
808
816
  text-box-trim: trim-both;
809
817
  text-box-edge: cap alphabetic;
810
818
  }
811
819
  }
812
- [data-component~="ui-button"][data-ui-button-state~="size=xs"][data-v-7a112b47] {
820
+ [data-component~="ui-button"][data-ui-button-state~="size=xs"][data-v-ffb3d8ad] {
813
821
  min-block-size: 1.5rem;
814
- padding: var(--ui-space-0-5) calc(var(--ui-space-1) * 1.5);
822
+ padding: calc(var(--ui-space-0-5) * 1.5) calc(var(--ui-space-1) * 1.25);
815
823
  font-size: var(--ui-font-size-xs);
816
824
  }
817
- [data-component~="ui-button"][data-ui-button-state~="shape=pill"][data-v-7a112b47] {
825
+ [data-component~="ui-button"][data-ui-button-state~="shape=pill"][data-v-ffb3d8ad] {
818
826
  border-radius: var(--ui-button-radius, var(--ui-radius-round));
819
827
  }
820
- [data-component~="ui-button"][data-ui-button-state~="size=sm"][data-v-7a112b47] {
828
+ [data-component~="ui-button"][data-ui-button-state~="size=sm"][data-v-ffb3d8ad] {
821
829
  min-block-size: var(--ui-control-size-sm);
822
830
  padding: var(--ui-space-1) var(--ui-space-2);
823
831
  font-size: var(--ui-font-size-sm);
824
832
  }
825
- [data-component~="ui-button"][data-ui-button-state~="size=lg"][data-v-7a112b47] {
833
+ [data-component~="ui-button"][data-ui-button-state~="size=lg"][data-v-ffb3d8ad] {
826
834
  min-block-size: var(--ui-control-size-lg);
827
835
  padding: var(--ui-space-3) var(--ui-space-4);
828
836
  font-size: var(--ui-font-size-md);
@@ -830,29 +838,29 @@ svg[data-v-8de2921b],
830
838
 
831
839
  /* A link is never :enabled, so interactive states test "not unavailable" instead: for a button that
832
840
  is exactly :enabled, at the same specificity, and it also matches a link that can be followed. */
833
- [data-component~="ui-button"][data-v-7a112b47]:hover:not(:disabled, [aria-disabled="true"]) {
841
+ [data-component~="ui-button"][data-v-ffb3d8ad]:hover:not(:disabled, [aria-disabled="true"]) {
834
842
  border-color: var(--ui-button-hover-border, var(--_tone-hover));
835
843
  background-color: var(--ui-button-hover-surface, oklch(from var(--_tone) l c h / 12%));
836
844
  }
837
845
 
838
846
  /* Pressed reads as pressed in: the highlight goes, and the shadow moves inside. */
839
- [data-component~="ui-button"][data-v-7a112b47]:active:not(:disabled, [aria-disabled="true"]) {
847
+ [data-component~="ui-button"][data-v-ffb3d8ad]:active:not(:disabled, [aria-disabled="true"]) {
840
848
  background-color: var(--ui-button-active-surface, oklch(from var(--_tone) l c h / 18%));
841
849
  background-image: none;
842
850
  box-shadow: var(--ui-pressed);
843
851
  }
844
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-7a112b47],
845
- [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-7a112b47] {
852
+ [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-ffb3d8ad],
853
+ [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-ffb3d8ad] {
846
854
  border-color: var(--_tone);
847
855
  background-color: var(--_tone);
848
856
  color: var(--_on-tone);
849
857
  box-shadow: var(--ui-button-solid-shadow, var(--ui-raised));
850
858
  }
851
- :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-7a112b47], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-7a112b47]):hover:not(:disabled, [aria-disabled="true"]) {
859
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-ffb3d8ad], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-ffb3d8ad]):hover:not(:disabled, [aria-disabled="true"]) {
852
860
  background-color: var(--_tone-hover);
853
861
  border-color: var(--_tone-hover);
854
862
  }
855
- :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-7a112b47], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-7a112b47]):active:not(:disabled, [aria-disabled="true"]) {
863
+ :is([data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-ffb3d8ad], [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-ffb3d8ad]):active:not(:disabled, [aria-disabled="true"]) {
856
864
  background-color: var(--_tone-active);
857
865
  border-color: var(--_tone-active);
858
866
  background-image: none;
@@ -861,43 +869,43 @@ svg[data-v-8de2921b],
861
869
 
862
870
  /* Tone is the colour, variant is the volume. Every pairing works: a danger outline is a
863
871
  destructive secondary action, not a special case. */
864
- [data-component~="ui-button"][data-ui-button-state~="tone=neutral"][data-v-7a112b47] {
872
+ [data-component~="ui-button"][data-ui-button-state~="tone=neutral"][data-v-ffb3d8ad] {
865
873
  --_hue: var(--ui-text);
866
874
  --_hue-hover: var(--ui-text);
867
875
  --_hue-active: var(--ui-text);
868
876
  --_hue-text: var(--ui-text);
869
877
  --_on-hue: var(--ui-surface);
870
878
  }
871
- [data-component~="ui-button"][data-ui-button-state~="tone=danger"][data-v-7a112b47],
872
- [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-7a112b47] {
879
+ [data-component~="ui-button"][data-ui-button-state~="tone=danger"][data-v-ffb3d8ad],
880
+ [data-component~="ui-button"][data-ui-button-state~="variant=danger"][data-v-ffb3d8ad] {
873
881
  --_hue: var(--ui-danger);
874
882
  --_hue-hover: var(--ui-danger-hover);
875
883
  --_hue-active: var(--ui-danger-hover);
876
884
  --_hue-text: var(--ui-danger-hover);
877
885
  --_on-hue: var(--ui-on-danger);
878
886
  }
879
- [data-component~="ui-button"][data-ui-button-state~="tone=success"][data-v-7a112b47] {
887
+ [data-component~="ui-button"][data-ui-button-state~="tone=success"][data-v-ffb3d8ad] {
880
888
  --_hue: var(--ui-success);
881
889
  --_hue-hover: color-mix(in srgb, var(--ui-success) 85%, #000);
882
890
  --_hue-active: color-mix(in srgb, var(--ui-success) 75%, #000);
883
891
  --_hue-text: color-mix(in srgb, var(--ui-success) 85%, #000);
884
892
  --_on-hue: var(--ui-surface);
885
893
  }
886
- [data-component~="ui-button"][data-ui-button-state~="tone=warning"][data-v-7a112b47] {
894
+ [data-component~="ui-button"][data-ui-button-state~="tone=warning"][data-v-ffb3d8ad] {
887
895
  --_hue: var(--ui-warning);
888
896
  --_hue-hover: color-mix(in srgb, var(--ui-warning) 85%, #000);
889
897
  --_hue-active: color-mix(in srgb, var(--ui-warning) 75%, #000);
890
898
  --_hue-text: color-mix(in srgb, var(--ui-warning) 85%, #000);
891
899
  --_on-hue: var(--ui-surface);
892
900
  }
893
- [data-component~="ui-button"][data-ui-button-state~="tone=info"][data-v-7a112b47] {
901
+ [data-component~="ui-button"][data-ui-button-state~="tone=info"][data-v-ffb3d8ad] {
894
902
  --_hue: var(--ui-info);
895
903
  --_hue-hover: color-mix(in srgb, var(--ui-info) 85%, #000);
896
904
  --_hue-active: color-mix(in srgb, var(--ui-info) 75%, #000);
897
905
  --_hue-text: color-mix(in srgb, var(--ui-info) 85%, #000);
898
906
  --_on-hue: var(--ui-surface);
899
907
  }
900
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-7a112b47] {
908
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-ffb3d8ad] {
901
909
  border-color: transparent;
902
910
  background: transparent;
903
911
  box-shadow: none;
@@ -905,14 +913,14 @@ svg[data-v-8de2921b],
905
913
  }
906
914
 
907
915
  /* Tints derive from the text colour, not a surface token, so hover and press show on any background. */
908
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-7a112b47]:hover:not(:disabled, [aria-disabled="true"]) {
916
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-ffb3d8ad]:hover:not(:disabled, [aria-disabled="true"]) {
909
917
  background-color: var(--ui-button-ghost-hover-surface, oklch(from var(--_tone) l c h / 10%));
910
918
  border-color: transparent;
911
919
  }
912
920
 
913
921
  /* Pressed, or holding its menu open, a ghost button sinks slightly: a light surface with a faint
914
922
  edge and a soft inner shadow. Lighter than hover, so an open menu's trigger never reads as heavy. */
915
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-7a112b47]:is(:active, [aria-expanded="true"]):not(:disabled, [aria-disabled="true"]) {
923
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-ffb3d8ad]:is(:active, [aria-expanded="true"]):not(:disabled, [aria-disabled="true"]) {
916
924
  background-color: var(
917
925
  --ui-button-ghost-active-surface,
918
926
  color-mix(in oklch, var(--ui-surface), var(--_tone) 3%)
@@ -924,13 +932,13 @@ svg[data-v-8de2921b],
924
932
  }
925
933
 
926
934
  /* A soft halo that transitions in with the other box-shadow changes, layered on the raised shadow. */
927
- [data-component~="ui-button"][data-v-7a112b47]:focus-visible {
935
+ [data-component~="ui-button"][data-v-ffb3d8ad]:focus-visible {
928
936
  outline: none;
929
937
  box-shadow:
930
938
  var(--ui-button-shadow, var(--ui-raised)),
931
939
  var(--ui-focus-halo);
932
940
  }
933
- [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-7a112b47]:focus-visible {
941
+ [data-component~="ui-button"][data-ui-button-state~="variant=solid"][data-v-ffb3d8ad]:focus-visible {
934
942
  box-shadow:
935
943
  var(--ui-button-solid-shadow, var(--ui-raised)),
936
944
  var(--ui-focus-halo);
@@ -938,7 +946,7 @@ svg[data-v-8de2921b],
938
946
 
939
947
  /* A loading action stays visually active; only an explicitly disabled action takes the
940
948
  neutral disabled palette. Its flat surface and muted ink make the state unambiguous. */
941
- [data-component~="ui-button"][data-v-7a112b47]:is(:disabled, [aria-disabled="true"]:not([aria-busy="true"])) {
949
+ [data-component~="ui-button"][data-v-ffb3d8ad]:is(:disabled, [aria-disabled="true"]:not([aria-busy="true"])) {
942
950
  border-color: var(--ui-disabled-border);
943
951
  background-color: var(--ui-disabled-surface);
944
952
  color: var(--ui-disabled-text);
@@ -948,21 +956,19 @@ svg[data-v-8de2921b],
948
956
  background-image: none;
949
957
  box-shadow: none;
950
958
  }
951
- :is([data-component~="ui-button"][data-ui-button-state~="loading"][data-v-7a112b47], [data-component~="ui-button"][data-ui-button-state~="pending"][data-v-7a112b47]):focus-visible {
959
+ :is([data-component~="ui-button"][data-ui-button-state~="loading"][data-v-ffb3d8ad], [data-component~="ui-button"][data-ui-button-state~="pending"][data-v-ffb3d8ad]):focus-visible {
952
960
  box-shadow: var(--ui-focus-halo);
953
961
  }
954
962
 
955
963
  /* Ghost keeps no border, but gains the same neutral surface as other disabled buttons. */
956
- [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-7a112b47]:is(:disabled, [aria-disabled="true"]:not([aria-busy="true"])) {
964
+ [data-component~="ui-button"][data-ui-button-state~="variant=ghost"][data-v-ffb3d8ad]:is(:disabled, [aria-disabled="true"]:not([aria-busy="true"])) {
957
965
  background-color: var(--ui-disabled-surface);
958
966
  border-color: transparent;
959
967
  }
960
968
 
961
969
  /* The size states outrank :host, so sm is named too, or a small button would stay small. */
962
970
  @media (pointer: coarse) {
963
- [data-component~="ui-button"][data-v-7a112b47],
964
- [data-component~="ui-button"][data-ui-button-state~="size=sm"][data-v-7a112b47],
965
- [data-component~="ui-button"][data-ui-button-state~="size=xs"][data-v-7a112b47] {
971
+ [data-component~="ui-button"][data-v-ffb3d8ad], [data-component~="ui-button"][data-ui-button-state~="size=sm"][data-v-ffb3d8ad] {
966
972
  min-block-size: var(--ui-control-min-block-size);
967
973
  }
968
974
  }
@@ -970,13 +976,18 @@ svg[data-v-8de2921b],
970
976
  /* Under touch, a link-style button's press lands within the control minimum: an invisible hit
971
977
  area, centred on it, like IconButton's. A boxed button grows to the minimum itself (below);
972
978
  a link-style one keeps its text size, so a standalone one ("See all" under a list) needs the
973
- area. Only the link needs it: a hit area on every button would reach over its neighbours. */
979
+ area. xs sits inside a line of text the same way, so it keeps its size and gains the area too.
980
+ Only these need it: a hit area on every button would reach over its neighbours. */
981
+ html[data-ui-input-modality="touch"]
982
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-ffb3d8ad],
974
983
  html[data-ui-input-modality="touch"]
975
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-7a112b47] {
984
+ [data-component~="ui-button"][data-ui-button-state~="size=xs"][data-v-ffb3d8ad] {
976
985
  position: relative;
977
986
  }
978
987
  html[data-ui-input-modality="touch"]
979
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-7a112b47]::after {
988
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-ffb3d8ad]::after,
989
+ html[data-ui-input-modality="touch"]
990
+ [data-component~="ui-button"][data-ui-button-state~="size=xs"][data-v-ffb3d8ad]::after {
980
991
  position: absolute;
981
992
  inset: 50% auto auto 50%;
982
993
  inline-size: max(100%, var(--ui-control-min-block-size));
@@ -987,7 +998,7 @@ html[data-ui-input-modality="touch"]
987
998
 
988
999
  /* Link: an inline text action. No box, padding, or minimum height (inline targets are exempt under
989
1000
  WCAG 2.5.8); the inherited font; an underline that strengthens on hover; a visible focus ring. */
990
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-7a112b47] {
1001
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-ffb3d8ad] {
991
1002
  display: inline;
992
1003
  min-block-size: 0;
993
1004
  padding: 0;
@@ -1005,21 +1016,21 @@ html[data-ui-input-modality="touch"]
1005
1016
  color var(--ui-motion-fast) var(--ui-motion-ease),
1006
1017
  text-decoration-color var(--ui-motion-fast) var(--ui-motion-ease);
1007
1018
  }
1008
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-7a112b47]:hover:not(:disabled, [aria-disabled="true"]) {
1019
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-ffb3d8ad]:hover:not(:disabled, [aria-disabled="true"]) {
1009
1020
  color: var(--ui-button-link-hover-text, var(--ui-text-primary));
1010
1021
  text-decoration-color: currentColor;
1011
1022
  background: none;
1012
1023
  transform: none;
1013
1024
  }
1014
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-7a112b47]:active:not(:disabled, [aria-disabled="true"]) {
1025
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-ffb3d8ad]:active:not(:disabled, [aria-disabled="true"]) {
1015
1026
  background: none;
1016
1027
  transform: none;
1017
1028
  }
1018
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-7a112b47]:is(:disabled, [aria-disabled="true"]:not([aria-busy="true"])) {
1029
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-ffb3d8ad]:is(:disabled, [aria-disabled="true"]:not([aria-busy="true"])) {
1019
1030
  background: none;
1020
1031
  border-color: transparent;
1021
1032
  }
1022
- [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-7a112b47]:focus-visible {
1033
+ [data-component~="ui-button"][data-ui-button-state~="variant=link"][data-v-ffb3d8ad]:focus-visible {
1023
1034
  box-shadow: none;
1024
1035
  /* Fallback keeps the ring if the token is missing: an unresolved var() would void the outline. */
1025
1036
  outline: 2px solid var(--ui-focus-ring);
@@ -1027,7 +1038,7 @@ html[data-ui-input-modality="touch"]
1027
1038
  }
1028
1039
 
1029
1040
  /* A component that sets its display still honours the hidden attribute on its root. */
1030
- [data-component~="ui-button"][hidden][data-v-7a112b47] {
1041
+ [data-component~="ui-button"][hidden][data-v-ffb3d8ad] {
1031
1042
  display: none;
1032
1043
  }
1033
1044