@threadlabs/looma 0.27.0 → 0.27.1

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.27.0",
3
+ "version": "0.27.1",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -279,6 +279,15 @@
279
279
  box-shadow: 0 1px 1px light-dark(rgb(0 0 0 / 0.08), rgb(0 0 0 / 0.12));
280
280
  }
281
281
 
282
+ /* A count can keep its containing control's contrasting ink instead of reversing it.
283
+ The faint fill is painted over that control; no second rim or shadow competes with it. */
284
+ :scope[data-ui-badge-state~="variant=overlay"] {
285
+ --_badge-outline-surface: var(--ui-badge-surface, color-mix(in srgb, currentColor 10%, transparent));
286
+ color: var(--ui-badge-text, inherit);
287
+ border-color: var(--ui-badge-border, transparent);
288
+ box-shadow: none;
289
+ }
290
+
282
291
  /* Neutral reverses a subtle surface/ink pair rather than a strong tone. Keep its original
283
292
  surface ink: moving it toward dark-theme primary text would reduce overlay contrast. */
284
293
  :scope[data-ui-badge-state~="variant=inverse"][data-ui-badge-state~="tone=neutral"] {
@@ -366,6 +375,10 @@
366
375
  text-box-trim: none;
367
376
  }
368
377
 
378
+ :scope[data-ui-badge-state~="shape=circle"][data-ui-badge-state~="size=sm"][data-ui-badge-state~="variant=overlay"] {
379
+ --_badge-circle-size: var(--ui-badge-circle-size, calc(var(--ui-space-4) + var(--ui-space-1)));
380
+ }
381
+
369
382
  :scope[data-ui-badge-state~="shape=circle"][data-ui-badge-state~="size=xs"] {
370
383
  --_badge-circle-size: var(--ui-badge-circle-size, var(--ui-space-4));
371
384
  }
@@ -519,6 +532,9 @@
519
532
  /* A circular count has a tone-tinted surface, a separate lighter rim and readable tone ink.
520
533
  Dark controls retain a shaded surface and theme ink with a quieter rim. */
521
534
 
535
+ /* A count can keep its containing control's contrasting ink instead of reversing it.
536
+ The faint fill is painted over that control; no second rim or shadow competes with it. */
537
+
522
538
  /* Neutral reverses a subtle surface/ink pair rather than a strong tone. Keep its original
523
539
  surface ink: moving it toward dark-theme primary text would reduce overlay contrast. */
524
540
 
@@ -9,7 +9,7 @@ export interface UiBadgeProps {
9
9
  shape?: "pill" | "tag" | "dot" | "square" | "circle" | null;
10
10
  size?: "xs" | "sm" | "md" | "lg" | null;
11
11
  tone?: "neutral" | "accent" | "info" | "success" | "warning" | "danger" | null;
12
- variant?: "solid" | "subtle" | "outline" | "inverse" | null;
12
+ variant?: "solid" | "subtle" | "outline" | "inverse" | "overlay" | null;
13
13
  attributes?: Readonly<Record<string, string | number | boolean | null | undefined>>;
14
14
  children?: readonly (string | Node)[];
15
15
  slots?: Readonly<Record<string, readonly (string | Node)[]>>;
@@ -7,7 +7,7 @@ const T1 = ["",[]];
7
7
  const T2 = ["",[]];
8
8
  let P0, P1, P2;
9
9
  const S = { n: [], t: [], f: import.meta.url, g: "ui-badge" };
10
- const D = { props: { "shape": { "required":false,"values":["pill","tag","dot","square","circle"],"default":"pill", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat),false] }, "size": { "required":false,"values":["xs","sm","md","lg"],"default":"md", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat),false] }, "tone": { "required":false,"values":["neutral","accent","info","success","warning","danger"],"default":"neutral", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat),false] }, "variant": { "required":false,"values":["solid","subtle","outline","inverse"],"default":"subtle", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat),false] } } };
10
+ const D = { props: { "shape": { "required":false,"values":["pill","tag","dot","square","circle"],"default":"pill", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat),false] }, "size": { "required":false,"values":["xs","sm","md","lg"],"default":"md", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat),false] }, "tone": { "required":false,"values":["neutral","accent","info","success","warning","danger"],"default":"neutral", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat),false] }, "variant": { "required":false,"values":["solid","subtle","outline","inverse","overlay"],"default":"subtle", c: checkFormat("keyword", keywordFormat), t: "keyword", j: 0, m: ["p",formatOf(keywordFormat),false] } } };
11
11
 
12
12
  export function createUiBadge(options = {}, html) {
13
13
  const { attributes = {}, children = [], slots = {}, ...componentProps } = options;
package/vue/UiBadge.d.ts CHANGED
@@ -19,7 +19,7 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
19
19
  default: string;
20
20
  };
