@agent-ui-kit/components 0.0.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/dist/base-styles.css +8 -0
- package/dist/component-styles.css +147 -0
- package/dist/controls/_base/index.d.ts +3 -0
- package/dist/controls/_base/index.js +9 -0
- package/dist/controls/_base/indicator-element.d.ts +132 -0
- package/dist/controls/_base/indicator-element.js +158 -0
- package/dist/controls/_base/listbox-element.d.ts +70 -0
- package/dist/controls/_base/listbox-element.js +74 -0
- package/dist/controls/_base/range-element.d.ts +120 -0
- package/dist/controls/_base/range-element.js +158 -0
- package/dist/controls/_surface/container-box.css +301 -0
- package/dist/controls/_surface/container.css +156 -0
- package/dist/controls/_token-surface/token-surface.d.ts +35 -0
- package/dist/controls/_token-surface/token-surface.js +97 -0
- package/dist/controls/attachment/attachment-meta.d.ts +8 -0
- package/dist/controls/attachment/attachment-meta.js +84 -0
- package/dist/controls/attachment/attachment.css +91 -0
- package/dist/controls/attachment/attachment.d.ts +40 -0
- package/dist/controls/attachment/attachment.js +70 -0
- package/dist/controls/avatar/avatar-initials.d.ts +4 -0
- package/dist/controls/avatar/avatar-initials.js +30 -0
- package/dist/controls/avatar/avatar.css +87 -0
- package/dist/controls/avatar/avatar.d.ts +40 -0
- package/dist/controls/avatar/avatar.js +77 -0
- package/dist/controls/badge/badge.css +175 -0
- package/dist/controls/badge/badge.d.ts +36 -0
- package/dist/controls/badge/badge.js +71 -0
- package/dist/controls/bar-chart/bar-chart.css +110 -0
- package/dist/controls/bar-chart/bar-chart.d.ts +26 -0
- package/dist/controls/bar-chart/bar-chart.js +80 -0
- package/dist/controls/bar-chart/bar-math.d.ts +31 -0
- package/dist/controls/bar-chart/bar-math.js +106 -0
- package/dist/controls/button/button.css +263 -0
- package/dist/controls/button/button.d.ts +61 -0
- package/dist/controls/button/button.js +115 -0
- package/dist/controls/calendar/calendar.css +495 -0
- package/dist/controls/calendar/calendar.d.ts +161 -0
- package/dist/controls/calendar/calendar.js +1004 -0
- package/dist/controls/card/card-content.d.ts +24 -0
- package/dist/controls/card/card-content.js +133 -0
- package/dist/controls/card/card-footer.d.ts +3 -0
- package/dist/controls/card/card-footer.js +16 -0
- package/dist/controls/card/card-header.d.ts +3 -0
- package/dist/controls/card/card-header.js +18 -0
- package/dist/controls/card/card.css +445 -0
- package/dist/controls/card/card.d.ts +51 -0
- package/dist/controls/card/card.js +61 -0
- package/dist/controls/checkbox/checkbox.css +204 -0
- package/dist/controls/checkbox/checkbox.d.ts +114 -0
- package/dist/controls/checkbox/checkbox.js +130 -0
- package/dist/controls/code/code.css +72 -0
- package/dist/controls/code/code.d.ts +23 -0
- package/dist/controls/code/code.js +40 -0
- package/dist/controls/color-picker/color-picker.css +209 -0
- package/dist/controls/color-picker/color-picker.d.ts +84 -0
- package/dist/controls/color-picker/color-picker.js +545 -0
- package/dist/controls/color-picker/color.d.ts +40 -0
- package/dist/controls/color-picker/color.js +159 -0
- package/dist/controls/column/column.css +151 -0
- package/dist/controls/column/column.d.ts +107 -0
- package/dist/controls/column/column.js +57 -0
- package/dist/controls/combo-box/combo-box.css +397 -0
- package/dist/controls/combo-box/combo-box.d.ts +133 -0
- package/dist/controls/combo-box/combo-box.js +680 -0
- package/dist/controls/command-modal/command-modal.css +226 -0
- package/dist/controls/command-modal/command-modal.d.ts +42 -0
- package/dist/controls/command-modal/command-modal.js +315 -0
- package/dist/controls/disclosure/disclosure.css +162 -0
- package/dist/controls/disclosure/disclosure.d.ts +37 -0
- package/dist/controls/disclosure/disclosure.js +207 -0
- package/dist/controls/field/field.css +140 -0
- package/dist/controls/field/field.d.ts +27 -0
- package/dist/controls/field/field.js +228 -0
- package/dist/controls/form-provider/form-provider.css +40 -0
- package/dist/controls/form-provider/form-provider.d.ts +42 -0
- package/dist/controls/form-provider/form-provider.js +105 -0
- package/dist/controls/grid/grid.css +72 -0
- package/dist/controls/grid/grid.d.ts +60 -0
- package/dist/controls/grid/grid.js +56 -0
- package/dist/controls/icon/icon.css +37 -0
- package/dist/controls/icon/icon.d.ts +25 -0
- package/dist/controls/icon/icon.js +55 -0
- package/dist/controls/index.d.ts +57 -0
- package/dist/controls/index.js +136 -0
- package/dist/controls/ladder/ladder.css +89 -0
- package/dist/controls/ladder/ladder.d.ts +27 -0
- package/dist/controls/ladder/ladder.js +78 -0
- package/dist/controls/list/list.css +118 -0
- package/dist/controls/list/list.d.ts +84 -0
- package/dist/controls/list/list.js +48 -0
- package/dist/controls/menu/menu.css +195 -0
- package/dist/controls/menu/menu.d.ts +44 -0
- package/dist/controls/menu/menu.js +248 -0
- package/dist/controls/modal/modal.css +99 -0
- package/dist/controls/modal/modal.d.ts +61 -0
- package/dist/controls/modal/modal.js +155 -0
- package/dist/controls/popover/popover.css +83 -0
- package/dist/controls/popover/popover.d.ts +44 -0
- package/dist/controls/popover/popover.js +164 -0
- package/dist/controls/progress/progress.css +106 -0
- package/dist/controls/progress/progress.d.ts +28 -0
- package/dist/controls/progress/progress.js +80 -0
- package/dist/controls/radio/radio-group.css +84 -0
- package/dist/controls/radio/radio-group.d.ts +152 -0
- package/dist/controls/radio/radio-group.js +350 -0
- package/dist/controls/radio/radio.css +219 -0
- package/dist/controls/radio/radio.d.ts +19 -0
- package/dist/controls/radio/radio.js +75 -0
- package/dist/controls/ramp/ramp.css +75 -0
- package/dist/controls/ramp/ramp.d.ts +29 -0
- package/dist/controls/ramp/ramp.js +72 -0
- package/dist/controls/row/row.css +152 -0
- package/dist/controls/row/row.d.ts +95 -0
- package/dist/controls/row/row.js +45 -0
- package/dist/controls/segment/segment.css +66 -0
- package/dist/controls/segment/segment.d.ts +3 -0
- package/dist/controls/segment/segment.js +26 -0
- package/dist/controls/segmented-control/segmented-control.css +189 -0
- package/dist/controls/segmented-control/segmented-control.d.ts +20 -0
- package/dist/controls/segmented-control/segmented-control.js +64 -0
- package/dist/controls/select/select.css +467 -0
- package/dist/controls/select/select.d.ts +147 -0
- package/dist/controls/select/select.js +538 -0
- package/dist/controls/slider/slider.css +231 -0
- package/dist/controls/slider/slider.d.ts +111 -0
- package/dist/controls/slider/slider.js +60 -0
- package/dist/controls/slider-multi/slider-multi.css +197 -0
- package/dist/controls/slider-multi/slider-multi.d.ts +149 -0
- package/dist/controls/slider-multi/slider-multi.js +380 -0
- package/dist/controls/sparkline/sparkline-math.d.ts +23 -0
- package/dist/controls/sparkline/sparkline-math.js +79 -0
- package/dist/controls/sparkline/sparkline.css +61 -0
- package/dist/controls/sparkline/sparkline.d.ts +27 -0
- package/dist/controls/sparkline/sparkline.js +74 -0
- package/dist/controls/split/constrain.d.ts +44 -0
- package/dist/controls/split/constrain.js +126 -0
- package/dist/controls/split/split-pane.css +63 -0
- package/dist/controls/split/split-pane.d.ts +49 -0
- package/dist/controls/split/split-pane.js +42 -0
- package/dist/controls/split/split.css +127 -0
- package/dist/controls/split/split.d.ts +42 -0
- package/dist/controls/split/split.js +364 -0
- package/dist/controls/stat/stat-model.d.ts +22 -0
- package/dist/controls/stat/stat-model.js +69 -0
- package/dist/controls/stat/stat.css +101 -0
- package/dist/controls/stat/stat.d.ts +30 -0
- package/dist/controls/stat/stat.js +73 -0
- package/dist/controls/status-stream/status-stream.css +151 -0
- package/dist/controls/status-stream/status-stream.d.ts +108 -0
- package/dist/controls/status-stream/status-stream.js +417 -0
- package/dist/controls/swatch/swatch.css +66 -0
- package/dist/controls/swatch/swatch.d.ts +28 -0
- package/dist/controls/swatch/swatch.js +74 -0
- package/dist/controls/swiper/swiper-item.css +17 -0
- package/dist/controls/swiper/swiper-item.d.ts +31 -0
- package/dist/controls/swiper/swiper-item.js +47 -0
- package/dist/controls/swiper/swiper-label.css +10 -0
- package/dist/controls/swiper/swiper-label.d.ts +4 -0
- package/dist/controls/swiper/swiper-label.js +20 -0
- package/dist/controls/swiper/swiper-paddles.css +26 -0
- package/dist/controls/swiper/swiper-paddles.d.ts +10 -0
- package/dist/controls/swiper/swiper-paddles.js +62 -0
- package/dist/controls/swiper/swiper-pagination.css +60 -0
- package/dist/controls/swiper/swiper-pagination.d.ts +31 -0
- package/dist/controls/swiper/swiper-pagination.js +78 -0
- package/dist/controls/swiper/swiper.css +153 -0
- package/dist/controls/swiper/swiper.d.ts +158 -0
- package/dist/controls/swiper/swiper.js +659 -0
- package/dist/controls/switch/switch.css +194 -0
- package/dist/controls/switch/switch.d.ts +5 -0
- package/dist/controls/switch/switch.js +18 -0
- package/dist/controls/table/table-model.d.ts +43 -0
- package/dist/controls/table/table-model.js +124 -0
- package/dist/controls/table/table.css +90 -0
- package/dist/controls/table/table.d.ts +29 -0
- package/dist/controls/table/table.js +178 -0
- package/dist/controls/tabs/tab-panel.d.ts +10 -0
- package/dist/controls/tabs/tab-panel.js +89 -0
- package/dist/controls/tabs/tab.d.ts +26 -0
- package/dist/controls/tabs/tab.js +67 -0
- package/dist/controls/tabs/tabs.css +181 -0
- package/dist/controls/tabs/tabs.d.ts +61 -0
- package/dist/controls/tabs/tabs.js +176 -0
- package/dist/controls/text/href.d.ts +26 -0
- package/dist/controls/text/href.js +48 -0
- package/dist/controls/text/text.css +328 -0
- package/dist/controls/text/text.d.ts +85 -0
- package/dist/controls/text/text.js +273 -0
- package/dist/controls/text-field/text-field.css +498 -0
- package/dist/controls/text-field/text-field.d.ts +226 -0
- package/dist/controls/text-field/text-field.js +905 -0
- package/dist/controls/textarea/textarea.css +251 -0
- package/dist/controls/textarea/textarea.d.ts +136 -0
- package/dist/controls/textarea/textarea.js +312 -0
- package/dist/controls/theme-provider/theme-provider.css +48 -0
- package/dist/controls/theme-provider/theme-provider.d.ts +59 -0
- package/dist/controls/theme-provider/theme-provider.js +40 -0
- package/dist/controls/timeline/timeline.css +33 -0
- package/dist/controls/timeline/timeline.d.ts +39 -0
- package/dist/controls/timeline/timeline.js +52 -0
- package/dist/controls/timeline-item/timeline-item.css +495 -0
- package/dist/controls/timeline-item/timeline-item.d.ts +79 -0
- package/dist/controls/timeline-item/timeline-item.js +360 -0
- package/dist/controls/toast/toast-region.css +56 -0
- package/dist/controls/toast/toast-region.d.ts +20 -0
- package/dist/controls/toast/toast-region.js +113 -0
- package/dist/controls/toast/toast.css +96 -0
- package/dist/controls/toast/toast.d.ts +28 -0
- package/dist/controls/toast/toast.js +191 -0
- package/dist/controls/toolbar/toolbar.css +125 -0
- package/dist/controls/toolbar/toolbar.d.ts +108 -0
- package/dist/controls/toolbar/toolbar.js +68 -0
- package/dist/controls/tooltip/tooltip.css +80 -0
- package/dist/controls/tooltip/tooltip.d.ts +57 -0
- package/dist/controls/tooltip/tooltip.js +205 -0
- package/dist/descriptor/component-descriptor.d.ts +112 -0
- package/dist/descriptor/component-descriptor.js +492 -0
- package/dist/descriptor/index.d.ts +1 -0
- package/dist/descriptor/index.js +5 -0
- package/dist/dom/container.d.ts +45 -0
- package/dist/dom/container.js +60 -0
- package/dist/dom/element.d.ts +98 -0
- package/dist/dom/element.js +221 -0
- package/dist/dom/form.d.ts +240 -0
- package/dist/dom/form.js +399 -0
- package/dist/dom/index.d.ts +10 -0
- package/dist/dom/index.js +31 -0
- package/dist/dom/props.d.ts +56 -0
- package/dist/dom/props.js +231 -0
- package/dist/dom/repeat.d.ts +10 -0
- package/dist/dom/repeat.js +202 -0
- package/dist/dom/template.d.ts +161 -0
- package/dist/dom/template.js +634 -0
- package/dist/dom/watch.d.ts +11 -0
- package/dist/dom/watch.js +82 -0
- package/dist/foundation-styles.css +9 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/reactive/graph.d.ts +65 -0
- package/dist/reactive/graph.js +310 -0
- package/dist/reactive/index.d.ts +3 -0
- package/dist/reactive/index.js +4 -0
- package/dist/reactive/scheduler.d.ts +13 -0
- package/dist/reactive/scheduler.js +49 -0
- package/dist/traits/area-drag.d.ts +12 -0
- package/dist/traits/area-drag.js +70 -0
- package/dist/traits/form-registry.d.ts +36 -0
- package/dist/traits/form-registry.js +108 -0
- package/dist/traits/index.d.ts +24 -0
- package/dist/traits/index.js +19 -0
- package/dist/traits/overlay.d.ts +50 -0
- package/dist/traits/overlay.js +277 -0
- package/dist/traits/pane-resize.d.ts +26 -0
- package/dist/traits/pane-resize.js +103 -0
- package/dist/traits/press-activation.d.ts +11 -0
- package/dist/traits/press-activation.js +55 -0
- package/dist/traits/roving-focus.d.ts +52 -0
- package/dist/traits/roving-focus.js +214 -0
- package/dist/traits/scroll-fade.d.ts +28 -0
- package/dist/traits/scroll-fade.js +120 -0
- package/dist/traits/selection-commit.d.ts +30 -0
- package/dist/traits/selection-commit.js +146 -0
- package/dist/traits/tabbable.d.ts +12 -0
- package/dist/traits/tabbable.js +59 -0
- package/dist/traits/track-user-invalid.d.ts +36 -0
- package/dist/traits/track-user-invalid.js +52 -0
- package/dist/traits/value-codec.d.ts +120 -0
- package/dist/traits/value-codec.js +497 -0
- package/dist/traits/value-drag.d.ts +18 -0
- package/dist/traits/value-drag.js +84 -0
- package/package.json +263 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/* select.css — ui-select, the single-file component stylesheet (ADR-0003).
|
|
2
|
+
* Two clearly-SECTIONED blocks, mirroring button.css / text-field.css / popover.css:
|
|
3
|
+
*
|
|
4
|
+
* [1] TOKEN BLOCK — `:where(ui-select)` (specificity 0,0,0) DECLARES the `--ui-select-*`
|
|
5
|
+
* chain from the colour roles (`--md-sys-color-{family}-{role}`; family = neutral for the trigger frame)
|
|
6
|
+
* and the dimensional ramp (`--md-sys-height-{size}` / `--md-sys-font-{size}` / `--md-sys-icon-{size}`).
|
|
7
|
+
* The trigger is a Control-class surface carrying Kim's filled/container state law (TKT-0062,
|
|
8
|
+
* superseding the ADR-0014 cl.2c border-only channel) — background/border/text-ink ALL repoint
|
|
9
|
+
* per state (default/filled/hover/focus/disabled; "filled" = a real selection, via a NEW
|
|
10
|
+
* `[data-empty]` toggle select.ts's label effect now maintains); a caret = font inline
|
|
11
|
+
* affordance, §4.1. The listbox
|
|
12
|
+
* panel is a Container/surface (bg + outline + radius + padding — NOT a control height). The
|
|
13
|
+
* option rows use the legacy item-pad model (§5.1 / ROV-C5: padding-based row height, NOT
|
|
14
|
+
* the --md-sys-compact widget box — option rows are text items, not stand-alone widgets) — but
|
|
15
|
+
* (2026-07-06) the item-pad's own NUMBERS are now DERIVED off the trigger ramp
|
|
16
|
+
* (`--ui-select-height`/`--ui-select-font`), superseding the earlier fixed-px item-pad
|
|
17
|
+
* (6px/12px): panel inset + option padding now scale with `[size]` the same as the trigger,
|
|
18
|
+
* and option text aligns under the trigger label (listbox-padding + option-inline == h/2).
|
|
19
|
+
* `[size]` repoints the geometry (trigger AND, transitively via the shared height/font
|
|
20
|
+
* tokens, the panel/options — no separate [size] block needed for the panel).
|
|
21
|
+
* [2] STYLES BLOCK — `@scope (ui-select)` CONSUMES only `--ui-select-*` for its OWN tokens.
|
|
22
|
+
* The host is `display: inline-block` with a min-inline-size floor (the ADR-0021 lesson:
|
|
23
|
+
* a control that can be empty needs a floor; the trigger inherits full width from the host).
|
|
24
|
+
* The trigger is `display: inline-grid; grid-template-columns: 1fr auto` — always a
|
|
25
|
+
* [label | caret] anatomy (the trailing caret slot is always present, so the grid is fixed
|
|
26
|
+
* at two columns). The caret span is an icon-sized cell (the CELL stays icon-sized for both
|
|
27
|
+
* icon and caret roles; only the GLYPH inside differs — padding: (icon−glyph)/2 centers the
|
|
28
|
+
* font-sized glyph in the icon cell, the §4.1 caret centering law). The listbox panel uses
|
|
29
|
+
* the overlay controller's position:fixed + inset (we do NOT fix position here). Option rows
|
|
30
|
+
* get hover/selected/focus states; focus ring on :focus-visible (real DOM focus via rovingFocus).
|
|
31
|
+
* Forced-colors keeps the trigger frame, label, and panel surface visible as system colours.
|
|
32
|
+
*
|
|
33
|
+
* Standalone stylesheet — NOT injected from select.ts (behaviour-only `.ts`, no runtime injection
|
|
34
|
+
* per plan §2). Linked via the component-styles barrel (s12); the barrel @imports foundation roles
|
|
35
|
+
* FIRST. The listbox's `popover` attribute (set by the overlay controller) gives it `display: none`
|
|
36
|
+
* in the UA stylesheet until `showPopover()` places it in the top layer.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/* ════════════════ [1] TOKEN BLOCK — :where(ui-select) declares --ui-select-* ════════════════ */
|
|
40
|
+
|
|
41
|
+
:where(ui-select) {
|
|
42
|
+
/* ── Trigger frame — Kim's filled/container state law (TKT-0062), SUPERSEDING the ADR-0014
|
|
43
|
+
cl.2c border-only channel: bg/border/ink ALL repoint per state now (default/filled/hover/
|
|
44
|
+
focus/disabled — same table as text-field/textarea/combo-box). "filled" = the trigger shows a
|
|
45
|
+
real selection, not the placeholder (`[data-empty]`, a NEW toggle this ticket added to
|
|
46
|
+
select.ts's own label effect — select previously had no emptiness hook at all, unlike its
|
|
47
|
+
siblings). Focus = transparent: the shared outline ring is the SOLE focus indicator (ADR-0014
|
|
48
|
+
dev#1; a border-focus step doubles with the ring). */
|
|
49
|
+
--ui-select-bg: var(--md-sys-color-neutral-container-low); /* default */
|
|
50
|
+
--ui-select-bg-filled: var(--md-sys-color-neutral-container);
|
|
51
|
+
--ui-select-bg-hover: var(--md-sys-color-neutral-container);
|
|
52
|
+
--ui-select-bg-focus: var(--md-sys-color-neutral-container-low);
|
|
53
|
+
--ui-select-border: transparent; /* default */
|
|
54
|
+
--ui-select-border-hover: var(--md-sys-color-neutral-outline-variant); /* the ONE visible-border state */
|
|
55
|
+
--ui-select-border-focus: transparent; /* transparent holds the geometry — no layout shift */
|
|
56
|
+
/* user-invalid (ADR-0051) — the danger border, appearing only AFTER the first interaction (the
|
|
57
|
+
trackUserInvalid controller owns that timing; select.ts toggles :state(user-invalid)). The
|
|
58
|
+
text-field precedent, on the trigger's border channel — UNCHANGED, orthogonal to the table above. */
|
|
59
|
+
--ui-select-border-invalid: var(--md-sys-color-danger);
|
|
60
|
+
--ui-select-border-invalid-hover: var(--md-sys-color-danger-high);
|
|
61
|
+
--ui-select-ink: var(--md-sys-color-neutral); /* default — showing the placeholder */
|
|
62
|
+
--ui-select-ink-filled: var(--md-sys-color-neutral-on-surface-variant);
|
|
63
|
+
--ui-select-ink-hover: var(--md-sys-color-neutral-on-surface-variant);
|
|
64
|
+
--ui-select-ink-focus: var(--md-sys-color-neutral-on-surface);
|
|
65
|
+
--ui-select-radius: var(--md-sys-shape-corner-base); /* fixed rounded-rect radius, like text-field */
|
|
66
|
+
|
|
67
|
+
/* ── Trigger geometry (Control class, default size = md) — off the §1-row ramp (ADR-0038).
|
|
68
|
+
height/font/icon from the explicit per-[scale] table; gap = font/2 × density (on `*`).
|
|
69
|
+
The caret (inline affordance) follows the §4.1 law: glyph size = font (RHYTHM family,
|
|
70
|
+
not frame) → glyph is font-sized, centred in the icon cell by padding=(icon−glyph)/2. */
|
|
71
|
+
--ui-select-height: var(--md-sys-height-md);
|
|
72
|
+
--ui-select-font: var(--md-sys-font-md);
|
|
73
|
+
--ui-select-gap: var(--md-sys-gap-md); /* the density-bearing column-gap */
|
|
74
|
+
--ui-select-icon: var(--md-sys-icon-md); /* the icon-cell size (caret CELL is icon-wide) */
|
|
75
|
+
--ui-select-glyph: var(--ui-select-font); /* caret GLYPH = font (§4.1 caret law) */
|
|
76
|
+
|
|
77
|
+
/* ── Sizing floor (ADR-0021 lesson) — a select with no selection or an empty placeholder
|
|
78
|
+
needs a floor so it is hittable and the overlay positions against a real anchor rect. */
|
|
79
|
+
--ui-select-min-inline-size: 10ch;
|
|
80
|
+
|
|
81
|
+
/* ── Disabled state — TKT-0062's disabled row (bg=container-low, border=transparent, ink=neutral-low) */
|
|
82
|
+
--ui-select-border-disabled: transparent;
|
|
83
|
+
--ui-select-bg-disabled: var(--md-sys-color-neutral-container-low);
|
|
84
|
+
--ui-select-ink-disabled: var(--md-sys-color-neutral-low);
|
|
85
|
+
|
|
86
|
+
/* ── Listbox panel — Container/surface geometry (NOT a control height; like modal/card/popover) */
|
|
87
|
+
--ui-select-listbox-bg: var(--md-sys-color-neutral-surface);
|
|
88
|
+
--ui-select-listbox-ink: var(--md-sys-color-neutral-on-surface);
|
|
89
|
+
--ui-select-listbox-outline: var(--md-sys-color-neutral-outline-variant);
|
|
90
|
+
--ui-select-listbox-radius: var(--md-sys-shape-corner-base);
|
|
91
|
+
/* ── The size-carrying derivation (supersedes the fixed "legacy item-pad" px, 2026-07-06) ──────
|
|
92
|
+
ROV-C5/ADR-0043 cl.5 keep options OUT of the h/2 "comfortable control" model and the
|
|
93
|
+
`--md-sys-compact` widget box — that boundary is unchanged. What changes: the item-pad's OWN
|
|
94
|
+
numbers are now DERIVED off the trigger's `--ui-select-height`/`--ui-select-font` (the same
|
|
95
|
+
ramp the trigger already repoints at `[size=sm|lg]`) instead of hand-picked fixed px, so the
|
|
96
|
+
panel inset + option row scale WITH the trigger — no new sm/lg blocks needed; the calc()s
|
|
97
|
+
below repoint for free.
|
|
98
|
+
• Alignment law: listbox-padding + option-inline == h/2, so option text starts at the same
|
|
99
|
+
inline offset from the panel's outer edge as the trigger label does from the trigger's
|
|
100
|
+
edge (h/2) — text lines up under text. Split evenly (h/4 + h/4).
|
|
101
|
+
• Row-height law: option-block == (h − font)/2 — the SAME (height−glyph)/2 centering law
|
|
102
|
+
(geometry.md), applied on the block axis to the option's own text (glyph = font here) —
|
|
103
|
+
so the option row's rendered content-box height == the trigger height exactly, and both
|
|
104
|
+
grow together under [size]. */
|
|
105
|
+
--ui-select-listbox-padding: calc(var(--ui-select-height) / 4); /* panel-edge inset + inter-row gap (feeds --ui-box-inset below) */
|
|
106
|
+
--ui-select-listbox-min-inline-size: 12ch; /* floor: at least as wide as a short option */
|
|
107
|
+
|
|
108
|
+
/* Panel max-block-size (TKT-0027) — public dial, default min(50vh, 12 real option rows): the
|
|
109
|
+
smaller of half the viewport or a scannable dozen rows, replacing the old flat 40vh magic
|
|
110
|
+
number. An option row's rendered height IS --ui-select-height exactly (the row-height law
|
|
111
|
+
above: (h−font)/2 padding × 2 + font == h, line-height:1 makes it exact) — no separate item-
|
|
112
|
+
block token needed, unlike ui-menu (which has no analogous control height to reuse). 12 rows
|
|
113
|
+
= 12 option boxes + 13 inset margins (leading + 11 collapsed inter-row + trailing — the
|
|
114
|
+
listbox is `[data-box]`, a BFC that collapses adjacent siblings' margins to ONE inset, never
|
|
115
|
+
doubled); --ui-select-listbox-padding IS the value fed to --ui-box-inset below, so this reads
|
|
116
|
+
the same source, not the DOM-positional --ui-box-inset itself. Rides [size]/[scale] naturally
|
|
117
|
+
(both terms derive from --ui-select-height); density-invariant, like the trigger height itself
|
|
118
|
+
(geometry.md: height is a frame quantity, not rhythm). + 2px — the panel's border-box:
|
|
119
|
+
max-block-size caps the BORDER box (box-sizing: border-box below), but the row math above
|
|
120
|
+
computed the CONTENT box (12 rows + 13 insets, no border term) — without this the 12th row
|
|
121
|
+
would overflow its own cap by exactly the 1px+1px border width (FIXED 2026-07-12, TKT-0027
|
|
122
|
+
browser-leg finding, same shape as ui-menu). */
|
|
123
|
+
--ui-select-listbox-max-block-size: min(50vh, calc(12 * var(--ui-select-height) + 13 * var(--ui-select-listbox-padding) + 2px));
|
|
124
|
+
|
|
125
|
+
/* ── Option rows — legacy item-pad (§5.1 / ROV-C5): padding-based row height, NOT a fixed
|
|
126
|
+
--md-sys-compact widget box. Options are text list items, not stand-alone widgets. The row
|
|
127
|
+
radius is inset from the listbox shell radius (nested-radius: shell − padding, ADR-0018). */
|
|
128
|
+
--ui-select-option-block: calc((var(--ui-select-height) - var(--ui-select-font)) / 2); /* (h−font)/2 — row height == trigger height */
|
|
129
|
+
--ui-select-option-inline: calc(var(--ui-select-height) / 4); /* h/4 — pairs with listbox-padding to total h/2 */
|
|
130
|
+
--ui-select-option-font: var(--ui-select-font); /* options were size-blind on FONT too (bug fix) — now the ramp */
|
|
131
|
+
--ui-select-option-radius: calc(var(--ui-select-listbox-radius) - var(--ui-select-listbox-padding)); /* nested-radius off the REAL inset (was a hardcoded 4px, ADR-0018) */
|
|
132
|
+
--ui-select-option-bg-hover: var(--md-sys-color-neutral-surface-high);
|
|
133
|
+
--ui-select-option-bg-selected: var(--md-sys-color-primary-container-low);
|
|
134
|
+
--ui-select-option-ink-selected: var(--md-sys-color-primary-high);
|
|
135
|
+
--ui-select-option-bg-focus: var(--md-sys-color-primary-container-low);
|
|
136
|
+
--ui-select-option-ink-focus: var(--md-sys-color-primary-high);
|
|
137
|
+
|
|
138
|
+
/* ── Group header (optgroup parity) — a non-interactive label above a [role=group]'s options.
|
|
139
|
+
Muted, small, slightly emphasised; the option rows below it keep the standard item-pad. */
|
|
140
|
+
--ui-select-group-label-ink: var(--md-sys-color-neutral-on-surface-variant);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* ── size → dimensional ramp repoint ────────────────────────────────────────────────────────── */
|
|
144
|
+
|
|
145
|
+
:where(ui-select[size='sm']) {
|
|
146
|
+
--ui-select-height: var(--md-sys-height-sm);
|
|
147
|
+
--ui-select-font: var(--md-sys-font-sm);
|
|
148
|
+
--ui-select-gap: var(--md-sys-gap-sm);
|
|
149
|
+
--ui-select-icon: var(--md-sys-icon-sm);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
:where(ui-select[size='lg']) {
|
|
153
|
+
--ui-select-height: var(--md-sys-height-lg);
|
|
154
|
+
--ui-select-font: var(--md-sys-font-lg);
|
|
155
|
+
--ui-select-gap: var(--md-sys-gap-lg);
|
|
156
|
+
--ui-select-icon: var(--md-sys-icon-lg);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* ── disabled → muted neutral roles ────────────────────────────────────────────────────────── */
|
|
160
|
+
/* TKT-0063: widened to `:is([disabled], :state(disabled))` — the own `[disabled]` attribute ALONE
|
|
161
|
+
missed the effective-disabled case (own || ancestor <fieldset disabled>/form-provider); select.ts's
|
|
162
|
+
effectiveDisabled() effect now also sets :state(disabled), mirroring text-field.ts's own precedent. */
|
|
163
|
+
:where(ui-select:is([disabled], :state(disabled))) {
|
|
164
|
+
--ui-select-border: var(--ui-select-border-disabled);
|
|
165
|
+
--ui-select-bg: var(--ui-select-bg-disabled);
|
|
166
|
+
--ui-select-ink: var(--ui-select-ink-disabled);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* ════════════════ [2] STYLES BLOCK — @scope (ui-select) consumes ONLY --ui-select-* ════════════════ */
|
|
170
|
+
|
|
171
|
+
@scope (ui-select) {
|
|
172
|
+
/* The host: an inline-block with a min-inline-size floor. The trigger child fills the host
|
|
173
|
+
width (width: 100%) so the select is as wide as its container or at least 10ch. */
|
|
174
|
+
:scope {
|
|
175
|
+
display: inline-block;
|
|
176
|
+
min-inline-size: var(--ui-select-min-inline-size);
|
|
177
|
+
vertical-align: middle; /* align with adjacent form controls (like a native <select>) */
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* The trigger button (Control class): a [label | caret] inline-grid. The trigger ALWAYS
|
|
181
|
+
has the caret trailing — unlike button.css which is presence-driven. So the grid is fixed
|
|
182
|
+
at two columns: 1fr (the label grows to fill) + auto (the caret icon cell). The label side
|
|
183
|
+
gets the h/2 start pad; the caret side gets the ½(h−icon) end pad (same as a trailing slot
|
|
184
|
+
in button.css — the icon-cell-edge law). block-size is the Control-class ramp height.
|
|
185
|
+
line-height: 1 (ADR-0036 single-line control standard — the glyph is centred by the frame). */
|
|
186
|
+
:scope > [data-part='trigger'] {
|
|
187
|
+
display: inline-grid;
|
|
188
|
+
grid-template-columns: 1fr auto;
|
|
189
|
+
column-gap: var(--ui-select-gap);
|
|
190
|
+
align-items: center;
|
|
191
|
+
box-sizing: border-box;
|
|
192
|
+
width: 100%; /* fill the host's inline-size */
|
|
193
|
+
|
|
194
|
+
block-size: var(--ui-select-height);
|
|
195
|
+
padding-block: 0; /* NEVER block padding — geometry law */
|
|
196
|
+
padding-inline-start: calc(var(--ui-select-height) / 2); /* h/2 label edge */
|
|
197
|
+
padding-inline-end: calc((var(--ui-select-height) - var(--ui-select-icon)) / 2); /* ½(h−icon) caret edge */
|
|
198
|
+
min-inline-size: 0; /* allow the trigger to shrink within a narrow host (the host sets the floor) */
|
|
199
|
+
|
|
200
|
+
font-size: var(--ui-select-font);
|
|
201
|
+
line-height: var(--md-sys-control-line-height); /* single-line control standard (ADR-0036) */
|
|
202
|
+
background: var(--ui-select-bg);
|
|
203
|
+
color: var(--ui-select-ink);
|
|
204
|
+
border: 1px solid var(--ui-select-border);
|
|
205
|
+
border-radius: var(--ui-select-radius);
|
|
206
|
+
cursor: pointer;
|
|
207
|
+
text-align: start;
|
|
208
|
+
-webkit-user-select: none;
|
|
209
|
+
user-select: none;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* The label span: fills the 1fr column; clipped for overflow. */
|
|
213
|
+
:scope > [data-part='trigger'] > [data-part='label'] {
|
|
214
|
+
overflow: hidden;
|
|
215
|
+
text-overflow: ellipsis;
|
|
216
|
+
white-space: nowrap;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* The aria-label span (ADR-0085): visually-hidden, always present (idempotent-parts precedent) —
|
|
220
|
+
referenced by the trigger's aria-labelledby only when `label` is set and the control is unfielded.
|
|
221
|
+
The standard CLIP technique, not `hidden`/`display:none` — some AT/engine combinations do not
|
|
222
|
+
reliably fold a display:none-referenced node's text into the accessible-name computation for an
|
|
223
|
+
aria-labelledby reference, while the clip approach is the robust cross-engine convention. */
|
|
224
|
+
:scope > [data-part='aria-label'] {
|
|
225
|
+
position: absolute;
|
|
226
|
+
inline-size: 1px;
|
|
227
|
+
block-size: 1px;
|
|
228
|
+
padding: 0;
|
|
229
|
+
margin: -1px;
|
|
230
|
+
overflow: hidden;
|
|
231
|
+
clip: rect(0, 0, 0, 0);
|
|
232
|
+
white-space: nowrap;
|
|
233
|
+
border: 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* The caret span: an icon-sized CELL (box-sizing border-box). The glyph inside is font-sized
|
|
237
|
+
and centred by padding = ½(icon − glyph), the §4.1 caret law (same as button.css [data-role='caret']).
|
|
238
|
+
aria-hidden is set in select.ts; the glyph is decorative. */
|
|
239
|
+
:scope > [data-part='trigger'] > [data-part='caret'] {
|
|
240
|
+
inline-size: var(--ui-select-icon);
|
|
241
|
+
block-size: var(--ui-select-icon);
|
|
242
|
+
box-sizing: border-box;
|
|
243
|
+
padding: calc((var(--ui-select-icon) - var(--ui-select-glyph)) / 2);
|
|
244
|
+
display: flex;
|
|
245
|
+
align-items: center;
|
|
246
|
+
justify-content: center;
|
|
247
|
+
font-size: var(--ui-select-glyph); /* the caret glyph = font (§4.1) */
|
|
248
|
+
line-height: 1;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* The injected Phosphor caret-down (setIcon, @agent-ui-kit/icons): sized explicitly to the glyph
|
|
252
|
+
rhythm — not the icon cell — so the §4.1 law holds independent of the padding math above
|
|
253
|
+
(the padding already insets to the same value; this is belt-and-suspenders, not reliance on
|
|
254
|
+
it). Overrides the svg's own width/height="100%" presentational attributes (resolve.ts). */
|
|
255
|
+
:scope > [data-part='trigger'] > [data-part='caret'] svg {
|
|
256
|
+
display: block;
|
|
257
|
+
inline-size: var(--ui-select-glyph);
|
|
258
|
+
block-size: var(--ui-select-glyph);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/* Trigger filled/container state law (TKT-0062, Kim's ruling — supersedes the ADR-0014 cl.2c
|
|
262
|
+
border-only channel) — repoints background/border-color/color together. "filled" = the trigger
|
|
263
|
+
shows a real selection (`[data-part='trigger']:not([data-empty])` — the NEW toggle select.ts's
|
|
264
|
+
label effect now maintains). Precedence is enforced by MUTUAL EXCLUSION (`:not()`), never
|
|
265
|
+
source-order/specificity (the lesson from text-field.css's own build: a disabled-guarded `:hover`
|
|
266
|
+
measured HIGHER specificity than an unguarded focus selector, so a mouse-click focus — which also
|
|
267
|
+
leaves the pointer hovering — kept the wrong state painted). `[disabled]` is checked directly on
|
|
268
|
+
the TRIGGER (select.ts already collapses own-disabled OR form-disabled onto the trigger's own
|
|
269
|
+
attribute — `trigger.setAttribute('disabled', ...)` in the effectiveDisabled() effect — so no
|
|
270
|
+
separate `:state(disabled)` check is needed here, unlike text-field/textarea/combo-box). */
|
|
271
|
+
:scope > [data-part='trigger']:not(:hover):not(:focus-visible):not([disabled]):not([data-empty]) {
|
|
272
|
+
background: var(--ui-select-bg-filled);
|
|
273
|
+
color: var(--ui-select-ink-filled);
|
|
274
|
+
}
|
|
275
|
+
:scope > [data-part='trigger']:not(:focus-visible):not([disabled]):hover {
|
|
276
|
+
background: var(--ui-select-bg-hover);
|
|
277
|
+
border-color: var(--ui-select-border-hover);
|
|
278
|
+
color: var(--ui-select-ink-hover);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
:scope > [data-part='trigger']:focus-within,
|
|
282
|
+
:scope > [data-part='trigger']:focus-visible {
|
|
283
|
+
outline: none; /* the outline ring below replaces the UA default */
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* Focus (keyboard-only :focus-visible, the button precedent — a trigger is an action control, not a
|
|
287
|
+
text-entry surface) wins over filled/hover unconditionally; a disabled trigger cannot be focused,
|
|
288
|
+
so no guard is needed here (matching the old law). */
|
|
289
|
+
:scope > [data-part='trigger']:focus-visible {
|
|
290
|
+
background: var(--ui-select-bg-focus);
|
|
291
|
+
border-color: var(--ui-select-border-focus);
|
|
292
|
+
color: var(--ui-select-ink-focus);
|
|
293
|
+
outline: var(--md-sys-state-focus-ring-width) solid var(--md-sys-color-focus-ring);
|
|
294
|
+
outline-offset: var(--md-sys-state-focus-ring-offset);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/* user-invalid (ADR-0051) — the danger border, appearing only AFTER the first interaction. */
|
|
298
|
+
:scope:state(user-invalid) > [data-part='trigger'] {
|
|
299
|
+
border-color: var(--ui-select-border-invalid);
|
|
300
|
+
}
|
|
301
|
+
:scope:state(user-invalid) > [data-part='trigger']:hover {
|
|
302
|
+
border-color: var(--ui-select-border-invalid-hover);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* Disabled trigger: pointer-inert; :hover/:active become inert. The trigger is a REAL native
|
|
306
|
+
<button> whose own `disabled` attribute select.ts already sets from effectiveDisabled() (own ||
|
|
307
|
+
form-disabled) — a native disabled button is platform-inert regardless of this rule, so
|
|
308
|
+
`pointer-events: none` here is defensive-only; `:state(disabled)` (TKT-0063) widens the match so
|
|
309
|
+
`cursor: default` also applies under the form-disabled-only case, not just the host's own
|
|
310
|
+
`[disabled]` attribute. */
|
|
311
|
+
:scope:is([disabled], :state(disabled)) > [data-part='trigger'] {
|
|
312
|
+
pointer-events: none;
|
|
313
|
+
cursor: default;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/* ── The listbox panel (Container/surface in the Popover API top layer) ───────────────────────
|
|
317
|
+
The UA `[popover]` rule hides this with display:none until showPopover(). The overlay
|
|
318
|
+
controller sets position:fixed + inset on open — we do NOT set position here. `margin: 0`
|
|
319
|
+
prevents UA-default centering from conflicting with the controller's explicit placement. */
|
|
320
|
+
:scope > [data-part='listbox'] {
|
|
321
|
+
box-sizing: border-box;
|
|
322
|
+
margin: 0;
|
|
323
|
+
min-inline-size: var(--ui-select-listbox-min-inline-size);
|
|
324
|
+
/* Matching the trigger width is done in JS (see select.ts model→overlay effect): in the
|
|
325
|
+
Popover API top layer `100%` resolves to 100vw (the viewport), NOT the trigger width.
|
|
326
|
+
select.ts stamps `listbox.style.minInlineSize = triggerWidth + 'px'` before showPopover(). */
|
|
327
|
+
max-block-size: var(--ui-select-listbox-max-block-size); /* TKT-0027 — public dial, default min(50vh, 12 option rows) */
|
|
328
|
+
overflow-y: auto;
|
|
329
|
+
|
|
330
|
+
background-color: var(--ui-select-listbox-bg);
|
|
331
|
+
color: var(--ui-select-listbox-ink);
|
|
332
|
+
border: 1px solid var(--ui-select-listbox-outline);
|
|
333
|
+
border-radius: var(--ui-select-listbox-radius);
|
|
334
|
+
padding: 0; /* the box-model child margins provide the inset (container-box.css); no shell padding */
|
|
335
|
+
/* Repoint the shared container-box.css inset LOCALLY to the size-derived value (the panel
|
|
336
|
+
carries [data-box], see select.ts — that rule declares --ui-box-inset: 0.375rem at
|
|
337
|
+
specificity 0; this higher-specificity declaration on the same element overrides it so
|
|
338
|
+
the size-carrying model owns the value fed to the rows' margin + the nested-radius calc
|
|
339
|
+
below, instead of the shared fleet-wide 6px fallback every [data-box] otherwise gets). */
|
|
340
|
+
--ui-box-inset: var(--ui-select-listbox-padding);
|
|
341
|
+
font-size: var(--ui-select-option-font); /* was unset (inherited ambient) — options + the group label now read the ramp */
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* ── Option rows (legacy item-pad — §5.1 / ROV-C5) ──────────────────────────────────────────
|
|
345
|
+
Options are text list items with padding-based row height (not a fixed widget-box height).
|
|
346
|
+
rovingFocus manages tabindex (0 = roving current, -1 = others). The focus ring on :focus-visible
|
|
347
|
+
is the fleet ring (the direct --md-sys-color-focus-ring read, the one permitted exception — same as button). */
|
|
348
|
+
:scope > [data-part='listbox'] [role='option'] {
|
|
349
|
+
box-sizing: border-box;
|
|
350
|
+
display: block;
|
|
351
|
+
margin: var(--ui-box-inset, 0.375rem); /* the box-model inset — repointed above to the size-derived value */
|
|
352
|
+
padding-block: var(--ui-select-option-block);
|
|
353
|
+
padding-inline: var(--ui-select-option-inline);
|
|
354
|
+
/* line-height: 1 makes the (h−font)/2 block-pad math exact — font + 2×pad == the trigger
|
|
355
|
+
height, so the row's rendered content-box is literally the SAME height as the control that
|
|
356
|
+
opened it (the row-height law above), not the DoD's Control-class line-height rule (that
|
|
357
|
+
one is scoped to Control-class elements; this is a local derivation choice for item rows). */
|
|
358
|
+
line-height: 1;
|
|
359
|
+
border-radius: var(--ui-select-option-radius);
|
|
360
|
+
cursor: pointer;
|
|
361
|
+
-webkit-user-select: none;
|
|
362
|
+
user-select: none;
|
|
363
|
+
outline: none; /* suppress the default UA outline — :focus-visible provides the ring below */
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/* Groups are STRUCTURAL, not visible boxes — full-bleed (no inset margin); their options + sticky header
|
|
367
|
+
carry the box rhythm. (Overrides the shared `[data-box] > *` margin for the group wrapper.) */
|
|
368
|
+
:scope > [data-part='listbox'] > [role='group'] {
|
|
369
|
+
margin: 0;
|
|
370
|
+
display: block;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/* Group header (optgroup parity) — a non-interactive label; NOT a [role=option], so rovingFocus +
|
|
374
|
+
selectionCommit skip it. STICKY: pins to the panel's scroll top and takes the panel surface so scrolled
|
|
375
|
+
options never show through (the container box-model region pattern). Full-bleed edge-to-edge. */
|
|
376
|
+
:scope > [data-part='listbox'] [data-part='group-label'] {
|
|
377
|
+
box-sizing: border-box;
|
|
378
|
+
display: block;
|
|
379
|
+
position: sticky;
|
|
380
|
+
inset-block-start: 0;
|
|
381
|
+
z-index: 1;
|
|
382
|
+
margin: 0; /* full-bleed — spans the panel width so the sticky surface fully occludes scrolled rows */
|
|
383
|
+
padding-block: var(--ui-select-option-block);
|
|
384
|
+
padding-inline: var(--ui-select-option-inline);
|
|
385
|
+
background-color: var(--ui-select-listbox-bg); /* the panel surface — occludes scrolled content */
|
|
386
|
+
color: var(--ui-select-group-label-ink);
|
|
387
|
+
font-size: 0.85em;
|
|
388
|
+
font-weight: 600;
|
|
389
|
+
letter-spacing: 0.02em;
|
|
390
|
+
cursor: default;
|
|
391
|
+
-webkit-user-select: none;
|
|
392
|
+
user-select: none;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* A group after the first gets a hairline divider above it (visual grouping between sticky sections). */
|
|
396
|
+
:scope > [data-part='listbox'] > [role='group'] + [role='group'] {
|
|
397
|
+
border-block-start: 1px solid var(--ui-select-listbox-outline);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
:scope > [data-part='listbox'] [role='option']:hover {
|
|
401
|
+
background-color: var(--ui-select-option-bg-hover);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
:scope > [data-part='listbox'] [role='option'][aria-selected='true'] {
|
|
405
|
+
background-color: var(--ui-select-option-bg-selected);
|
|
406
|
+
color: var(--ui-select-option-ink-selected);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
:scope > [data-part='listbox'] [role='option']:focus-visible {
|
|
410
|
+
background-color: var(--ui-select-option-bg-focus);
|
|
411
|
+
color: var(--ui-select-option-ink-focus);
|
|
412
|
+
outline: var(--md-sys-state-focus-ring-width) solid var(--md-sys-color-focus-ring);
|
|
413
|
+
outline-offset: var(--md-sys-state-focus-ring-offset);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/* Disabled option — not interactive; rovingFocus skips disabled items. */
|
|
417
|
+
:scope > [data-part='listbox'] [role='option'][aria-disabled='true'],
|
|
418
|
+
:scope > [data-part='listbox'] [role='option'][disabled] {
|
|
419
|
+
color: var(--ui-select-ink-disabled);
|
|
420
|
+
cursor: default;
|
|
421
|
+
pointer-events: none;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/* ── Motion (state-PAINT only; geometry snaps — ADR-0008) ─────────────────────────────────── */
|
|
425
|
+
|
|
426
|
+
:scope > [data-part='trigger']:state(ready) {
|
|
427
|
+
transition:
|
|
428
|
+
background-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
|
|
429
|
+
border-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
|
|
430
|
+
color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard);
|
|
431
|
+
}
|
|
432
|
+
@media (prefers-reduced-motion: reduce) {
|
|
433
|
+
:scope > [data-part='trigger']:state(ready) {
|
|
434
|
+
transition: none;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* ── forced-colors (WHCM) ─────────────────────────────────────────────────────────────────── */
|
|
439
|
+
|
|
440
|
+
@media (forced-colors: active) {
|
|
441
|
+
/* Trigger: keep ink and border as system ButtonText/ButtonBorder; background stays as
|
|
442
|
+
ButtonFace. The focus ring survives via --md-sys-color-focus-ring → Highlight. */
|
|
443
|
+
:scope > [data-part='trigger'] {
|
|
444
|
+
color: ButtonText;
|
|
445
|
+
border-color: ButtonBorder;
|
|
446
|
+
background: ButtonFace;
|
|
447
|
+
forced-color-adjust: none; /* preserve our explicit border/bg/color from the OS wash */
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* Panel: Canvas/CanvasText for the surface; CanvasText for the border. */
|
|
451
|
+
:scope > [data-part='listbox'] {
|
|
452
|
+
background-color: Canvas;
|
|
453
|
+
color: CanvasText;
|
|
454
|
+
border-color: CanvasText;
|
|
455
|
+
forced-color-adjust: none;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/* Option selected/hover: Highlight/HighlightText (system selection colours). */
|
|
459
|
+
:scope > [data-part='listbox'] [role='option']:hover,
|
|
460
|
+
:scope > [data-part='listbox'] [role='option'][aria-selected='true'],
|
|
461
|
+
:scope > [data-part='listbox'] [role='option']:focus-visible {
|
|
462
|
+
background-color: Highlight;
|
|
463
|
+
color: HighlightText;
|
|
464
|
+
forced-color-adjust: none;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type ReactiveProps } from '../../dom/index.js';
|
|
2
|
+
import { UIFormElement } from '../../dom/index.js';
|
|
3
|
+
import type { FormValue, ValidityResult, FieldLabelling } from '../../dom/index.js';
|
|
4
|
+
import { type OverlayHandle } from '../../traits/overlay.js';
|
|
5
|
+
declare const props: {
|
|
6
|
+
value: {
|
|
7
|
+
reflect: true;
|
|
8
|
+
type: import("../../index.js").PropType<string>;
|
|
9
|
+
default: string;
|
|
10
|
+
attribute?: string | false;
|
|
11
|
+
};
|
|
12
|
+
label: {
|
|
13
|
+
reflect: true;
|
|
14
|
+
type: import("../../index.js").PropType<string>;
|
|
15
|
+
default: string;
|
|
16
|
+
attribute?: string | false;
|
|
17
|
+
};
|
|
18
|
+
open: {
|
|
19
|
+
reflect: true;
|
|
20
|
+
type: import("../../index.js").PropType<boolean>;
|
|
21
|
+
default: boolean;
|
|
22
|
+
attribute?: string | false;
|
|
23
|
+
};
|
|
24
|
+
placeholder: {
|
|
25
|
+
type: import("../../index.js").PropType<string>;
|
|
26
|
+
default: string;
|
|
27
|
+
attribute?: string | false;
|
|
28
|
+
reflect?: boolean;
|
|
29
|
+
};
|
|
30
|
+
size: {
|
|
31
|
+
reflect: true;
|
|
32
|
+
type: import("../../index.js").PropType<"sm" | "md" | "lg">;
|
|
33
|
+
default: "sm" | "md" | "lg";
|
|
34
|
+
attribute?: string | false;
|
|
35
|
+
};
|
|
36
|
+
name: {
|
|
37
|
+
reflect: true;
|
|
38
|
+
type: import("../../index.js").PropType<string>;
|
|
39
|
+
default: string;
|
|
40
|
+
attribute?: string | false;
|
|
41
|
+
};
|
|
42
|
+
disabled: {
|
|
43
|
+
reflect: true;
|
|
44
|
+
type: import("../../index.js").PropType<boolean>;
|
|
45
|
+
default: boolean;
|
|
46
|
+
attribute?: string | false;
|
|
47
|
+
};
|
|
48
|
+
required: {
|
|
49
|
+
reflect: true;
|
|
50
|
+
type: import("../../index.js").PropType<boolean>;
|
|
51
|
+
default: boolean;
|
|
52
|
+
attribute?: string | false;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
export interface UISelectElement extends ReactiveProps<typeof props> {
|
|
56
|
+
}
|
|
57
|
+
export declare class UISelectElement extends UIFormElement {
|
|
58
|
+
#private;
|
|
59
|
+
static props: {
|
|
60
|
+
value: {
|
|
61
|
+
reflect: true;
|
|
62
|
+
type: import("../../index.js").PropType<string>;
|
|
63
|
+
default: string;
|
|
64
|
+
attribute?: string | false;
|
|
65
|
+
};
|
|
66
|
+
label: {
|
|
67
|
+
reflect: true;
|
|
68
|
+
type: import("../../index.js").PropType<string>;
|
|
69
|
+
default: string;
|
|
70
|
+
attribute?: string | false;
|
|
71
|
+
};
|
|
72
|
+
open: {
|
|
73
|
+
reflect: true;
|
|
74
|
+
type: import("../../index.js").PropType<boolean>;
|
|
75
|
+
default: boolean;
|
|
76
|
+
attribute?: string | false;
|
|
77
|
+
};
|
|
78
|
+
placeholder: {
|
|
79
|
+
type: import("../../index.js").PropType<string>;
|
|
80
|
+
default: string;
|
|
81
|
+
attribute?: string | false;
|
|
82
|
+
reflect?: boolean;
|
|
83
|
+
};
|
|
84
|
+
size: {
|
|
85
|
+
reflect: true;
|
|
86
|
+
type: import("../../index.js").PropType<"sm" | "md" | "lg">;
|
|
87
|
+
default: "sm" | "md" | "lg";
|
|
88
|
+
attribute?: string | false;
|
|
89
|
+
};
|
|
90
|
+
name: {
|
|
91
|
+
reflect: true;
|
|
92
|
+
type: import("../../index.js").PropType<string>;
|
|
93
|
+
default: string;
|
|
94
|
+
attribute?: string | false;
|
|
95
|
+
};
|
|
96
|
+
disabled: {
|
|
97
|
+
reflect: true;
|
|
98
|
+
type: import("../../index.js").PropType<boolean>;
|
|
99
|
+
default: boolean;
|
|
100
|
+
attribute?: string | false;
|
|
101
|
+
};
|
|
102
|
+
required: {
|
|
103
|
+
reflect: true;
|
|
104
|
+
type: import("../../index.js").PropType<boolean>;
|
|
105
|
+
default: boolean;
|
|
106
|
+
attribute?: string | false;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Protected overlay handle — accessible to test probes (C10 idempotent-cleanup DoD).
|
|
111
|
+
* Replaced on each reconnect (connected() re-runs); the old handle's cleanup fires via the
|
|
112
|
+
* scope effect disposer at disconnect before this is re-assigned.
|
|
113
|
+
*/
|
|
114
|
+
protected _overlayHandle: OverlayHandle | null;
|
|
115
|
+
protected formValue(): FormValue;
|
|
116
|
+
protected formValidity(): ValidityResult;
|
|
117
|
+
protected formReset(): void;
|
|
118
|
+
protected disconnected(): void;
|
|
119
|
+
/** Feeds `FormConnectDetail.userInvalid` (ADR-0050) — the `trackUserInvalid` tracker IS the one
|
|
120
|
+
* timing source; this override just exposes its gate. */
|
|
121
|
+
protected formUserInvalid(): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* The part-role override (ADR-0085 cl.4) — the trigger carries no `internals.role` (a logical select
|
|
124
|
+
* wrapper), so the base's guarded internals-reflection default (dom/form.ts) never fires; wire the
|
|
125
|
+
* trigger's `aria-labelledby`/`aria-describedby` content attributes directly instead (the text-field
|
|
126
|
+
* `applyFieldLabelling` precedent, ADR-0051 cl.2/LLD-C2).
|
|
127
|
+
*
|
|
128
|
+
* `aria-labelledby` is the BARE-mode effect's (connected(), below) exclusive concern whenever
|
|
129
|
+
* `fieldLabelling === null` — this override never touches it on dissociation (`refs === null`), so the
|
|
130
|
+
* two effects can never race to write the SAME attribute in the same flush wave (the F3 dual-writer
|
|
131
|
+
* discipline text-field's own override documents). While fielded (`refs !== null`), THIS method is the
|
|
132
|
+
* exclusive writer: MERGE, never clobber — `<refs.label.id> <value-span-id>` (field label + value),
|
|
133
|
+
* the same two-id shape as the bare path, only the name source swaps.
|
|
134
|
+
*
|
|
135
|
+
* `aria-describedby` has NO other owner for this control (the trigger carries no internal
|
|
136
|
+
* validity-message node, unlike text-field's editor) — so unlike text-field's override, THIS one is the
|
|
137
|
+
* describedby's exclusive owner in BOTH directions: written from `[refs.description, refs.error]` when
|
|
138
|
+
* fielded, cleared on dissociation.
|
|
139
|
+
*
|
|
140
|
+
* Guards a not-yet-created trigger (the LLD-C2 override contract) — cannot happen in practice
|
|
141
|
+
* (`#ensureParts()` runs synchronously at the top of `connected()`, before the base's forwarding effect
|
|
142
|
+
* installs), but the guard costs nothing and documents the contract.
|
|
143
|
+
*/
|
|
144
|
+
protected applyFieldLabelling(refs: FieldLabelling | null): void;
|
|
145
|
+
protected connected(): void;
|
|
146
|
+
}
|
|
147
|
+
export {};
|