@threadlabs/looma 0.14.2 → 0.14.3

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.14.2",
3
+ "version": "0.14.3",
4
4
  "type": "module",
5
5
  "description": "Looma components as HTML, Vue, and plain DOM, with design tokens.",
6
6
  "license": "MIT",
@@ -55,12 +55,90 @@
55
55
  --_meter-fill: var(--ui-danger-solid);
56
56
  }
57
57
 
58
+ /* Segments: the track and its fill show through a mask of equal segments with a gap between them.
59
+ A mask over the whole meter, rather than one element per segment, because the expression
60
+ language has no range to render N elements from, and one mask lines the fill up with the track
61
+ for free. Each period is a segment and the gap after it; N periods span the track plus one
62
+ trailing gap, so the segments sit symmetrically and right to left needs nothing of its own.
63
+ The count arrives as an attribute, not an inline custom property (which would add a style
64
+ attribute to every meter), so a continuous meter renders exactly as before, and the rules below
65
+ turn it into a number. That caps it at 12, until attr() with a type reads any count in every engine. */
66
+ :scope[data-ui-meter-segments="2"] {
67
+ --_segments: 2;
68
+ }
69
+
70
+ :scope[data-ui-meter-segments="3"] {
71
+ --_segments: 3;
72
+ }
73
+
74
+ :scope[data-ui-meter-segments="4"] {
75
+ --_segments: 4;
76
+ }
77
+
78
+ :scope[data-ui-meter-segments="5"] {
79
+ --_segments: 5;
80
+ }
81
+
82
+ :scope[data-ui-meter-segments="6"] {
83
+ --_segments: 6;
84
+ }
85
+
86
+ :scope[data-ui-meter-segments="7"] {
87
+ --_segments: 7;
88
+ }
89
+
90
+ :scope[data-ui-meter-segments="8"] {
91
+ --_segments: 8;
92
+ }
93
+
94
+ :scope[data-ui-meter-segments="9"] {
95
+ --_segments: 9;
96
+ }
97
+
98
+ :scope[data-ui-meter-segments="10"] {
99
+ --_segments: 10;
100
+ }
101
+
102
+ :scope[data-ui-meter-segments="11"] {
103
+ --_segments: 11;
104
+ }
105
+
106
+ :scope[data-ui-meter-segments="12"] {
107
+ --_segments: 12;
108
+ }
109
+
110
+ :scope[data-ui-meter-segments] {
111
+ --_meter-gap: var(--ui-space-1);
112
+ --_meter-period: calc((100% + var(--_meter-gap)) / var(--_segments));
113
+ --_meter-segment: calc(var(--_meter-period) - var(--_meter-gap));
114
+
115
+ mask-image: repeating-linear-gradient(to right, #000 0 var(--_meter-segment), transparent 0 var(--_meter-period));
116
+ }
117
+
118
+ :scope[data-ui-meter-state~="size=sm"][data-ui-meter-segments] {
119
+ --_meter-gap: var(--ui-space-0-5);
120
+ }
121
+
58
122
  /* Forced colours drop backgrounds: draw the fill in the text colour inside the outlined track. */
59
123
  @media (forced-colors: active) {
60
124
  .fill {
61
125
  forced-color-adjust: none;
62
126
  background: CanvasText;
63
127
  }
128
+
129
+ /* The mask clips the track's outline, so each segment draws its own: the inset shadow draws the
130
+ top and bottom edges and the gradient the ends. */
131
+ :scope[data-ui-meter-segments] {
132
+ forced-color-adjust: none;
133
+ background: repeating-linear-gradient(
134
+ to right,
135
+ CanvasText 0 1px,
136
+ Canvas 0 calc(var(--_meter-segment) - 1px),
137
+ CanvasText 0 var(--_meter-segment),
138
+ transparent 0 var(--_meter-period)
139
+ );
140
+ box-shadow: inset 0 0 0 1px CanvasText;
141
+ }
64
142
  }
65
143
 
66
144
  /* A component that sets its display still honours the hidden attribute on its root. */
@@ -73,8 +151,20 @@
73
151
  quiet border colour, so an empty meter still shows; every tone's fill keeps 3:1 against the
74
152
  track and the page. */
75
153
 
154
+ /* Segments: the track and its fill show through a mask of equal segments with a gap between them.
155
+ A mask over the whole meter, rather than one element per segment, because the expression
156
+ language has no range to render N elements from, and one mask lines the fill up with the track
157
+ for free. Each period is a segment and the gap after it; N periods span the track plus one
158
+ trailing gap, so the segments sit symmetrically and right to left needs nothing of its own.
159
+ The count arrives as an attribute, not an inline custom property (which would add a style
160
+ attribute to every meter), so a continuous meter renders exactly as before, and the rules below
161
+ turn it into a number. That caps it at 12, until attr() with a type reads any count in every engine. */
162
+
76
163
  /* Forced colours drop backgrounds: draw the fill in the text colour inside the outlined track. */
77
164
  @media (forced-colors: active) {
165
+
166
+ /* The mask clips the track's outline, so each segment draws its own: the inset shadow draws the
167
+ top and bottom edges and the gradient the ends. */
78
168
  }
79
169
 
80
170
  /* A component that sets its display still honours the hidden attribute on its root. */
@@ -8,6 +8,7 @@ export interface UiMeterElement extends HTMLSpanElement {
8
8
  export interface UiMeterProps {
9
9
  label?: string | null;
10
10
  max?: number | null;
11
+ segments?: number | null;
11
12
  size?: "sm" | "md" | null;
12
13
  tone?: "neutral" | "accent" | "info" | "success" | "warning" | "danger" | null;
13
14
  value?: number | null;
@@ -2,7 +2,7 @@
2
2
  import { manageComponentLifecycle } from "@nextwebwg/html-next/runtime";
3
3
  import "../styles/ui-meter.css";
4
4
 
5
- const definition = {...{"contract":{"tag":"ui-meter","props":{"label":{"type":"string","required":false,"target":{"attribute":"label"},"default":""},"max":{"type":"number","required":false,"target":{"attribute":"aria-valuemax"},"default":1},"size":{"type":{"enum":["sm","md"]},"required":false,"target":{"attribute":"size"},"default":"md"},"tone":{"type":{"enum":["neutral","accent","info","success","warning","danger"]},"required":false,"target":{"attribute":"tone"},"default":"neutral"},"value":{"type":"number","required":false,"target":{"attribute":"value"},"default":0},"valueText":{"type":"string","required":false,"target":{"attribute":"valuetext"},"default":""}}},"template":{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"role","value":"meter"},{"kind":"literal","name":"aria-valuemin","value":"0"},{"kind":"attribute","name":"aria-valuemax","expression":"max","expressionPlan":{"source":"max","ast":{"kind":"id","name":"max"},"dependencies":["max"]}},{"kind":"attribute","name":"aria-valuenow","expression":"amount","expressionPlan":{"source":"amount","ast":{"kind":"id","name":"amount"},"dependencies":["amount"]}},{"kind":"attribute","name":"aria-valuetext","expression":"{ true: valueText, false: format('%s%', whole) }[format('%s', valueText != '')]","expressionPlan":{"source":"{ true: valueText, false: format('%s%', whole) }[format('%s', valueText != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"id","name":"valueText"}},{"key":"false","value":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s%"},{"kind":"id","name":"whole"}]}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"valueText"},"right":{"kind":"literal","value":""}}]}},"dependencies":["valueText","whole"]}},{"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"]}}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"fill"},{"kind":"attribute","name":"inline-size","expression":"format('%s%', percent)","expressionPlan":{"source":"format('%s%', percent)","ast":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s%"},{"kind":"id","name":"percent"}]},"dependencies":["percent"]},"target":"style"}],"children":[]}]},"declarations":[{"kind":"computed","name":"amount","expression":{"source":"clamp(0, value, max)","ast":{"kind":"call","fn":"clamp","args":[{"kind":"literal","value":0},{"kind":"id","name":"value"},{"kind":"id","name":"max"}]},"dependencies":["max","value"]}},{"kind":"computed","name":"percent","expression":{"source":"{ true: amount / max * 100, false: 0 }[format('%s', max > 0)]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"binary","op":"*","left":{"kind":"binary","op":"/","left":{"kind":"id","name":"amount"},"right":{"kind":"id","name":"max"}},"right":{"kind":"literal","value":100}}},{"key":"false","value":{"kind":"literal","value":0}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":">","left":{"kind":"id","name":"max"},"right":{"kind":"literal","value":0}}]}},"dependencies":["amount","max"]}},{"kind":"computed","name":"whole","expression":{"source":"percent + 0.5 - (percent + 0.5) % 1","ast":{"kind":"binary","op":"-","left":{"kind":"binary","op":"+","left":{"kind":"id","name":"percent"},"right":{"kind":"literal","value":0.5}},"right":{"kind":"binary","op":"%","left":{"kind":"binary","op":"+","left":{"kind":"id","name":"percent"},"right":{"kind":"literal","value":0.5}},"right":{"kind":"literal","value":1}}},"dependencies":["percent"]}}],"root":{"kind":"native","element":"span","choices":["span"]}},source:{file:import.meta.url},css:""};
5
+ const definition = {...{"contract":{"tag":"ui-meter","props":{"label":{"type":"string","required":false,"target":{"attribute":"label"},"default":""},"max":{"type":"number","required":false,"target":{"attribute":"aria-valuemax"},"default":1},"segments":{"type":"number","required":false,"target":{"attribute":"segments"},"default":0},"size":{"type":{"enum":["sm","md"]},"required":false,"target":{"attribute":"size"},"default":"md"},"tone":{"type":{"enum":["neutral","accent","info","success","warning","danger"]},"required":false,"target":{"attribute":"tone"},"default":"neutral"},"value":{"type":"number","required":false,"target":{"attribute":"value"},"default":0},"valueText":{"type":"string","required":false,"target":{"attribute":"valuetext"},"default":""}}},"template":{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"role","value":"meter"},{"kind":"literal","name":"aria-valuemin","value":"0"},{"kind":"attribute","name":"aria-valuemax","expression":"max","expressionPlan":{"source":"max","ast":{"kind":"id","name":"max"},"dependencies":["max"]}},{"kind":"attribute","name":"aria-valuenow","expression":"amount","expressionPlan":{"source":"amount","ast":{"kind":"id","name":"amount"},"dependencies":["amount"]}},{"kind":"attribute","name":"aria-valuetext","expression":"{ true: valueText, false: format('%s%', whole) }[format('%s', valueText != '')]","expressionPlan":{"source":"{ true: valueText, false: format('%s%', whole) }[format('%s', valueText != '')]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"id","name":"valueText"}},{"key":"false","value":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s%"},{"kind":"id","name":"whole"}]}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":"!=","left":{"kind":"id","name":"valueText"},"right":{"kind":"literal","value":""}}]}},"dependencies":["valueText","whole"]}},{"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":"data-ui-meter-segments","expression":"{ true: segments, false: null }[format('%s', segments > 1)]","expressionPlan":{"source":"{ true: segments, false: null }[format('%s', segments > 1)]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"id","name":"segments"}},{"key":"false","value":{"kind":"literal","value":null}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":">","left":{"kind":"id","name":"segments"},"right":{"kind":"literal","value":1}}]}},"dependencies":["segments"]}}],"children":[{"kind":"element","name":"span","attributes":[{"kind":"literal","name":"class","value":"fill"},{"kind":"attribute","name":"inline-size","expression":"format('%s%', percent)","expressionPlan":{"source":"format('%s%', percent)","ast":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s%"},{"kind":"id","name":"percent"}]},"dependencies":["percent"]},"target":"style"}],"children":[]}]},"declarations":[{"kind":"computed","name":"amount","expression":{"source":"clamp(0, value, max)","ast":{"kind":"call","fn":"clamp","args":[{"kind":"literal","value":0},{"kind":"id","name":"value"},{"kind":"id","name":"max"}]},"dependencies":["max","value"]}},{"kind":"computed","name":"percent","expression":{"source":"{ true: amount / max * 100, false: 0 }[format('%s', max > 0)]","ast":{"kind":"index","object":{"kind":"object","pairs":[{"key":"true","value":{"kind":"binary","op":"*","left":{"kind":"binary","op":"/","left":{"kind":"id","name":"amount"},"right":{"kind":"id","name":"max"}},"right":{"kind":"literal","value":100}}},{"key":"false","value":{"kind":"literal","value":0}}]},"index":{"kind":"call","fn":"format","args":[{"kind":"literal","value":"%s"},{"kind":"binary","op":">","left":{"kind":"id","name":"max"},"right":{"kind":"literal","value":0}}]}},"dependencies":["amount","max"]}},{"kind":"computed","name":"whole","expression":{"source":"percent + 0.5 - (percent + 0.5) % 1","ast":{"kind":"binary","op":"-","left":{"kind":"binary","op":"+","left":{"kind":"id","name":"percent"},"right":{"kind":"literal","value":0.5}},"right":{"kind":"binary","op":"%","left":{"kind":"binary","op":"+","left":{"kind":"id","name":"percent"},"right":{"kind":"literal","value":0.5}},"right":{"kind":"literal","value":1}}},"dependencies":["percent"]}}],"root":{"kind":"native","element":"span","choices":["span"]}},source:{file:import.meta.url},css:""};
6
6
 