21
21
  variant: {
22
- type: PropType<"solid" | "subtle" | "outline" | "inverse" | null>;
22
+ type: PropType<"solid" | "subtle" | "outline" | "inverse" | "overlay" | null>;
23
23
  default: string;
24
24
  };
25
25
  }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
@@ -36,14 +36,14 @@ declare const __VLS_base: import("vue").DefineComponent<import("vue").ExtractPro
36
36
  default: string;
37
37
  };
38
38
  variant: {
39
- type: PropType<"solid" | "subtle" | "outline" | "inverse" | null>;
39
+ type: PropType<"solid" | "subtle" | "outline" | "inverse" | "overlay" | null>;
40
40
  default: string;
41
41
  };
42
42
  }>> & Readonly<{}>, {
43
43
  shape: "circle" | "dot" | "tag" | "square" | "pill" | null;
44
44
  size: "xs" | "sm" | "md" | "lg" | null;
45
45
  tone: "info" | "neutral" | "accent" | "success" | "warning" | "danger" | null;
46
- variant: "solid" | "subtle" | "outline" | "inverse" | null;
46
+ variant: "solid" | "subtle" | "outline" | "inverse" | "overlay" | null;
47
47
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
48
48
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
49
49
  declare const _default: typeof __VLS_export;
package/vue/UiBadge.vue CHANGED
@@ -22,7 +22,7 @@ const propValidityContract = {
22
22
  required: false,
23
23
  target: { attribute: 'size' },
24
24
  description:
25
- 'Scale. xs is compact; sm keeps the usual badge geometry with compact text, fitting short counts such as 99+ inside a 24px circle. md is the usual text\n badge; lg adds room to fixed marks while retaining the usual text size. A square is 24px at xs, 32px at sm and md, and 40px at lg. A circle is 16px at xs,\n 24px at sm and md, and 32px at lg.',
25
+ 'Scale. xs is compact; sm keeps the usual badge geometry with compact text, fitting short counts such as 99+ inside a 24px circle. md is the usual text\n badge; lg adds room to fixed marks while retaining the usual text size. A square is 24px at xs, 32px at sm and md, and 40px at lg. A circle is 16px at xs,\n 24px at sm and md, and 32px at lg. A small overlay circle is 20px.',
26
26
  values: ['xs', 'sm', 'md', 'lg'],
27
27
  default: 'md',
28
28
  },
@@ -40,8 +40,8 @@ const propValidityContract = {
40
40
  required: false,
41
41
  target: { attribute: 'variant' },
42
42
  description:
43
- 'Emphasis. subtle sets toned text over a soft wash; solid fills with the tone and uses contrasting text; outline keeps the surface with a toned edge.\n inverse uses a translucent contrasting surface and toned ink for a count or label on a filled control of that tone. Neutral looks the same in solid and\n subtle. When space is constrained, the label truncates with an ellipsis while its full text remains available.',
44
- values: ['solid', 'subtle', 'outline', 'inverse'],
43
+ 'Emphasis. subtle sets toned text over a soft wash; solid fills with the tone and uses contrasting text; outline keeps the surface with a toned edge.\n inverse uses a translucent contrasting surface and toned ink for a count or label on a filled control of that tone. Neutral looks the same in solid and\n subtle. overlay inherits the containing filled control\'s text colour on a faint translucent fill, without a visible rim or shadow. Its small circle is\n 20px. When space is constrained, the label truncates with an ellipsis while its full text remains available.',
44
+ values: ['solid', 'subtle', 'outline', 'inverse', 'overlay'],
45
45
  default: 'subtle',
46
46
  },
47
47
  },
@@ -59,7 +59,9 @@ const props = defineProps({
59
59
  default: 'neutral',
60
60
  },
61
61
  variant: {
62
- type: null as unknown as PropType<'solid' | 'subtle' | 'outline' | 'inverse' | null>,
62
+ type: null as unknown as PropType<
63
+ 'solid' | 'subtle' | 'outline' | 'inverse' | 'overlay' | null
64
+ >,
63
65
  default: 'subtle',
64
66
  },
65
67
  })
@@ -91,13 +93,13 @@ const checkedProps = computed(() => ({
91
93
  'tone',
92
94
  acceptedProps,
93
95
  ) as 'neutral' | 'accent' | 'info' | 'success' | 'warning' | 'danger',
94
- variant: checkedProp<'solid' | 'subtle' | 'outline' | 'inverse'>(
96
+ variant: checkedProp<'solid' | 'subtle' | 'outline' | 'inverse' | 'overlay'>(
95
97
  props['variant'],
96
98
  { kind: 'terminal', name: 'keyword' },
97
99
  false,
98
100
  'variant',
99
101
  acceptedProps,
100
- ) as 'solid' | 'subtle' | 'outline' | 'inverse',
102
+ ) as 'solid' | 'subtle' | 'outline' | 'inverse' | 'overlay',
101
103
  }))
