@adia-ai/web-components 0.5.11 → 0.5.13

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.
@@ -0,0 +1,218 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/ColorInput.json",
4
+ "title": "ColorInput",
5
+ "description": "Compact form-bearing color input — opens a popover-anchored\n`<color-picker-ui>` from an inline swatch button. §302 (v0.5.12,\nFEEDBACK-29 re-bucket from v0.6.0). Canonicalizes the USAGE.md §221f\nOption B recipe (popover + button + color-picker) into a single\nform-associated tag for inline form contexts (settings drawer \"source\ncolor\" field, swatch-row inline-edit, etc.).",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "baseHue": {
17
+ "description": "Reference hue (degrees) for the hueDriftMax constraint, forwarded to inner picker. Default NaN — picker falls back to its hue at first commit.",
18
+ "type": "number",
19
+ "default": "NaN"
20
+ },
21
+ "component": {
22
+ "const": "ColorInput"
23
+ },
24
+ "disabled": {
25
+ "description": "Disables the trigger button + suppresses popover.",
26
+ "type": "boolean",
27
+ "default": false
28
+ },
29
+ "format": {
30
+ "description": "Output format for the value property + event detail. Hex (`#rrggbb`) or OKLCH (`oklch(L C H)`).",
31
+ "type": "string",
32
+ "enum": [
33
+ "hex",
34
+ "oklch"
35
+ ],
36
+ "default": "hex"
37
+ },
38
+ "hueDriftMax": {
39
+ "description": "Generation constraint forwarded to inner picker — maximum allowed signed-shortest-path hue deviation (degrees) from baseHue. Default NaN (no constraint).",
40
+ "type": "number",
41
+ "default": "NaN"
42
+ },
43
+ "maxChroma": {
44
+ "description": "Generation constraint forwarded to the inner `<color-picker-ui>` (v0.5.13\n§-TBD, FB-33 §1). Clamp the OKLCH chroma channel to at most this value.\nDefault Infinity (no constraint).\n",
45
+ "type": "number",
46
+ "default": "Infinity"
47
+ },
48
+ "maxL": {
49
+ "description": "Generation constraint forwarded to inner picker — clamp OKLCH lightness to at most this value (0..1). Default 1.",
50
+ "type": "number",
51
+ "default": 1
52
+ },
53
+ "minL": {
54
+ "description": "Generation constraint forwarded to inner picker — clamp OKLCH lightness to at least this value (0..1). Default 0.",
55
+ "type": "number",
56
+ "default": 0
57
+ },
58
+ "name": {
59
+ "description": "Form field name.",
60
+ "type": "string",
61
+ "default": ""
62
+ },
63
+ "open": {
64
+ "description": "Reflects the popover's open state. Set programmatically to open/close the picker without a click.",
65
+ "type": "boolean",
66
+ "default": false
67
+ },
68
+ "placement": {
69
+ "description": "Popover placement relative to the trigger.",
70
+ "type": "string",
71
+ "enum": [
72
+ "top",
73
+ "bottom",
74
+ "left",
75
+ "right",
76
+ "top-start",
77
+ "top-end",
78
+ "bottom-start",
79
+ "bottom-end"
80
+ ],
81
+ "default": "bottom-start"
82
+ },
83
+ "value": {
84
+ "description": "Current color as a string in the active [format].",
85
+ "type": "string",
86
+ "default": "#3b82f6"
87
+ }
88
+ },
89
+ "required": [
90
+ "component"
91
+ ],
92
+ "unevaluatedProperties": false,
93
+ "x-adiaui": {
94
+ "anti_patterns": [],
95
+ "category": "input",
96
+ "composes": [
97
+ "button-ui",
98
+ "popover-ui",
99
+ "color-picker-ui"
100
+ ],
101
+ "events": {
102
+ "change": {
103
+ "description": "Fired when the color is committed (pointerup / Enter / picker close). Bubbles from inner picker.",
104
+ "detail": {
105
+ "c": {
106
+ "description": "Parsed OKLCH chroma scalar.",
107
+ "type": "number"
108
+ },
109
+ "h": {
110
+ "description": "Parsed OKLCH hue scalar (degrees, NaN ok for achromatic).",
111
+ "type": "number"
112
+ },
113
+ "hex": {
114
+ "description": "Hex form (`#rrggbb`).",
115
+ "type": "string"
116
+ },
117
+ "l": {
118
+ "description": "Parsed OKLCH lightness scalar (0..1). v0.5.13 §-TBD (FB-33 §1-adjacent).",
119
+ "type": "number"
120
+ },
121
+ "oklch": {
122
+ "description": "OKLCH string (`oklch(L C H)`).",
123
+ "type": "string"
124
+ },
125
+ "value": {
126
+ "description": "Color in the active [format] (hex or oklch).",
127
+ "type": "string"
128
+ }
129
+ }
130
+ },
131
+ "input": {
132
+ "description": "Fired during continuous drag of the inner picker. Bubbles from inner picker.",
133
+ "detail": {
134
+ "c": {
135
+ "type": "number"
136
+ },
137
+ "h": {
138
+ "type": "number"
139
+ },
140
+ "hex": {
141
+ "type": "string"
142
+ },
143
+ "l": {
144
+ "type": "number"
145
+ },
146
+ "oklch": {
147
+ "type": "string"
148
+ },
149
+ "value": {
150
+ "description": "Color in the active [format].",
151
+ "type": "string"
152
+ }
153
+ }
154
+ }
155
+ },
156
+ "examples": [
157
+ {
158
+ "description": "Inline compact color input — popover-anchored picker on click.",
159
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"ColorInput\",\n \"name\": \"brand\",\n \"value\": \"#3b82f6\"\n }\n]",
160
+ "name": "color-input-basic"
161
+ },
162
+ {
163
+ "description": "Compact picker exposing OKLCH value form.",
164
+ "a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"ColorInput\",\n \"name\": \"accent\",\n \"format\": \"oklch\",\n \"value\": \"oklch(0.7 0.15 220)\"\n }\n]",
165
+ "name": "color-input-oklch"
166
+ }
167
+ ],
168
+ "keywords": [
169
+ "color-input",
170
+ "color",
171
+ "input",
172
+ "picker",
173
+ "swatch",
174
+ "inline",
175
+ "compact"
176
+ ],
177
+ "name": "UIColorInput",
178
+ "related": [
179
+ "color-picker",
180
+ "popover",
181
+ "button",
182
+ "swatch"
183
+ ],
184
+ "slots": {},
185
+ "states": [
186
+ {
187
+ "description": "Default, ready for interaction.",
188
+ "name": "idle"
189
+ },
190
+ {
191
+ "description": "Popover is open; picker visible.",
192
+ "attribute": "open",
193
+ "name": "open"
194
+ },
195
+ {
196
+ "description": "Non-interactive; dimmed.",
197
+ "attribute": "disabled",
198
+ "name": "disabled"
199
+ }
200
+ ],
201
+ "synonyms": {
202
+ "color-field": [
203
+ "color-input"
204
+ ],
205
+ "inline-color-picker": [
206
+ "color-input"
207
+ ]
208
+ },
209
+ "tag": "color-input-ui",
210
+ "tokens": {
211
+ "--color-input-swatch-size": {
212
+ "description": "Diameter (em) of the inline swatch circle inside the trigger button. Default 1em."
213
+ }
214
+ },
215
+ "traits": [],
216
+ "version": 1
217
+ }
218
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * <color-input-ui> — compact form-bearing color input.
3
+ *
4
+ * §302 (v0.5.12, FEEDBACK-29 re-bucket). Light-DOM composition of
5
+ * <popover-ui> + <button-ui> + <color-picker-ui>. The host element
6
+ * carries form-control sizing tokens; inner button + popover keep
7
+ * their own primitive styling.
8
+ */
9
+
10
+ @scope (color-input-ui) {
11
+ :scope {
12
+ --color-input-swatch-size: 1em;
13
+
14
+ display: inline-flex;
15
+ vertical-align: middle;
16
+ }
17
+
18
+ :scope[disabled] {
19
+ opacity: 0.55;
20
+ pointer-events: none;
21
+ }
22
+
23
+ :scope > popover-ui {
24
+ display: inline-flex;
25
+ }
26
+
27
+ /* Inline swatch chip inside the trigger button. The button-ui owns its
28
+ * own padding + height; the swatch is sized in em so it tracks the
29
+ * button's font-size + density automatically. */
30
+ .color-input__swatch {
31
+ display: inline-block;
32
+ inline-size: var(--color-input-swatch-size);
33
+ block-size: var(--color-input-swatch-size);
34
+ border-radius: 50%;
35
+ background: var(--swatch-color, transparent);
36
+ border: 1px solid var(--a-ui-border, currentColor);
37
+ margin-inline-end: var(--a-space-2, 0.5em);
38
+ flex: 0 0 auto;
39
+ }
40
+
41
+ .color-input__value {
42
+ font-variant-numeric: tabular-nums;
43
+ font-family: var(--a-font-family-mono, monospace);
44
+ font-size: 0.92em;
45
+ color: var(--a-ui-text, currentColor);
46
+ }
47
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `<color-input-ui>` — compact form-bearing color input.
3
+ *
4
+ * §302 (v0.5.12, FEEDBACK-29 re-bucket from v0.6.0). Canonicalizes the
5
+ * USAGE.md §221f recipe (popover + button + color-picker) into a single
6
+ * form-associated tag for inline form contexts (settings drawer "source
7
+ * color" field, swatch-row inline-edit). Light-DOM composition; the
8
+ * inner picker fires `change` + `input` which this element re-emits
9
+ * with a flattened detail payload that always carries both hex + oklch
10
+ * string forms regardless of `[format]`.
11
+ *
12
+ * Hand-authored (FORM_BEARING in dts-codegen) because the rich event
13
+ * detail (parallel hex+oklch views) can't be expressed by the
14
+ * yaml `events:` shape alone.
15
+ *
16
+ * @see https://ui-kit.exe.xyz/site/components/color-input
17
+ */
18
+
19
+ import { UIFormElement } from '../../core/form.js';
20
+
21
+ /**
22
+ * Output format selected by the `[format]` attribute. Drives the shape of
23
+ * `value` (and the matching `detail.value` field). Both `hex` and `oklch`
24
+ * forms are always present on the event detail regardless of `[format]`.
25
+ */
26
+ export type ColorInputFormat = 'hex' | 'oklch';
27
+
28
+ /**
29
+ * Detail payload for both `change` (commit) and `input` (continuous drag)
30
+ * events. `value` mirrors the current `[format]`; `hex` and `oklch` are
31
+ * always populated parallel views, so consumers don't need to parse the
32
+ * string.
33
+ */
34
+ export interface ColorInputChangeEventDetail {
35
+ /** Format-respecting string. Equals `hex` when `[format="hex"]`; equals `oklch` when `[format="oklch"]`. */
36
+ value: string;
37
+ /** Hex form (`#rrggbb`). Gamut-mapped to sRGB. */
38
+ hex: string;
39
+ /** OKLCH string (`oklch(L C H)`, fixed precision). */
40
+ oklch: string;
41
+ /** Parsed OKLCH lightness (0..1). v0.5.13 §-TBD (FB-33 §1-adjacent). */
42
+ l: number;
43
+ /** Parsed OKLCH chroma. */
44
+ c: number;
45
+ /** Parsed OKLCH hue (degrees, NaN ok for achromatic). */
46
+ h: number;
47
+ }
48
+ export type ColorInputChangeEvent = CustomEvent<ColorInputChangeEventDetail>;
49
+ export type ColorInputInputEvent = CustomEvent<ColorInputChangeEventDetail>;
50
+
51
+ export class UIColorInput extends UIFormElement {
52
+ /** Form field name. */
53
+ name: string;
54
+ /** Current color string in the active `format`. */
55
+ value: string;
56
+ /** Output format — drives the shape of `value`. Detail always carries both `hex` and `oklch`. */
57
+ format: ColorInputFormat;
58
+ /** Disables the trigger button + suppresses the popover. */
59
+ disabled: boolean;
60
+ /** Popover placement relative to the trigger button. */
61
+ placement: 'top' | 'bottom' | 'left' | 'right' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end';
62
+ /** Reflects the inner popover's open state. Set programmatically to open/close without a click. */
63
+ open: boolean;
64
+ /** Generation constraint forwarded to inner picker: clamp OKLCH chroma to at most this value. `Infinity` = unconstrained. v0.5.13 §-TBD (FB-33 §1). */
65
+ maxChroma: number;
66
+ /** Generation constraint forwarded to inner picker: clamp OKLCH lightness to at most this value (0..1). `1` = unconstrained. */
67
+ maxL: number;
68
+ /** Generation constraint forwarded to inner picker: clamp OKLCH lightness to at least this value (0..1). `0` = unconstrained. */
69
+ minL: number;
70
+ /** Generation constraint forwarded to inner picker: maximum allowed signed-shortest-path hue drift in degrees from `baseHue`. `NaN` = unconstrained. */
71
+ hueDriftMax: number;
72
+ /** Reference hue (degrees) for `hueDriftMax`, forwarded to inner picker. `NaN` = picker falls back to its hue at first commit. */
73
+ baseHue: number;
74
+
75
+ /** Open the picker programmatically. */
76
+ showPicker(): void;
77
+ /** Close the picker programmatically. */
78
+ hidePicker(): void;
79
+
80
+ addEventListener<K extends keyof HTMLElementEventMap>(
81
+ type: K,
82
+ listener: (this: UIColorInput, ev: HTMLElementEventMap[K]) => unknown,
83
+ options?: boolean | AddEventListenerOptions,
84
+ ): void;
85
+ addEventListener(type: 'change', listener: (ev: ColorInputChangeEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
86
+ addEventListener(type: 'input', listener: (ev: ColorInputInputEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
87
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `<color-input-ui>` — auto-registers the tag on import.
3
+ *
4
+ * For non-side-effect class import (test isolation, tag override), use
5
+ * the `class` subpath:
6
+ *
7
+ * import { UIColorInput } from '@adia-ai/web-components/components/color-input/class';
8
+ *
9
+ * Per ADR-0027, this module does NOT auto-import the composed primitives
10
+ * (`<popover-ui>`, `<button-ui>`, `<color-picker-ui>`). Consumers must
11
+ * register them explicitly before mounting `<color-input-ui>`:
12
+ *
13
+ * import '@adia-ai/web-components/components/popover';
14
+ * import '@adia-ai/web-components/components/button';
15
+ * import '@adia-ai/web-components/components/color-picker';
16
+ * import '@adia-ai/web-components/components/color-input';
17
+ *
18
+ * @see ../../USAGE.md#registration--auto-vs-explicit
19
+ */
20
+
21
+ import { defineIfFree } from '../../core/register.js';
22
+ import { UIColorInput } from './class.js';
23
+
24
+ defineIfFree('color-input-ui', UIColorInput);
25
+
26
+ export { UIColorInput };
@@ -0,0 +1,188 @@
1
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
2
+ name: UIColorInput
3
+ tag: color-input-ui
4
+ component: ColorInput
5
+ category: input
6
+ version: 1
7
+ description: |-
8
+ Compact form-bearing color input — opens a popover-anchored
9
+ `<color-picker-ui>` from an inline swatch button. §302 (v0.5.12,
10
+ FEEDBACK-29 re-bucket from v0.6.0). Canonicalizes the USAGE.md §221f
11
+ Option B recipe (popover + button + color-picker) into a single
12
+ form-associated tag for inline form contexts (settings drawer "source
13
+ color" field, swatch-row inline-edit, etc.).
14
+ # Per ADR-0027 — primitives that programmatically create other primitives
15
+ # do NOT auto-import them. The consumer (or demo shell) explicitly imports
16
+ # the composed primitives. This element creates `<button-ui>` +
17
+ # `<popover-ui>` + `<color-picker-ui>` light-DOM children but assumes the
18
+ # consumer has already imported their auto-register modules.
19
+ composes:
20
+ - button-ui
21
+ - popover-ui
22
+ - color-picker-ui
23
+ props:
24
+ name:
25
+ description: Form field name.
26
+ type: string
27
+ default: ""
28
+ value:
29
+ description: Current color as a string in the active [format].
30
+ type: string
31
+ default: "#3b82f6"
32
+ format:
33
+ description: Output format for the value property + event detail. Hex (`#rrggbb`) or OKLCH (`oklch(L C H)`).
34
+ type: string
35
+ default: hex
36
+ reflect: true
37
+ enum:
38
+ - hex
39
+ - oklch
40
+ disabled:
41
+ description: Disables the trigger button + suppresses popover.
42
+ type: boolean
43
+ default: false
44
+ reflect: true
45
+ placement:
46
+ description: Popover placement relative to the trigger.
47
+ type: string
48
+ default: bottom-start
49
+ reflect: true
50
+ enum:
51
+ - top
52
+ - bottom
53
+ - left
54
+ - right
55
+ - top-start
56
+ - top-end
57
+ - bottom-start
58
+ - bottom-end
59
+ open:
60
+ description: Reflects the popover's open state. Set programmatically to open/close the picker without a click.
61
+ type: boolean
62
+ default: false
63
+ reflect: true
64
+ maxChroma:
65
+ description: |
66
+ Generation constraint forwarded to the inner `<color-picker-ui>` (v0.5.13
67
+ §-TBD, FB-33 §1). Clamp the OKLCH chroma channel to at most this value.
68
+ Default Infinity (no constraint).
69
+ type: number
70
+ default: Infinity
71
+ reflect: true
72
+ maxL:
73
+ description: Generation constraint forwarded to inner picker — clamp OKLCH lightness to at most this value (0..1). Default 1.
74
+ type: number
75
+ default: 1
76
+ reflect: true
77
+ minL:
78
+ description: Generation constraint forwarded to inner picker — clamp OKLCH lightness to at least this value (0..1). Default 0.
79
+ type: number
80
+ default: 0
81
+ reflect: true
82
+ hueDriftMax:
83
+ description: Generation constraint forwarded to inner picker — maximum allowed signed-shortest-path hue deviation (degrees) from baseHue. Default NaN (no constraint).
84
+ type: number
85
+ default: NaN
86
+ reflect: true
87
+ baseHue:
88
+ description: Reference hue (degrees) for the hueDriftMax constraint, forwarded to inner picker. Default NaN — picker falls back to its hue at first commit.
89
+ type: number
90
+ default: NaN
91
+ reflect: true
92
+ events:
93
+ change:
94
+ description: Fired when the color is committed (pointerup / Enter / picker close). Bubbles from inner picker.
95
+ detail:
96
+ value:
97
+ type: string
98
+ description: Color in the active [format] (hex or oklch).
99
+ hex:
100
+ type: string
101
+ description: Hex form (`#rrggbb`).
102
+ oklch:
103
+ type: string
104
+ description: OKLCH string (`oklch(L C H)`).
105
+ l:
106
+ type: number
107
+ description: Parsed OKLCH lightness scalar (0..1). v0.5.13 §-TBD (FB-33 §1-adjacent).
108
+ c:
109
+ type: number
110
+ description: Parsed OKLCH chroma scalar.
111
+ h:
112
+ type: number
113
+ description: Parsed OKLCH hue scalar (degrees, NaN ok for achromatic).
114
+ input:
115
+ description: Fired during continuous drag of the inner picker. Bubbles from inner picker.
116
+ detail:
117
+ value:
118
+ type: string
119
+ description: Color in the active [format].
120
+ hex:
121
+ type: string
122
+ oklch:
123
+ type: string
124
+ l:
125
+ type: number
126
+ c:
127
+ type: number
128
+ h:
129
+ type: number
130
+ slots: {}
131
+ states:
132
+ - name: idle
133
+ description: Default, ready for interaction.
134
+ - name: open
135
+ description: Popover is open; picker visible.
136
+ attribute: open
137
+ - name: disabled
138
+ description: Non-interactive; dimmed.
139
+ attribute: disabled
140
+ traits: []
141
+ tokens:
142
+ --color-input-swatch-size:
143
+ description: Diameter (em) of the inline swatch circle inside the trigger button. Default 1em.
144
+ a2ui:
145
+ rules: []
146
+ anti_patterns: []
147
+ examples:
148
+ - name: color-input-basic
149
+ description: Inline compact color input — popover-anchored picker on click.
150
+ a2ui: >-
151
+ [
152
+ {
153
+ "id": "root",
154
+ "component": "ColorInput",
155
+ "name": "brand",
156
+ "value": "#3b82f6"
157
+ }
158
+ ]
159
+ - name: color-input-oklch
160
+ description: Compact picker exposing OKLCH value form.
161
+ a2ui: >-
162
+ [
163
+ {
164
+ "id": "root",
165
+ "component": "ColorInput",
166
+ "name": "accent",
167
+ "format": "oklch",
168
+ "value": "oklch(0.7 0.15 220)"
169
+ }
170
+ ]
171
+ keywords:
172
+ - color-input
173
+ - color
174
+ - input
175
+ - picker
176
+ - swatch
177
+ - inline
178
+ - compact
179
+ synonyms:
180
+ color-field:
181
+ - color-input
182
+ inline-color-picker:
183
+ - color-input
184
+ related:
185
+ - color-picker
186
+ - popover
187
+ - button
188
+ - swatch
@@ -47,6 +47,7 @@ export { UIBadge } from './badge/badge.js';
47
47
  export { UIBreadcrumb } from './breadcrumb/breadcrumb.js';
48
48
  export { UICommand } from './command/command.js';
49
49
  export { UIColorPicker } from './color-picker/color-picker.js';
50
+ export { UIColorInput } from './color-input/color-input.js';
50
51
  export { UINoodles } from './noodles/noodles.js';
51
52
  export { UITable } from './table/table.js';
52
53
  export { UITableToolbar } from './table-toolbar/table-toolbar.js';
@@ -0,0 +1,38 @@
1
+ /**
2
+ * `<stat-ui>` — Metric/KPI display. Value + label + optional change indicator and trend.
3
+ *
4
+ * @see https://ui-kit.exe.xyz/site/components/stat
5
+ *
6
+ * HAND-AUTHORED (v0.5.12 §253 FB-30 close-out). The codegen pipeline's
7
+ * `isCssOnly()` check at `scripts/build/dts-codegen.mjs:186-194` looks for
8
+ * `${dirname}.js` — but stat ships `stat-ui.js` (irregular filename; the
9
+ * v0.6.0 §303 rename will normalize to `stat.js`). Pre-§303 the codegen
10
+ * false-positives `isCssOnly` and skips this directory entirely. Hand-author
11
+ * this file matching `stat.yaml` props until §303 lands.
12
+ *
13
+ * Filename mirrors the JS file (`stat-ui.js` → `stat-ui.d.ts`) so TypeScript
14
+ * resolves the side-effect import `import '@adia-ai/web-components/components/stat/stat-ui.js'`
15
+ * to a typed module. Post-§303, both files rename to `stat.{js,d.ts}` and
16
+ * codegen takes over.
17
+ */
18
+
19
+ import { UIElement } from '../../core/element.js';
20
+
21
+ export class UIStat extends UIElement {
22
+ /** Eyebrow label describing the metric. */
23
+ label: string;
24
+ /** Primary metric value (large display number / string). */
25
+ value: string;
26
+ /** Change indicator text (e.g. "+12%", "-3%"). Slotted as a badge next to value. */
27
+ change: string;
28
+ /**
29
+ * Trend direction or narrative subtitle. Canonical values color the change
30
+ * badge: `up` = success (green), `down` = danger (red), `flat`/`neutral` =
31
+ * muted. Any other string renders as caption-style text under the primary
32
+ * value (not enum-validated; yaml documents the canonical set but the type
33
+ * stays `string` for narrative subtitles).
34
+ */
35
+ trend: string;
36
+ /** Icon name displayed in the icon slot (Phosphor icon registry). */
37
+ icon: string;
38
+ }
@@ -167,6 +167,14 @@ export class UISwatch extends UIElement {
167
167
  selectable: { type: Boolean, default: false, reflect: true },
168
168
  selected: { type: Boolean, default: false, reflect: true },
169
169
  autoContrast: { type: Boolean, default: false, reflect: true },
170
+ // §253 (v0.5.12, FB-23 §1): label position relative to the tile.
171
+ // `below` (default) = current shape="block" stacked layout.
172
+ // `overlay` = label sits ON the tile via absolute positioning;
173
+ // pair with `[auto-contrast]` for OKLab-L-driven legibility.
174
+ // Only meaningful when shape="block"; other shapes ignore this prop.
175
+ // CSS-only behavior; no JS runtime delta beyond yaml-declared
176
+ // reflective attribute. Unblocks Tokens Studio C1.3 dogfood.
177
+ labelPosition: { type: String, default: 'below', reflect: true, attribute: 'label-position' },
170
178
  };
171
179
 
172
180
  static template = () => null;
@@ -204,8 +212,20 @@ export class UISwatch extends UIElement {
204
212
 
205
213
  #stamp() {
206
214
  if (this.#stamped) return;
215
+
216
+ // v0.5.13 §-TBD (FB-34 §1) — `[slot="chrome"]` children are sibling-of-tile
217
+ // chrome (gamut badges, override/tracked dots, custom indicators) that must
218
+ // survive stamping as direct host siblings, NOT funnel into #labelEl where
219
+ // absolute-positioning anchors against the label text bbox. Pull them out
220
+ // FIRST so the innerHTML='' wipe below doesn't lose them.
221
+ const chromeSlot = Array.from(this.children).filter(
222
+ n => n.getAttribute && n.getAttribute('slot') === 'chrome'
223
+ );
224
+ for (const n of chromeSlot) n.remove();
225
+
207
226
  // Capture pre-existing default-slot content so consumer-authored
208
227
  // children (e.g. <swatch-ui>Forecast</swatch-ui>) survive stamping.
228
+ // `[slot="chrome"]` children are already removed above, so won't appear here.
209
229
  const slotted = Array.from(this.childNodes).filter(n =>
210
230
  !(n.nodeType === 1 && n.dataset && (
211
231
  n.dataset.tile !== undefined ||
@@ -222,6 +242,14 @@ export class UISwatch extends UIElement {
222
242
  this.#tileEl.setAttribute('aria-hidden', 'true');
223
243
  this.appendChild(this.#tileEl);
224
244
 
245
+ // Chrome-slot children re-attach IMMEDIATELY after the tile so they can
246
+ // position absolutely against the host (host carries position: relative
247
+ // when shape="block" + label-position="overlay" per swatch.css). Keeping
248
+ // them sibling-of-tile (not descendant-of-tile) lets consumer CSS use
249
+ // `position: absolute; top: 2px; right: 2px` and anchor against the host's
250
+ // padding-box, which matches the tile's geometry in the block layout.
251
+ for (const n of chromeSlot) this.appendChild(n);
252
+
225
253
  // Badge container — holds one or more <span data-badge-variant="..."> children.
226
254
  // Multi-badge support added in v0.4.9 §92 (FEEDBACK-04 follow-up).
227
255
  this.#badgeEl = document.createElement('span');