7
7
  export function createUiMeter(options = {}) {
8
8
  const { attributes = {}, children = [], slots = {}, ...componentProps } = options;
package/vue/UiMeter.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  type __VLS_Props = {
2
2
  label?: string;
3
3
  max?: number;
4
+ segments?: number;
4
5
  size?: 'sm' | 'md';
5
6
  tone?: 'neutral' | 'accent' | 'info' | 'success' | 'warning' | 'danger';
6
7
  value?: number;
@@ -12,6 +13,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
12
13
  value: number;
13
14
  max: number;
14
15
  tone: "neutral" | "accent" | "info" | "success" | "warning" | "danger";
16
+ segments: number;
15
17
  valueText: string;
16
18
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
19
  declare const _default: typeof __VLS_export;
package/vue/UiMeter.vue CHANGED
@@ -8,6 +8,7 @@ const props = withDefaults(
8
8
  defineProps<{
9
9
  label?: string
10
10
  max?: number
11
+ segments?: number
11
12
  size?: 'sm' | 'md'
12
13
  tone?: 'neutral' | 'accent' | 'info' | 'success' | 'warning' | 'danger'
13
14
  value?: number
@@ -16,6 +17,7 @@ const props = withDefaults(
16
17
  {
17
18
  label: '',
18
19
  max: 1,
20
+ segments: 0,
19
21
  size: 'md',
20
22
  tone: 'neutral',
21
23
  value: 0,
@@ -119,6 +121,7 @@ function number(value: unknown): number | undefined {
119
121
  :aria-label="typeof (({ true: label, false: null })[`${label !== ''}`]) === 'boolean'
120
122
  ? String(({ true: label, false: null })[`${label !== ''}`])
121
123
  : attribute(({ true: label, false: null })[`${label !== ''}`])"
124
+ :data-ui-meter-segments="attribute(({ true: segments, false: null })[`${segments > 1}`])"
122
125
  :data-ui-meter-state="hostState || undefined"
123
126
  ><span class="fill" :style="{ 'inline-size': `${text(percent)}%` }"></span></span>
124
127
  </template>
@@ -179,12 +182,94 @@ function number(value: unknown): number | undefined {
179
182
  --_meter-fill: var(--ui-danger-solid);
180
183
  }
181
184
 
185
+ /* Segments: the track and its fill show through a mask of equal segments with a gap between them.
186
+ A mask over the whole meter, rather than one element per segment, because the expression
187
+ language has no range to render N elements from, and one mask lines the fill up with the track
188
+ for free. Each period is a segment and the gap after it; N periods span the track plus one
189
+ trailing gap, so the segments sit symmetrically and right to left needs nothing of its own.
190
+ The count arrives as an attribute, not an inline custom property (which would add a style
191
+ attribute to every meter), so a continuous meter renders exactly as before, and the rules below
192
+ turn it into a number. That caps it at 12, until attr() with a type reads any count in every engine. */
193
+ [data-component~="ui-meter"][data-ui-meter-segments="2"] {
194
+ --_segments: 2;
195
+ }
196
+
197
+ [data-component~="ui-meter"][data-ui-meter-segments="3"] {
198
+ --_segments: 3;
199
+ }
200
+
201
+ [data-component~="ui-meter"][data-ui-meter-segments="4"] {
202
+ --_segments: 4;
203
+ }
204
+
205
+ [data-component~="ui-meter"][data-ui-meter-segments="5"] {
206
+ --_segments: 5;
207
+ }
208
+
209
+ [data-component~="ui-meter"][data-ui-meter-segments="6"] {
210
+ --_segments: 6;
211
+ }
212
+
213
+ [data-component~="ui-meter"][data-ui-meter-segments="7"] {
214
+ --_segments: 7;
215
+ }
216
+
217
+ [data-component~="ui-meter"][data-ui-meter-segments="8"] {
218
+ --_segments: 8;
219
+ }
220
+
221
+ [data-component~="ui-meter"][data-ui-meter-segments="9"] {
222
+ --_segments: 9;
223
+ }
224
+
225
+ [data-component~="ui-meter"][data-ui-meter-segments="10"] {
226
+ --_segments: 10;
227
+ }
228
+
229
+ [data-component~="ui-meter"][data-ui-meter-segments="11"] {
230
+ --_segments: 11;
231
+ }
232
+
233
+ [data-component~="ui-meter"][data-ui-meter-segments="12"] {
234
+ --_segments: 12;
235
+ }
236
+
237
+ [data-component~="ui-meter"][data-ui-meter-segments] {
238
+ --_meter-gap: var(--ui-space-1);
239
+ --_meter-period: calc((100% + var(--_meter-gap)) / var(--_segments));
240
+ --_meter-segment: calc(var(--_meter-period) - var(--_meter-gap));
241
+
242
+ mask-image: repeating-linear-gradient(
243
+ to right,
244
+ #000 0 var(--_meter-segment),
245
+ transparent 0 var(--_meter-period)
246
+ );
247
+ }
248
+
249
+ [data-component~="ui-meter"][data-ui-meter-state~="size=sm"][data-ui-meter-segments] {
250
+ --_meter-gap: var(--ui-space-0-5);
251
+ }
252
+
182
253
  /* Forced colours drop backgrounds: draw the fill in the text colour inside the outlined track. */
183
254
  @media (forced-colors: active) {
184
255
  .fill {
185
256
  forced-color-adjust: none;
186
257
  background: CanvasText;
187
258
  }
259
+
260
+ /* The mask clips the track's outline, so each segment draws its own: the inset shadow draws the
261
+ top and bottom edges and the gradient the ends. */
262
+ [data-component~="ui-meter"][data-ui-meter-segments] {
263
+ forced-color-adjust: none;
264
+ background: repeating-linear-gradient(
265
+ to right,
266
+ CanvasText 0 1px,
267
+ Canvas 0 calc(var(--_meter-segment) - 1px),
268
+ CanvasText 0 var(--_meter-segment),
269
+ transparent 0 var(--_meter-period)
270
+ );
271
+ box-shadow: inset 0 0 0 1px CanvasText;
272
+ }
188
273
  }
189
274
 
190
275
  /* A component that sets its display still honours the hidden attribute on its root. */
@@ -6,6 +6,7 @@ var _hoisted_1 = [
6
6
  "aria-valuenow",
7
7
  "aria-valuetext",
8
8
  "aria-label",
9
+ "data-ui-meter-segments",
9
10
  "data-ui-meter-state"
10
11
  ];
11
12
  //#endregion
@@ -16,6 +17,7 @@ var UiMeter_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
16
17
  props: {
17
18
  label: { default: "" },
18
19
  max: { default: 1 },
20
+ segments: { default: 0 },
19
21
  size: { default: "md" },
20
22
  tone: { default: "neutral" },
21
23
  value: { default: 0 },
@@ -70,6 +72,10 @@ var UiMeter_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
70
72
  true: __props.label,
71
73
  false: null
72
74
  }[`${__props.label !== ""}`]),
75
+ "data-ui-meter-segments": attribute({
76
+ true: __props.segments,
77
+ false: null
78
+ }[`${__props.segments > 1}`]),
73
79
  "data-ui-meter-state": hostState.value || void 0
74
80
  }), [createElementVNode("span", {
75
81
  class: "fill",
@@ -77,6 +83,6 @@ var UiMeter_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE
77
83
  }, null, 4)], 16, _hoisted_1);
78
84
  };
79
85
  }
80
- }), [["__scopeId", "data-v-f716cd47"]]);
86
+ }), [["__scopeId", "data-v-c0b7936e"]]);
81
87
  //#endregion