102
104
  void checkedProps.value
103
105
  watchSyncEffect(() => {
@@ -533,6 +535,18 @@ const hostState = computed(() =>
533
535
  box-shadow: 0 1px 1px light-dark(rgb(0 0 0 / 0.08), rgb(0 0 0 / 0.12));
534
536
  }
535
537
 
538
+ /* A count can keep its containing control's contrasting ink instead of reversing it.
539
+ The faint fill is painted over that control; no second rim or shadow competes with it. */
540
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=overlay"] {
541
+ --_badge-outline-surface: var(
542
+ --ui-badge-surface,
543
+ color-mix(in srgb, currentColor 10%, transparent)
544
+ );
545
+ color: var(--ui-badge-text, inherit);
546
+ border-color: var(--ui-badge-border, transparent);
547
+ box-shadow: none;
548
+ }
549
+
536
550
  /* Neutral reverses a subtle surface/ink pair rather than a strong tone. Keep its original
537
551
  surface ink: moving it toward dark-theme primary text would reduce overlay contrast. */
538
552
  [data-component~="ui-badge"][data-ui-badge-state~="variant=inverse"][data-ui-badge-state~="tone=neutral"] {
@@ -627,6 +641,10 @@ const hostState = computed(() =>
627
641
  text-box-trim: none;
628
642
  }
629
643
 
644
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"][data-ui-badge-state~="size=sm"][data-ui-badge-state~="variant=overlay"] {
645
+ --_badge-circle-size: var(--ui-badge-circle-size, calc(var(--ui-space-4) + var(--ui-space-1)));
646
+ }
647
+
630
648
  [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"][data-ui-badge-state~="size=xs"] {
631
649
  --_badge-circle-size: var(--ui-badge-circle-size, var(--ui-space-4));
632
650
  }
@@ -63,7 +63,7 @@ var UiBadge_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
63
63
  },
64
64
  required: false,
65
65
  target: { attribute: "size" },
66
- description: "Scale. xs is compact; sm keeps the usual badge geometry with compact text, fitting short counts such as 99+ inside a 24px circle. md is the usual text\n badge; lg adds room to fixed marks while retaining the usual text size. A square is 24px at xs, 32px at sm and md, and 40px at lg. A circle is 16px at xs,\n 24px at sm and md, and 32px at lg.",
66
+ description: "Scale. xs is compact; sm keeps the usual badge geometry with compact text, fitting short counts such as 99+ inside a 24px circle. md is the usual text\n badge; lg adds room to fixed marks while retaining the usual text size. A square is 24px at xs, 32px at sm and md, and 40px at lg. A circle is 16px at xs,\n 24px at sm and md, and 32px at lg. A small overlay circle is 20px.",
67
67
  values: [
68
68
  "xs",
69
69
  "sm",
@@ -97,12 +97,13 @@ var UiBadge_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
97
97
  },
98
98
  required: false,
99
99
  target: { attribute: "variant" },
100
- description: "Emphasis. subtle sets toned text over a soft wash; solid fills with the tone and uses contrasting text; outline keeps the surface with a toned edge.\n inverse uses a translucent contrasting surface and toned ink for a count or label on a filled control of that tone. Neutral looks the same in solid and\n subtle. When space is constrained, the label truncates with an ellipsis while its full text remains available.",
100
+ description: "Emphasis. subtle sets toned text over a soft wash; solid fills with the tone and uses contrasting text; outline keeps the surface with a toned edge.\n inverse uses a translucent contrasting surface and toned ink for a count or label on a filled control of that tone. Neutral looks the same in solid and\n subtle. overlay inherits the containing filled control's text colour on a faint translucent fill, without a visible rim or shadow. Its small circle is\n 20px. When space is constrained, the label truncates with an ellipsis while its full text remains available.",
101
101
  values: [
102
102
  "solid",
103
103
  "subtle",
104
104
  "outline",
105
- "inverse"
105
+ "inverse",
106
+ "overlay"
106
107
  ],
107
108
  default: "subtle"
108
109
  }
@@ -180,6 +181,6 @@ var UiBadge_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
180
181
  }]]);
181
182
  };
182
183
  }
183
- }), [["__scopeId", "data-v-491791b3"]]);
184
+ }), [["__scopeId", "data-v-3a29482a"]]);
184
185
  //#endregion
185
186
  export { UiBadge_default as t };
@@ -360,7 +360,7 @@ svg[data-v-abaeb064] {
360
360
  syntax: "*";
361
361
  inherits: false;
362
362
  }
363
- [data-component~="ui-badge"][data-v-491791b3] {
363
+ [data-component~="ui-badge"][data-v-3a29482a] {
364
364
  --_badge-surface: color-mix(in srgb, var(--ui-surface-subtle) 80%, var(--ui-surface-default));
365
365
  --_badge-text: var(--ui-text-secondary);
366
366
  /* Sit between the soft fill and its stronger theme ink. */
@@ -406,27 +406,27 @@ svg[data-v-abaeb064] {
406
406
 
407
407
  /* A constrained badge keeps its surface intact and ellipsizes only its label. The full
408
408
  text stays in the DOM for accessible names and consumers' selected values. */
409
- .label[data-v-491791b3] {
409
+ .label[data-v-3a29482a] {
410
410
  display: block;
411
411
  min-inline-size: 0;
412
412
  overflow: hidden;
413
413
  text-overflow: ellipsis;
414
414
  }
415
- .label[data-v-491791b3]:empty {
415
+ .label[data-v-3a29482a]:empty {
416
416
  display: none;
417
417
  }
418
418
 
419
419
  /* Icon content has its own flex item so label ellipsis never clips its strokes. */
420
- .icon[data-v-491791b3] {
420
+ .icon[data-v-3a29482a] {
421
421
  display: inline-flex;
422
422
  align-items: center;
423
423
  justify-content: center;
424
424
  flex: none;
425
425
  }
426
- .icon[data-v-491791b3]:empty {
426
+ .icon[data-v-3a29482a]:empty {
427
427
  display: none;
428
428
  }
429
- .icon svg[data-v-491791b3-s] {
429
+ .icon svg[data-v-3a29482a-s] {
430
430
  display: block;
431
431
  inline-size: 1em;
432
432
  block-size: 1em;
@@ -434,39 +434,39 @@ svg[data-v-abaeb064] {
434
434
  }
435
435
 
436
436
  /* Ordinary icon-plus-text children keep inline ellipsis, with explicit glyph spacing. */
437
- .label svg[data-v-491791b3-s] {
437
+ .label svg[data-v-3a29482a-s] {
438
438
  inline-size: 1em;
439
439
  block-size: 1em;
440
440
  vertical-align: middle;
441
441
  margin-inline-end: var(--ui-space-1);
442
442
  }
443
- .label[data-v-491791b3]:has(svg) {
443
+ .label[data-v-3a29482a]:has(svg) {
444
444
  text-box-trim: none;
445
445
  }
446
446
 
447
447
  /* Center the capital/baseline text box, while clipping only inline overflow for ellipsis.
448
448
  Vertical ink remains visible so trimming never cuts off ascenders or descenders. */
449
449
  @supports (text-box-trim: trim-both) {
450
- [data-component~="ui-badge"][data-v-491791b3] {
450
+ [data-component~="ui-badge"][data-v-3a29482a] {
451
451
  display: inline-block;
452
452
  min-block-size: var(--ui-badge-min-block-size, 1.5rem);
453
453
  text-align: center;
454
454
  align-content: center;
455
455
  text-box-trim: none;
456
456
  }
457
- [data-component~="ui-badge"][data-v-491791b3]:has(.icon:not(:empty)),
458
- [data-component~="ui-badge"][data-v-491791b3]:has(.label svg) {
457
+ [data-component~="ui-badge"][data-v-3a29482a]:has(.icon:not(:empty)),
458
+ [data-component~="ui-badge"][data-v-3a29482a]:has(.label svg) {
459
459
  display: inline-flex;
460
460
  text-box-trim: none;
461
461
  }
462
- .label[data-v-491791b3]:not(:has(svg)) {
462
+ .label[data-v-3a29482a]:not(:has(svg)) {
463
463
  text-box-trim: trim-both;
464
464
  text-box-edge: cap alphabetic;
465
465
  overflow-x: clip;
466
466
  overflow-y: visible;
467
467
  }
468
468
  }
469
- [data-component~="ui-badge"][data-ui-badge-state~="size=xs"][data-v-491791b3] {
469
+ [data-component~="ui-badge"][data-ui-badge-state~="size=xs"][data-v-3a29482a] {
470
470
  box-sizing: border-box;
471
471
  min-inline-size: 1rem;
472
472
  min-block-size: var(--ui-badge-min-block-size, 1rem);
@@ -476,56 +476,56 @@ svg[data-v-abaeb064] {
476
476
  font-size: var(--ui-badge-font-size, var(--ui-font-size-2xs));
477
477
  line-height: var(--ui-badge-line-height, 1);
478
478
  }
479
- [data-component~="ui-badge"][data-ui-badge-state~="size=sm"][data-v-491791b3] {
479
+ [data-component~="ui-badge"][data-ui-badge-state~="size=sm"][data-v-3a29482a] {
480
480
  font-size: var(--ui-badge-font-size, var(--ui-font-size-2xs));
481
481
  line-height: var(--ui-badge-line-height, 1);
482
482
  }
483
- [data-component~="ui-badge"][data-ui-badge-state~="tone=accent"][data-v-491791b3] {
483
+ [data-component~="ui-badge"][data-ui-badge-state~="tone=accent"][data-v-3a29482a] {
484
484
  --_badge-surface: var(--ui-accent-strong-surface, var(--ui-accent-solid));
485
485
  --_badge-border: var(--ui-accent-strong-surface, var(--ui-accent-solid));
486
486
  --_badge-text: var(--ui-accent-on-strong, var(--ui-text-on-accent));
487
487
  }
488
- [data-component~="ui-badge"][data-ui-badge-state~="tone=danger"][data-v-491791b3] {
488
+ [data-component~="ui-badge"][data-ui-badge-state~="tone=danger"][data-v-3a29482a] {
489
489
  --_badge-surface: var(--ui-danger-strong-surface, var(--ui-danger-solid));
490
490
  --_badge-border: var(--ui-danger-strong-surface, var(--ui-danger-solid));
491
491
  --_badge-text: var(--ui-danger-on-strong, var(--ui-text-on-accent));
492
492
  }
493
- [data-component~="ui-badge"][data-ui-badge-state~="tone=success"][data-v-491791b3] {
493
+ [data-component~="ui-badge"][data-ui-badge-state~="tone=success"][data-v-3a29482a] {
494
494
  --_badge-surface: var(--ui-success-strong-surface, var(--ui-success-solid));
495
495
  --_badge-border: var(--ui-success-strong-surface, var(--ui-success-solid));
496
496
  --_badge-text: var(--ui-success-on-strong, var(--ui-text-on-accent));
497
497
  }
498
- [data-component~="ui-badge"][data-ui-badge-state~="tone=warning"][data-v-491791b3] {
498
+ [data-component~="ui-badge"][data-ui-badge-state~="tone=warning"][data-v-3a29482a] {
499
499
  --_badge-surface: var(--ui-warning-strong-surface, var(--ui-warning-solid));
500
500
  --_badge-border: var(--ui-warning-strong-surface, var(--ui-warning-solid));
501
501
  --_badge-text: var(--ui-warning-on-strong, var(--ui-text-on-accent));
502
502
  }
503
- [data-component~="ui-badge"][data-ui-badge-state~="tone=info"][data-v-491791b3] {
503
+ [data-component~="ui-badge"][data-ui-badge-state~="tone=info"][data-v-3a29482a] {
504
504
  --_badge-surface: var(--ui-info-strong-surface, var(--ui-info-solid));
505
505
  --_badge-border: var(--ui-info-strong-surface, var(--ui-info-solid));
506
506
  --_badge-text: var(--ui-info-on-strong, var(--ui-text-on-accent));
507
507
  }
508
- [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=accent"][data-v-491791b3] {
508
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=accent"][data-v-3a29482a] {
509
509
  --_badge-surface: color-mix(in srgb, var(--ui-accent-solid) 19%, var(--ui-surface-default));
510
510
  --_badge-border: color-mix(in srgb, var(--ui-accent-solid) 25%, var(--ui-surface-default));
511
511
  --_badge-text: var(--ui-accent-subtle-text, var(--ui-accent-solid));
512
512
  }
513
- [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=danger"][data-v-491791b3] {
513
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=danger"][data-v-3a29482a] {
514
514
  --_badge-surface: color-mix(in srgb, var(--ui-danger-solid) 19%, var(--ui-surface-default));
515
515
  --_badge-border: color-mix(in srgb, var(--ui-danger-solid) 25%, var(--ui-surface-default));
516
516
  --_badge-text: var(--ui-danger-subtle-text, var(--ui-danger-solid));
517
517
  }
518
- [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=success"][data-v-491791b3] {
518
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=success"][data-v-3a29482a] {
519
519
  --_badge-surface: color-mix(in srgb, var(--ui-success-solid) 19%, var(--ui-surface-default));
520
520
  --_badge-border: color-mix(in srgb, var(--ui-success-solid) 25%, var(--ui-surface-default));
521
521
  --_badge-text: var(--ui-success-subtle-text, var(--ui-success-solid));
522
522
  }
523
- [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=warning"][data-v-491791b3] {
523
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=warning"][data-v-3a29482a] {
524
524
  --_badge-surface: color-mix(in srgb, var(--ui-warning-solid) 19%, var(--ui-surface-default));
525
525
  --_badge-border: color-mix(in srgb, var(--ui-warning-solid) 25%, var(--ui-surface-default));
526
526
  --_badge-text: var(--ui-warning-subtle-text, var(--ui-warning-solid));
527
527
  }
528
- [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=info"][data-v-491791b3] {
528
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=subtle"][data-ui-badge-state~="tone=info"][data-v-3a29482a] {
529
529
  --_badge-surface: color-mix(in srgb, var(--ui-info-solid) 19%, var(--ui-surface-default));
530
530
  --_badge-border: color-mix(in srgb, var(--ui-info-solid) 25%, var(--ui-surface-default));
531
531
  --_badge-text: var(--ui-info-subtle-text, var(--ui-info-solid));
@@ -533,7 +533,7 @@ svg[data-v-abaeb064] {
533
533
 
534
534
  /* A solid colour fills with the ink. Its text is the page surface, mixed at 0% only so that it too is
535
535
  invalid while the hook is unset. */
536
- [data-component~="ui-badge"][data-ui-badge-state~="variant=solid"][data-v-491791b3] {
536
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=solid"][data-v-3a29482a] {
537
537
  --_badge-color-surface: var(--_badge-color-ink);
538
538
  --_badge-color-border: var(--_badge-color-ink);
539
539
  --_badge-color-text: color-mix(in oklab, var(--_ui-badge-color) 0%, var(--ui-surface-default));
@@ -541,7 +541,7 @@ svg[data-v-abaeb064] {
541
541
 
542
542
  /* A translucent inverse surface picks up its filled control's tone. Deepen the tone ink
543
543
  toward the theme text to retain contrast against that composed surface. */
544
- [data-component~="ui-badge"][data-ui-badge-state~="variant=inverse"][data-v-491791b3] {
544
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=inverse"][data-v-3a29482a] {
545
545
  --_badge-color-surface: color-mix(
546
546
  in srgb,
547
547
  color-mix(in oklab, var(--_ui-badge-color) 0%, var(--ui-surface-default)) 88%,
@@ -567,7 +567,7 @@ svg[data-v-abaeb064] {
567
567
 
568
568
  /* A circular count has a tone-tinted surface, a separate lighter rim and readable tone ink.
569
569
  Dark controls retain a shaded surface and theme ink with a quieter rim. */
570
- [data-component~="ui-badge"][data-ui-badge-state~="variant=inverse"][data-ui-badge-state~="shape=circle"][data-v-491791b3] {
570
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=inverse"][data-ui-badge-state~="shape=circle"][data-v-3a29482a] {
571
571
  --_badge-color-surface: light-dark(
572
572
  color-mix(
573
573
  in srgb,
@@ -633,12 +633,24 @@ svg[data-v-abaeb064] {
633
633
  box-shadow: 0 1px 1px light-dark(rgb(0 0 0 / 0.08), rgb(0 0 0 / 0.12));
634
634
  }
635
635
 
636
+ /* A count can keep its containing control's contrasting ink instead of reversing it.
637
+ The faint fill is painted over that control; no second rim or shadow competes with it. */
638
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=overlay"][data-v-3a29482a] {
639
+ --_badge-outline-surface: var(
640
+ --ui-badge-surface,
641
+ color-mix(in srgb, currentColor 10%, transparent)
642
+ );
643
+ color: var(--ui-badge-text, inherit);
644
+ border-color: var(--ui-badge-border, transparent);
645
+ box-shadow: none;
646
+ }
647
+
636
648
  /* Neutral reverses a subtle surface/ink pair rather than a strong tone. Keep its original
637
649
  surface ink: moving it toward dark-theme primary text would reduce overlay contrast. */
638
- [data-component~="ui-badge"][data-ui-badge-state~="variant=inverse"][data-ui-badge-state~="tone=neutral"][data-v-491791b3] {
650
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=inverse"][data-ui-badge-state~="tone=neutral"][data-v-3a29482a] {
639
651
  color: var(--ui-badge-text, var(--_badge-color-text, var(--_badge-surface)));
640
652
  }
641
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-v-491791b3] {
653
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-v-3a29482a] {
642
654
  --_badge-surface: var(--ui-surface-default);
643
655
  --_badge-border: var(--ui-border-strong);
644
656
  --_badge-text: var(--ui-text-secondary);
@@ -646,29 +658,29 @@ svg[data-v-abaeb064] {
646
658
  --_badge-color-surface: initial;
647
659
  --_badge-color-border: var(--_badge-color-ink);
648
660
  }
649
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="tone=accent"][data-v-491791b3] {
661
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="tone=accent"][data-v-3a29482a] {
650
662
  --_badge-border: var(--ui-accent-solid);
651
663
  --_badge-text: var(--ui-accent-subtle-text, var(--ui-accent-solid));
652
664
  }
653
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="tone=danger"][data-v-491791b3] {
665
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="tone=danger"][data-v-3a29482a] {
654
666
  --_badge-border: var(--ui-danger-solid);
655
667
  --_badge-text: var(--ui-danger-subtle-text, var(--ui-danger-solid));
656
668
  }
657
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="tone=success"][data-v-491791b3] {
669
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="tone=success"][data-v-3a29482a] {
658
670
  --_badge-border: var(--ui-success-solid);
659
671
  --_badge-text: var(--ui-success-subtle-text, var(--ui-success-solid));
660
672
  }
661
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="tone=warning"][data-v-491791b3] {
673
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="tone=warning"][data-v-3a29482a] {
662
674
  --_badge-border: var(--ui-warning-solid);
663
675
  --_badge-text: var(--ui-warning-subtle-text, var(--ui-warning-solid));
664
676
  }
665
- [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="tone=info"][data-v-491791b3] {
677
+ [data-component~="ui-badge"][data-ui-badge-state~="variant=outline"][data-ui-badge-state~="tone=info"][data-v-3a29482a] {
666
678
  --_badge-border: var(--ui-info-solid);
667
679
  --_badge-text: var(--ui-info-subtle-text, var(--ui-info-solid));
668
680
  }
669
681
 
670
682
  /* An icon mark keeps the existing tone recipe and follows the global spacing and radius. */
671
- [data-component~="ui-badge"][data-ui-badge-state~="shape=square"][data-v-491791b3] {
683
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=square"][data-v-3a29482a] {
672
684
  --_badge-square-size: var(--ui-badge-square-size, calc(var(--ui-space-4) * 2));
673
685
  display: inline-flex;
674
686
  box-sizing: border-box;
@@ -681,29 +693,29 @@ svg[data-v-abaeb064] {
681
693
  border-radius: var(--ui-radius-md);
682
694
  text-box-trim: none;
683
695
  }
684
- [data-component~="ui-badge"][data-ui-badge-state~="shape=square"][data-ui-badge-state~="size=xs"][data-v-491791b3] {
696
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=square"][data-ui-badge-state~="size=xs"][data-v-3a29482a] {
685
697
  --_badge-square-size: var(--ui-badge-square-size, calc(var(--ui-space-3) * 2));
686
698
  }
687
- [data-component~="ui-badge"][data-ui-badge-state~="shape=square"][data-ui-badge-state~="size=lg"][data-v-491791b3] {
699
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=square"][data-ui-badge-state~="size=lg"][data-v-3a29482a] {
688
700
  --_badge-square-size: var(
689
701
  --ui-badge-square-size,
690
702
  calc(var(--ui-space-4) * 2 + var(--ui-space-2))
691
703
  );
692
704
  }
693
- [data-component~="ui-badge"][data-ui-badge-state~="shape=square"] .label[data-v-491791b3] {
705
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=square"] .label[data-v-3a29482a] {
694
706
  display: inline-flex;
695
707
  align-items: center;
696
708
  justify-content: center;
697
709
  }
698
- [data-component~="ui-badge"][data-ui-badge-state~="shape=square"] .label svg[data-v-491791b3-s] {
710
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=square"] .label svg[data-v-3a29482a-s] {
699
711
  margin-inline-end: 0;
700
712
  }
701
- [data-component~="ui-badge"][data-ui-badge-state~="shape=square"] .label[data-v-491791b3]:empty {
713
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=square"] .label[data-v-3a29482a]:empty {
702
714
  display: none;
703
715
  }
704
716
 
705
717
  /* A visible glyph needs fixed geometry, unlike a content-sized pill or a hidden-label dot. */
706
- [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"][data-v-491791b3] {
718
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"][data-v-3a29482a] {
707
719
  --_badge-circle-size: var(--ui-badge-circle-size, calc(var(--ui-space-3) * 2));
708
720
  display: inline-flex;
709
721
  flex: none;
@@ -715,31 +727,34 @@ svg[data-v-abaeb064] {
715
727
  border-radius: var(--ui-radius-round);
716
728
  text-box-trim: none;
717
729
  }
718
- [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"][data-ui-badge-state~="size=xs"][data-v-491791b3] {
730
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"][data-ui-badge-state~="size=sm"][data-ui-badge-state~="variant=overlay"][data-v-3a29482a] {
731
+ --_badge-circle-size: var(--ui-badge-circle-size, calc(var(--ui-space-4) + var(--ui-space-1)));
732
+ }
733
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"][data-ui-badge-state~="size=xs"][data-v-3a29482a] {
719
734
  --_badge-circle-size: var(--ui-badge-circle-size, var(--ui-space-4));
720
735
  }
721
- [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"][data-ui-badge-state~="size=lg"][data-v-491791b3] {
736
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"][data-ui-badge-state~="size=lg"][data-v-3a29482a] {
722
737
  --_badge-circle-size: var(--ui-badge-circle-size, calc(var(--ui-space-4) * 2));
723
738
  }
724
- [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"] .label[data-v-491791b3] {
739
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"] .label[data-v-3a29482a] {
725
740
  display: inline-flex;
726
741
  align-items: center;
727
742
  justify-content: center;
728
743
  line-height: 1;
729
744
  text-box-trim: none;
730
745
  }
731
- [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"] .label svg[data-v-491791b3-s] {
746
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"] .label svg[data-v-3a29482a-s] {
732
747
  margin-inline-end: 0;
733
748
  }
734
- [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"] .label[data-v-491791b3]:empty {
749
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"] .label[data-v-3a29482a]:empty {
735
750
  display: none;
736
751
  }
737
752
 
738
753
  /* Center capital and numeral ink, rather than the font's ascent/descent line box.
739
754
  Icon labels retain flex geometry; untrimmed browsers keep that fallback too. */
740
755
  @supports (text-box-trim: trim-both) {
741
- [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"] .label[data-v-491791b3]:not(:has(svg)),
742
- [data-component~="ui-badge"][data-ui-badge-state~="shape=square"] .label[data-v-491791b3]:not(:has(svg)) {
756
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=circle"] .label[data-v-3a29482a]:not(:has(svg)),
757
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=square"] .label[data-v-3a29482a]:not(:has(svg)) {
743
758
  display: block;
744
759
  text-box-trim: trim-both;
745
760
  text-box-edge: cap alphabetic;
@@ -748,7 +763,7 @@ svg[data-v-abaeb064] {
748
763
  }
749
764
 
750
765
  /* A dot uses the badge's tone recipe; only its silhouette and visible label change. */
751
- [data-component~="ui-badge"][data-ui-badge-state~="shape=dot"][data-v-491791b3] {
766
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=dot"][data-v-3a29482a] {
752
767
  position: relative;
753
768
  box-sizing: border-box;
754
769
  flex: none;
@@ -760,7 +775,7 @@ svg[data-v-abaeb064] {
760
775
  border-radius: var(--ui-radius-round);
761
776
  text-box-trim: none;
762
777
  }
763
- [data-component~="ui-badge"][data-ui-badge-state~="shape=dot"] .label[data-v-491791b3] {
778
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=dot"] .label[data-v-3a29482a] {
764
779
  position: absolute;
765
780
  inline-size: 1px;
766
781
  block-size: 1px;
@@ -771,7 +786,7 @@ svg[data-v-abaeb064] {
771
786
 
772
787
  /* The clipped point needs an inset surface over an outer border-colour shape: a native border
773
788
  has no diagonal edge to follow. Keep this edge for every tag variant. */
774
- [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-491791b3] {
789
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-3a29482a] {
775
790
  --_badge-point: var(--ui-badge-point, var(--ui-space-2));
776
791
 
777
792
  position: relative;
@@ -788,7 +803,7 @@ svg[data-v-abaeb064] {
788
803
  0 100%
789
804
  );
790
805
  }
791
- [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-491791b3]::before {
806
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-3a29482a]::before {
792
807
  content: "";
793
808
  position: absolute;
794
809
  inset: 0;
@@ -803,37 +818,37 @@ svg[data-v-abaeb064] {
803
818
  );
804
819
  pointer-events: none;
805
820
  }
806
- [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-ui-badge-state~="variant=inverse"][data-v-491791b3] {
821
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-ui-badge-state~="variant=inverse"][data-v-3a29482a] {
807
822
  background: var(--ui-badge-border, var(--_badge-color-border, var(--_badge-text)));
808
823
  }
809
- [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-491791b3]:dir(rtl) {
824
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-3a29482a]:dir(rtl) {
810
825
  padding-inline-end: var(--ui-badge-padding-inline, var(--ui-space-2));
811
826
  padding-inline-start: calc(
812
827
  var(--ui-badge-padding-inline, var(--ui-space-2)) + var(--_badge-point)
813
828
  );
814
829
  clip-path: polygon(var(--_badge-point) 0, 100% 0, 100% 100%, var(--_badge-point) 100%, 0 50%);
815
830
  }
816
- [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-491791b3]:dir(rtl)::before {
831
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-3a29482a]:dir(rtl)::before {
817
832
  clip-path: polygon(var(--_badge-point) 0, 100% 0, 100% 100%, var(--_badge-point) 100%, 0 50%);
818
833
  }
819
834
  @media (forced-colors: active) {
820
- [data-component~="ui-badge"][data-ui-badge-state~="shape=dot"][data-v-491791b3] {
835
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=dot"][data-v-3a29482a] {
821
836
  forced-color-adjust: none;
822
837
  border-color: CanvasText;
823
838
  background: CanvasText;
824
839
  }
825
- [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-491791b3] {
840
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-3a29482a] {
826
841
  forced-color-adjust: none;
827
842
  background: CanvasText;
828
843
  color: CanvasText;
829
844
  }
830
- [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-491791b3]::before {
845
+ [data-component~="ui-badge"][data-ui-badge-state~="shape=tag"][data-v-3a29482a]::before {
831
846
  background: Canvas;
832
847
  }
833
848
  }
834
849
 
835
850
  /* A component that sets its display still honours the hidden attribute on its root. */
836
- [data-component~="ui-badge"][hidden][data-v-491791b3] {
851
+ [data-component~="ui-badge"][hidden][data-v-3a29482a] {
837
852
  display: none;
838
853
  }
839
854