82
88
  export { UiMeter_default as t };
@@ -4443,7 +4443,7 @@ svg[data-v-006fa1b2-s] {
4443
4443
  syntax: "*";
4444
4444
  inherits: false;
4445
4445
  }
4446
- [data-component~="ui-meter"][data-v-f716cd47] {
4446
+ [data-component~="ui-meter"][data-v-c0b7936e] {
4447
4447
  --_meter-fill: var(--ui-text-muted);
4448
4448
 
4449
4449
  display: inline-block;
@@ -4458,41 +4458,111 @@ svg[data-v-006fa1b2-s] {
4458
4458
  /* Unseen until forced colours draw it, where the track's own colour is gone. */
4459
4459
  outline: 1px solid transparent;
4460
4460
  }
4461
- [data-component~="ui-meter"][data-ui-meter-state~="size=sm"][data-v-f716cd47] {
4461
+ [data-component~="ui-meter"][data-ui-meter-state~="size=sm"][data-v-c0b7936e] {
4462
4462
  inline-size: var(--ui-meter-inline-size, 5rem);
4463
4463
  block-size: var(--ui-space-1);
4464
4464
  }
4465
- .fill[data-v-f716cd47] {
4465
+ .fill[data-v-c0b7936e] {
4466
4466
  display: block;
4467
4467
  block-size: 100%;
4468
4468
  background: var(--_meter-fill);
4469
4469
  }
4470
- [data-component~="ui-meter"][data-ui-meter-state~="tone=accent"][data-v-f716cd47] {
4470
+ [data-component~="ui-meter"][data-ui-meter-state~="tone=accent"][data-v-c0b7936e] {
4471
4471
  --_meter-fill: var(--ui-accent-solid);
4472
4472
  }
4473
- [data-component~="ui-meter"][data-ui-meter-state~="tone=info"][data-v-f716cd47] {
4473
+ [data-component~="ui-meter"][data-ui-meter-state~="tone=info"][data-v-c0b7936e] {
4474
4474
  --_meter-fill: var(--ui-info-solid);
4475
4475
  }
4476
- [data-component~="ui-meter"][data-ui-meter-state~="tone=success"][data-v-f716cd47] {
4476
+ [data-component~="ui-meter"][data-ui-meter-state~="tone=success"][data-v-c0b7936e] {
4477
4477
  --_meter-fill: var(--ui-success-solid);
4478
4478
  }
4479
- [data-component~="ui-meter"][data-ui-meter-state~="tone=warning"][data-v-f716cd47] {
4479
+ [data-component~="ui-meter"][data-ui-meter-state~="tone=warning"][data-v-c0b7936e] {
4480
4480
  --_meter-fill: var(--ui-warning-solid);
4481
4481
  }
4482
- [data-component~="ui-meter"][data-ui-meter-state~="tone=danger"][data-v-f716cd47] {
4482
+ [data-component~="ui-meter"][data-ui-meter-state~="tone=danger"][data-v-c0b7936e] {
4483
4483
  --_meter-fill: var(--ui-danger-solid);
4484
4484
  }
4485
4485
 
4486
+ /* Segments: the track and its fill show through a mask of equal segments with a gap between them.
4487
+ A mask over the whole meter, rather than one element per segment, because the expression
4488
+ language has no range to render N elements from, and one mask lines the fill up with the track
4489
+ for free. Each period is a segment and the gap after it; N periods span the track plus one
4490
+ trailing gap, so the segments sit symmetrically and right to left needs nothing of its own.
4491
+ The count arrives as an attribute, not an inline custom property (which would add a style
4492
+ attribute to every meter), so a continuous meter renders exactly as before, and the rules below
4493
+ turn it into a number. That caps it at 12, until attr() with a type reads any count in every engine. */
4494
+ [data-component~="ui-meter"][data-ui-meter-segments="2"][data-v-c0b7936e] {
4495
+ --_segments: 2;
4496
+ }
4497
+ [data-component~="ui-meter"][data-ui-meter-segments="3"][data-v-c0b7936e] {
4498
+ --_segments: 3;
4499
+ }
4500
+ [data-component~="ui-meter"][data-ui-meter-segments="4"][data-v-c0b7936e] {
4501
+ --_segments: 4;
4502
+ }
4503
+ [data-component~="ui-meter"][data-ui-meter-segments="5"][data-v-c0b7936e] {
4504
+ --_segments: 5;
4505
+ }
4506
+ [data-component~="ui-meter"][data-ui-meter-segments="6"][data-v-c0b7936e] {
4507
+ --_segments: 6;
4508
+ }
4509
+ [data-component~="ui-meter"][data-ui-meter-segments="7"][data-v-c0b7936e] {
4510
+ --_segments: 7;
4511
+ }
4512
+ [data-component~="ui-meter"][data-ui-meter-segments="8"][data-v-c0b7936e] {
4513
+ --_segments: 8;
4514
+ }
4515
+ [data-component~="ui-meter"][data-ui-meter-segments="9"][data-v-c0b7936e] {
4516
+ --_segments: 9;
4517
+ }
4518
+ [data-component~="ui-meter"][data-ui-meter-segments="10"][data-v-c0b7936e] {
4519
+ --_segments: 10;
4520
+ }
4521
+ [data-component~="ui-meter"][data-ui-meter-segments="11"][data-v-c0b7936e] {
4522
+ --_segments: 11;
4523
+ }
4524
+ [data-component~="ui-meter"][data-ui-meter-segments="12"][data-v-c0b7936e] {
4525
+ --_segments: 12;
4526
+ }
4527
+ [data-component~="ui-meter"][data-ui-meter-segments][data-v-c0b7936e] {
4528
+ --_meter-gap: var(--ui-space-1);
4529
+ --_meter-period: calc((100% + var(--_meter-gap)) / var(--_segments));
4530
+ --_meter-segment: calc(var(--_meter-period) - var(--_meter-gap));
4531
+
4532
+ mask-image: repeating-linear-gradient(
4533
+ to right,
4534
+ #000 0 var(--_meter-segment),
4535
+ transparent 0 var(--_meter-period)
4536
+ );
4537
+ }
4538
+ [data-component~="ui-meter"][data-ui-meter-state~="size=sm"][data-ui-meter-segments][data-v-c0b7936e] {
4539
+ --_meter-gap: var(--ui-space-0-5);
4540
+ }
4541
+
4486
4542
  /* Forced colours drop backgrounds: draw the fill in the text colour inside the outlined track. */
4487
4543
  @media (forced-colors: active) {
4488
- .fill[data-v-f716cd47] {
4544
+ .fill[data-v-c0b7936e] {
4489
4545
  forced-color-adjust: none;
4490
4546
  background: CanvasText;
4491
4547
  }
4548
+
4549
+ /* The mask clips the track's outline, so each segment draws its own: the inset shadow draws the
4550
+ top and bottom edges and the gradient the ends. */
4551
+ [data-component~="ui-meter"][data-ui-meter-segments][data-v-c0b7936e] {
4552
+ forced-color-adjust: none;
4553
+ background: repeating-linear-gradient(
4554
+ to right,
4555
+ CanvasText 0 1px,
4556
+ Canvas 0 calc(var(--_meter-segment) - 1px),
4557
+ CanvasText 0 var(--_meter-segment),
4558
+ transparent 0 var(--_meter-period)
4559
+ );
4560
+ box-shadow: inset 0 0 0 1px CanvasText;
4561
+ }
4492
4562
  }
4493
4563
 
4494
4564
  /* A component that sets its display still honours the hidden attribute on its root. */
4495
- [data-component~="ui-meter"][hidden][data-v-f716cd47] {
4565
+ [data-component~="ui-meter"][hidden][data-v-c0b7936e] {
4496
4566
  display: none;
4497
4567
  }
4498
4568