@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,231 @@
|
|
|
1
|
+
/* slider.css — ui-slider, the single-thumb Range-class control (range-element.lld.md · ADR-0042 · ADR-0041).
|
|
2
|
+
* Two clearly-SECTIONED blocks:
|
|
3
|
+
*
|
|
4
|
+
* [1] TOKEN BLOCK — `:where(ui-slider)` (specificity 0,0,0) DECLARES the `--ui-slider-*` chain from
|
|
5
|
+
* the colour roles (`--md-sys-color-{family}-{role}`) and the compact widget ramp (`--md-sys-compact-{size}`,
|
|
6
|
+
* ADR-0041). `[size]` repoints the box token; `[disabled]` mutes to neutral roles. No `color-mix`
|
|
7
|
+
* — colour opinions live in the token layer (ADR-0008). `--ui-slider-rail-height` is a thin fleet
|
|
8
|
+
* constant (3px), NOT widget-box derived — the rail height does NOT scale with the thumb box.
|
|
9
|
+
* [2] STYLES BLOCK — `@scope (ui-slider)` CONSUMES only `--ui-slider-*` for its OWN tokens. The host
|
|
10
|
+
* is a `block` container: its block-size = the widget box (`--md-sys-compact-{size}`). The RAIL (`::before`)
|
|
11
|
+
* is a thin horizontal bar, full-width, vertically centred, with a gradient fill from the primary
|
|
12
|
+
* colour up to `--value-pct %` and neutral beyond — one declaration paints both the fill and the
|
|
13
|
+
* remainder. The THUMB (`::after`) is a circle `box − 2×inset = box − 4px` (ADR-0041 cl.3: the
|
|
14
|
+
* two-pixel inset law) centred vertically and positioned horizontally at `calc(--value-pct * 1%)`
|
|
15
|
+
* with `transform: translate(-50%, -50%)` so the circle centre lands on that percentage of the host.
|
|
16
|
+
* `--value-pct` is a UNITLESS number (0–100) set by UIRangeElement on the host inline style.
|
|
17
|
+
* `:focus-visible` ring (keyboard-only, ADR-0009). Forced-colors safe. Density-invariant.
|
|
18
|
+
*
|
|
19
|
+
* Standalone stylesheet — linked via the host page / component-styles barrel; NEVER injected from
|
|
20
|
+
* slider.ts (behaviour-only `.ts`, no runtime style injection — plan §2).
|
|
21
|
+
*
|
|
22
|
+
* 2026-07-07 fix (Kim-filed visual bug): the dark-mode thumb "blends into its surroundings." Measured
|
|
23
|
+
* root cause — NOT the fill/rail pair ADR-0059 already verified (those still clear 3:1: 4.79:1 light /
|
|
24
|
+
* 3.67:1 dark vs fill, 5.66:1 / 4.41:1 vs rail; unchanged below). The thumb is TALLER than the 3px rail,
|
|
25
|
+
* so most of its perimeter borders the general PAGE SURFACE, not the rail/fill — a dimension ADR-0059
|
|
26
|
+
* never measured (switch's thumb never has this problem: its track fills the FULL switch height, so no
|
|
27
|
+
* "outside the track" area exists). Measured: dark-mode `--md-sys-color-neutral-surface-brightest`
|
|
28
|
+
* (neutral-800, the SAME stop as `--md-sys-color-neutral-surface-highest`) vs the dark surface ladder it
|
|
29
|
+
* actually sits on (neutral-800…950) is only **1.09–1.46:1** — it nearly matches the page's own darkest
|
|
30
|
+
* surface tones by construction. Kim's literal ask ("use onPrimary like the switch") does NOT fix this and
|
|
31
|
+
* REGRESSES the already-solved rail case: `--md-sys-color-primary-on-primary` is a FLAT white in both
|
|
32
|
+
* schemes (unlike this scheme-adaptive thumb), and against the dark-mode rail (neutral-400, itself
|
|
33
|
+
* deliberately brightened by ADR-0059) a flat white thumb measures only **2.76:1** — an SC 1.4.11
|
|
34
|
+
* regression below the rail's own contrast floor. Rejected for the same reason: no single flat thumb fill
|
|
35
|
+
* can simultaneously clear 3:1 against the dark rail (needs thumb luminance ≤ ~0.077) AND against the
|
|
36
|
+
* dark surface ladder (needs thumb luminance ≥ ~0.21 to clear the brightest dark surface plane) — those
|
|
37
|
+
* two floors don't overlap with the current rail/surface tokens (verified via the OKLCH→linear-sRGB→WCAG
|
|
38
|
+
* path `tokens.test.ts` already uses). Fix: leave the interior fill (`--ui-slider-thumb`) UNTOUCHED — it
|
|
39
|
+
* already carries the ADR-0059-verified fill/rail contrast — and add a second, independent ring layer
|
|
40
|
+
* (`--ui-slider-thumb-ring`, a `border` on `::after`) using `--md-sys-color-neutral-on-surface` (the fleet's
|
|
41
|
+
* own page-ink role, AA-verified against every surface plane by `tokens.test.ts`). The ring is BRIGHT in
|
|
42
|
+
* dark mode (near-white) and dark in light mode — giving the thumb the switch's "bright accent" signature
|
|
43
|
+
* in the register (surface-adjacent) where it was actually failing, without touching the fill's proven-safe
|
|
44
|
+
* contrast against the rail/fill it was tuned for. Recorded in ADR-0094 (2026-07-07, extends ADR-0059) —
|
|
45
|
+
* the thumb-vs-PAGE-SURFACE dimension is part of its three-backdrop thumb contrast contract.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/* ════════════════ [1] TOKEN BLOCK — :where(ui-slider) declares --ui-slider-* ════════════════ */
|
|
49
|
+
|
|
50
|
+
:where(ui-slider) {
|
|
51
|
+
/* Box geometry — the compact widget-box ramp step (ADR-0041; default size = md). `[size]` repoints. */
|
|
52
|
+
--ui-slider-box: var(--md-sys-compact-md);
|
|
53
|
+
|
|
54
|
+
/* Rail colour — the unselected portion of the track. A state-bearing control part → the SOLID
|
|
55
|
+
--md-sys-color-neutral-track (SC 1.4.11 3:1 vs every surface plane), NOT the old translucent
|
|
56
|
+
--md-sys-color-neutral-outline-variant which composited to 1.51:1 light / 1.73:1 dark (an SC 1.4.11 fail — at
|
|
57
|
+
value 0 the whole track went near-invisible). The fill↔rail luminance boundary is intentionally low
|
|
58
|
+
(primary and neutral are L-close) — the VALUE is carried by the high-contrast THUMB, which clears 3:1
|
|
59
|
+
against both the fill (4.79/3.67) and this rail (4.69/3.74). 2026-07-02 color audit item 4 · ADR-0059. */
|
|
60
|
+
--ui-slider-rail: var(--md-sys-color-neutral-track);
|
|
61
|
+
|
|
62
|
+
/* Fill colour — the selected portion (min → value), primary family. */
|
|
63
|
+
--ui-slider-fill: var(--md-sys-color-primary);
|
|
64
|
+
|
|
65
|
+
/* Thumb colour — the drag handle, brightest neutral surface (≈ white on light; dark on dark).
|
|
66
|
+
Verified vs fill/rail (ADR-0059) — untouched by the 2026-07-07 ring fix below. */
|
|
67
|
+
--ui-slider-thumb: var(--md-sys-color-neutral-surface-brightest);
|
|
68
|
+
|
|
69
|
+
/* Thumb RING (2026-07-07 fix) — a border layer independent of the fill, using the fleet's page-ink
|
|
70
|
+
role (`--md-sys-color-neutral-on-surface`: dark in light mode, bright/white in dark mode — AA-verified
|
|
71
|
+
against every surface plane by tokens.test.ts). Covers the thumb-vs-PAGE-SURFACE contrast dimension
|
|
72
|
+
the fill alone can't (see the file-header note): most of the thumb's perimeter borders the general
|
|
73
|
+
surface, not the thin rail, and in dark mode the fill alone measured only 1.09–1.46:1 there. */
|
|
74
|
+
--ui-slider-thumb-ring: var(--md-sys-color-neutral-on-surface);
|
|
75
|
+
|
|
76
|
+
/* Rail height — thin constant, NOT derived from the widget box (ADR-0041 geometry: the inset law
|
|
77
|
+
governs the THUMB size only; the rail is a separate cosmetic layer). 3px reads cleanly at all
|
|
78
|
+
widget-box sizes (12–28 px). */
|
|
79
|
+
--ui-slider-rail-height: 3px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* [size] → compact widget ramp repoint (ADR-0041: the per-[scale] table in dimensions.css re-tables
|
|
83
|
+
--md-sys-compact-{sm,md,lg}; `[size]` on the host picks which step the slider box targets). */
|
|
84
|
+
:where(ui-slider[size='sm']) {
|
|
85
|
+
--ui-slider-box: var(--md-sys-compact-sm);
|
|
86
|
+
}
|
|
87
|
+
:where(ui-slider[size='lg']) {
|
|
88
|
+
--ui-slider-box: var(--md-sys-compact-lg);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* [disabled] → muted neutral roles for rail, fill, and thumb. The state holds at idle: the styles
|
|
92
|
+
block sets `pointer-events: none` so `:hover` never matches, preventing any token lift. The rail
|
|
93
|
+
deliberately stays the translucent --md-sys-color-neutral-outline-variant (NOT the solid --md-sys-color-neutral-track): a
|
|
94
|
+
disabled/inactive component is SC 1.4.11-EXEMPT, and the muted rail correctly reads as inactive. */
|
|
95
|
+
:where(ui-slider[disabled]) {
|
|
96
|
+
--ui-slider-rail: var(--md-sys-color-neutral-outline-variant);
|
|
97
|
+
--ui-slider-fill: var(--md-sys-color-neutral-surface-high);
|
|
98
|
+
--ui-slider-thumb: var(--md-sys-color-neutral-on-surface-variant);
|
|
99
|
+
--ui-slider-thumb-ring: var(--md-sys-color-neutral-on-surface-variant); /* SC 1.4.11-exempt; matches fill, no separate ring emphasis */
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* ════════════════ [2] STYLES BLOCK — @scope (ui-slider) consumes ONLY --ui-slider-* ════════════════ */
|
|
103
|
+
|
|
104
|
+
@scope (ui-slider) {
|
|
105
|
+
/* Host: a block container whose block-size equals the widget box (the interactive hit area is the
|
|
106
|
+
full height of the slider, spanning the thumb diameter). `position: relative` is the containing
|
|
107
|
+
block for both the rail and thumb absolute pseudo-elements. `touch-action: none` prevents the
|
|
108
|
+
browser from claiming pointer events for scroll/pan while a drag is in progress. */
|
|
109
|
+
:scope {
|
|
110
|
+
display: block;
|
|
111
|
+
position: relative;
|
|
112
|
+
/* A slider is a HORIZONTAL track — without an intrinsic width it collapses to the thumb (a dot) in
|
|
113
|
+
a shrink-wrapping flex/inline context (the same failure class as ui-text-field #74). A
|
|
114
|
+
min-inline-size floor prevents the collapse; above it, width is the layout's job. */
|
|
115
|
+
min-inline-size: var(--ui-slider-min-inline-size, 12rem);
|
|
116
|
+
block-size: var(--ui-slider-box);
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
touch-action: none; /* give pointer events exclusively to the slider during drag (no scroll-conflict) */
|
|
119
|
+
-webkit-user-select: none; /* WebKit/Safari still requires the prefix */
|
|
120
|
+
user-select: none;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* RAIL (::before) — thin horizontal bar, full-width, centred in the block dimension.
|
|
124
|
+
A single `background: linear-gradient` paints BOTH the fill (primary, from 0 to --value-pct%)
|
|
125
|
+
and the remaining rail (neutral, from --value-pct% to 100%). `--value-pct` is a unitless number
|
|
126
|
+
(0–100) so `calc(var(--value-pct, 0) * 1%)` converts it to a CSS percentage. No `color-mix` —
|
|
127
|
+
fill and rail are distinct colour roles (ADR-0008). No position transition: the fill updates in
|
|
128
|
+
real-time during drag; animating a gradient can lag. Background-color transitions (disabled↔enabled
|
|
129
|
+
state) could be added here but are omitted — the rail gradient change is instant. */
|
|
130
|
+
:scope::before {
|
|
131
|
+
content: '';
|
|
132
|
+
position: absolute;
|
|
133
|
+
inset-block-start: 50%;
|
|
134
|
+
transform: translateY(-50%);
|
|
135
|
+
inset-inline-start: 0;
|
|
136
|
+
inline-size: 100%;
|
|
137
|
+
block-size: var(--ui-slider-rail-height);
|
|
138
|
+
border-radius: calc(var(--ui-slider-rail-height) / 2);
|
|
139
|
+
background: linear-gradient(
|
|
140
|
+
to right,
|
|
141
|
+
var(--ui-slider-fill) calc(var(--value-pct, 0) * 1%),
|
|
142
|
+
var(--ui-slider-rail) calc(var(--value-pct, 0) * 1%)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* THUMB (::after) — a circle inset by --md-sys-widget-inset (2px) on all sides (ADR-0041 cl.3).
|
|
147
|
+
*
|
|
148
|
+
* Size: thumb = box − 2×inset = box − 4px (the two-pixel inset law, density-invariant flat fleet constant).
|
|
149
|
+
* Block: inset-block-start: 50% places the top edge at 50% of the host height; `transform: translate(-50%,
|
|
150
|
+
* -50%)` shifts the element back by half its own height and width, so its CENTRE lands at (50% of
|
|
151
|
+
* host height, --value-pct% of host width) — vertically centred in the widget box.
|
|
152
|
+
* Inline: inset-inline-start: calc(var(--value-pct, 0) * 1%) positions the left edge at N% of the host;
|
|
153
|
+
* the -50% X component of the same translate() centres the circle on that point. At value-pct=0
|
|
154
|
+
* and 100 the thumb centre is exactly on the host edge (slight overhang of radius; standard slider
|
|
155
|
+
* behaviour, no overflow clipping needed for the interaction — the hit area is the full host).
|
|
156
|
+
*
|
|
157
|
+
* Motion: NO position transition (drag lag). Background-color transitions for disabled/enabled state.
|
|
158
|
+
* Transition is zeroed under prefers-reduced-motion (accessibility non-negotiable).
|
|
159
|
+
*
|
|
160
|
+
* Ring: a 2px border in `--ui-slider-thumb-ring` (2026-07-07 fix, see file header) — box-sizing:
|
|
161
|
+
* border-box keeps the OUTER diameter at the ADR-0041 cl.3 box−4px law (the border eats into the fill,
|
|
162
|
+
* it never grows the box), so the size proof above is unaffected; the border simply repaints the outer
|
|
163
|
+
* rim in the ink role, giving the thumb a contrast layer independent of its own interior fill. */
|
|
164
|
+
:scope::after {
|
|
165
|
+
content: '';
|
|
166
|
+
position: absolute;
|
|
167
|
+
box-sizing: border-box;
|
|
168
|
+
|
|
169
|
+
/* ADR-0041 cl.3: thumb = box − 2×inset = box − 4px */
|
|
170
|
+
--ui-slider-thumb-size: calc(var(--ui-slider-box) - 2 * var(--md-sys-widget-inset));
|
|
171
|
+
inline-size: var(--ui-slider-thumb-size);
|
|
172
|
+
block-size: var(--ui-slider-thumb-size);
|
|
173
|
+
border-radius: 50%; /* circle */
|
|
174
|
+
|
|
175
|
+
background: var(--ui-slider-thumb);
|
|
176
|
+
border: 2px solid var(--ui-slider-thumb-ring);
|
|
177
|
+
|
|
178
|
+
/* Centre the thumb circle on the point (--value-pct% of host width, 50% of host height). */
|
|
179
|
+
inset-block-start: 50%;
|
|
180
|
+
inset-inline-start: calc(var(--value-pct, 0) * 1%);
|
|
181
|
+
transform: translate(-50%, -50%);
|
|
182
|
+
|
|
183
|
+
transition:
|
|
184
|
+
background-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
|
|
185
|
+
border-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Focus ring (ADR-0009) — the shared fleet ring: `:focus-visible` (keyboard-only). Surrounds the
|
|
189
|
+
whole control (host box), the fleet standard. Reads fleet-wide tokens directly — the one deliberate
|
|
190
|
+
exception to "consume only --ui-slider-*", matching the checkbox/switch pattern. */
|
|
191
|
+
:scope:focus-visible {
|
|
192
|
+
outline: var(--md-sys-state-focus-ring-width) solid var(--md-sys-color-focus-ring);
|
|
193
|
+
outline-offset: var(--md-sys-state-focus-ring-offset);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* Reduced motion — zero ALL transitions (accessibility non-negotiable). */
|
|
197
|
+
@media (prefers-reduced-motion: reduce) {
|
|
198
|
+
:scope::after {
|
|
199
|
+
transition: none;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Disabled — pointer-inert + cursor reset. The [disabled] token block already muted the colours;
|
|
204
|
+
`pointer-events: none` ensures `:hover` never matches (muted tokens hold at idle forever). */
|
|
205
|
+
:scope[disabled] {
|
|
206
|
+
pointer-events: none;
|
|
207
|
+
cursor: default;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* Forced-colors — map the rail and thumb to system colours so the slider survives high-contrast mode.
|
|
211
|
+
The rail gradient uses Highlight for the fill and ButtonText for the track; the thumb uses Canvas
|
|
212
|
+
with a Highlight border so the handle is distinct from the fill.
|
|
213
|
+
`forced-color-adjust: none` preserves our explicit system-colour mappings, preventing the browser
|
|
214
|
+
from re-overriding them (same pattern as switch.css / checkbox.css — ADR-0009 ring is free via
|
|
215
|
+
--md-sys-color-focus-ring → Highlight from the token layer). */
|
|
216
|
+
@media (forced-colors: active) {
|
|
217
|
+
:scope::before {
|
|
218
|
+
background: linear-gradient(
|
|
219
|
+
to right,
|
|
220
|
+
Highlight calc(var(--value-pct, 0) * 1%),
|
|
221
|
+
ButtonText calc(var(--value-pct, 0) * 1%)
|
|
222
|
+
);
|
|
223
|
+
forced-color-adjust: none;
|
|
224
|
+
}
|
|
225
|
+
:scope::after {
|
|
226
|
+
background: Canvas;
|
|
227
|
+
border: 2px solid Highlight;
|
|
228
|
+
forced-color-adjust: none;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { type ReactiveProps } from '../../dom/index.js';
|
|
2
|
+
import { UIRangeElement } from '../_base/range-element.js';
|
|
3
|
+
declare const sliderProps: {
|
|
4
|
+
min: {
|
|
5
|
+
reflect: true;
|
|
6
|
+
type: import("../../index.js").PropType<number | null>;
|
|
7
|
+
default: number | null;
|
|
8
|
+
attribute?: string | false;
|
|
9
|
+
};
|
|
10
|
+
max: {
|
|
11
|
+
reflect: true;
|
|
12
|
+
type: import("../../index.js").PropType<number | null>;
|
|
13
|
+
default: number | null;
|
|
14
|
+
attribute?: string | false;
|
|
15
|
+
};
|
|
16
|
+
step: {
|
|
17
|
+
reflect: true;
|
|
18
|
+
type: import("../../index.js").PropType<number | null>;
|
|
19
|
+
default: number | null;
|
|
20
|
+
attribute?: string | false;
|
|
21
|
+
};
|
|
22
|
+
value: {
|
|
23
|
+
reflect: true;
|
|
24
|
+
type: import("../../index.js").PropType<number | null>;
|
|
25
|
+
default: number | null;
|
|
26
|
+
attribute?: string | false;
|
|
27
|
+
};
|
|
28
|
+
size: {
|
|
29
|
+
reflect: true;
|
|
30
|
+
type: import("../../index.js").PropType<"sm" | "md" | "lg">;
|
|
31
|
+
default: "sm" | "md" | "lg";
|
|
32
|
+
attribute?: string | false;
|
|
33
|
+
};
|
|
34
|
+
name: {
|
|
35
|
+
reflect: true;
|
|
36
|
+
type: import("../../index.js").PropType<string>;
|
|
37
|
+
default: string;
|
|
38
|
+
attribute?: string | false;
|
|
39
|
+
};
|
|
40
|
+
disabled: {
|
|
41
|
+
reflect: true;
|
|
42
|
+
type: import("../../index.js").PropType<boolean>;
|
|
43
|
+
default: boolean;
|
|
44
|
+
attribute?: string | false;
|
|
45
|
+
};
|
|
46
|
+
required: {
|
|
47
|
+
reflect: true;
|
|
48
|
+
type: import("../../index.js").PropType<boolean>;
|
|
49
|
+
default: boolean;
|
|
50
|
+
attribute?: string | false;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
export interface UISliderElement extends ReactiveProps<typeof sliderProps> {
|
|
54
|
+
}
|
|
55
|
+
export declare class UISliderElement extends UIRangeElement {
|
|
56
|
+
static props: {
|
|
57
|
+
min: {
|
|
58
|
+
reflect: true;
|
|
59
|
+
type: import("../../index.js").PropType<number | null>;
|
|
60
|
+
default: number | null;
|
|
61
|
+
attribute?: string | false;
|
|
62
|
+
};
|
|
63
|
+
max: {
|
|
64
|
+
reflect: true;
|
|
65
|
+
type: import("../../index.js").PropType<number | null>;
|
|
66
|
+
default: number | null;
|
|
67
|
+
attribute?: string | false;
|
|
68
|
+
};
|
|
69
|
+
step: {
|
|
70
|
+
reflect: true;
|
|
71
|
+
type: import("../../index.js").PropType<number | null>;
|
|
72
|
+
default: number | null;
|
|
73
|
+
attribute?: string | false;
|
|
74
|
+
};
|
|
75
|
+
value: {
|
|
76
|
+
reflect: true;
|
|
77
|
+
type: import("../../index.js").PropType<number | null>;
|
|
78
|
+
default: number | null;
|
|
79
|
+
attribute?: string | false;
|
|
80
|
+
};
|
|
81
|
+
size: {
|
|
82
|
+
reflect: true;
|
|
83
|
+
type: import("../../index.js").PropType<"sm" | "md" | "lg">;
|
|
84
|
+
default: "sm" | "md" | "lg";
|
|
85
|
+
attribute?: string | false;
|
|
86
|
+
};
|
|
87
|
+
name: {
|
|
88
|
+
reflect: true;
|
|
89
|
+
type: import("../../index.js").PropType<string>;
|
|
90
|
+
default: string;
|
|
91
|
+
attribute?: string | false;
|
|
92
|
+
};
|
|
93
|
+
disabled: {
|
|
94
|
+
reflect: true;
|
|
95
|
+
type: import("../../index.js").PropType<boolean>;
|
|
96
|
+
default: boolean;
|
|
97
|
+
attribute?: string | false;
|
|
98
|
+
};
|
|
99
|
+
required: {
|
|
100
|
+
reflect: true;
|
|
101
|
+
type: import("../../index.js").PropType<boolean>;
|
|
102
|
+
default: boolean;
|
|
103
|
+
attribute?: string | false;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
static role: string;
|
|
107
|
+
protected _releaseDrag: (() => void);
|
|
108
|
+
protected connected(): void;
|
|
109
|
+
protected disconnected(): void;
|
|
110
|
+
}
|
|
111
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// slider.ts — UISliderElement, the single-thumb Range-class control (range-element.lld.md · ADR-0042 · ADR-0041).
|
|
2
|
+
// Extends UIRangeElement which owns: the numeric value model + clamp/snap (LLD-C1), the ARIA slider
|
|
3
|
+
// semantics (LLD-C2), keyboard step (LLD-C3), and the --value-pct geometry seam (LLD-C5). The leaf adds:
|
|
4
|
+
// • `static role = 'slider'` — confirmatory; the base sets internals.role='slider' directly in connected()
|
|
5
|
+
// • connected(): tabbable (keyboard focus) + valueDrag wiring (LLD-C4, one thumb at --value-pct)
|
|
6
|
+
// • slider.css — the rail (thin fill line) + thumb circle (box − 4px at --value-pct, ADR-0041 cl.3)
|
|
7
|
+
// • self-define as ui-slider
|
|
8
|
+
//
|
|
9
|
+
// All props (value / min / max / step / size / name / disabled / required) are inherited via the
|
|
10
|
+
// UIRangeElement.props spread. Zero-dep; controls → dom+traits inward only (✓); erasableSyntaxOnly ✓.
|
|
11
|
+
import {} from "../../dom/index.js";
|
|
12
|
+
import { UIRangeElement } from "../_base/range-element.js";
|
|
13
|
+
import { tabbable } from "../../traits/tabbable.js";
|
|
14
|
+
import { valueDrag } from "../../traits/value-drag.js";
|
|
15
|
+
// All props are inherited from UIRangeElement — spread into a local constant so UISliderElement
|
|
16
|
+
// carries its OWN static props (ADR-0013: static props cannot be inherited via the prototype chain;
|
|
17
|
+
// the spread is the documented workaround, matching how UICheckboxElement spreads UIIndicatorElement.props).
|
|
18
|
+
const sliderProps = {
|
|
19
|
+
...UIRangeElement.props,
|
|
20
|
+
};
|
|
21
|
+
export class UISliderElement extends UIRangeElement {
|
|
22
|
+
static props = sliderProps;
|
|
23
|
+
// LLD-C2 subclass contract: the role this leaf carries. The base hardcodes `this.internals.role =
|
|
24
|
+
// 'slider'` in connected() rather than reading a static field, so this declaration is confirmatory.
|
|
25
|
+
static role = 'slider';
|
|
26
|
+
// Connection-scoped valueDrag cleanup — re-assigned on each reconnect (connected() re-runs).
|
|
27
|
+
// Protected so test probes can call it directly to verify idempotent release (G6 DoD, decomp S1).
|
|
28
|
+
_releaseDrag = () => { };
|
|
29
|
+
connected() {
|
|
30
|
+
super.connected(); // base: normaliser · ARIA (ariaValueNow/Min/Max) · --value-pct seam · keyboard step
|
|
31
|
+
// Keyboard-focusable while enabled; removed from the tab order while disabled (ADR-0010).
|
|
32
|
+
tabbable(this, { disabled: () => this.effectiveDisabled() });
|
|
33
|
+
// LLD-C4: wire the pointer→value gesture controller. The host IS the interactive track surface
|
|
34
|
+
// (light-DOM — no child track element); opts.track() is re-read on each pointerdown so reconnect
|
|
35
|
+
// always resolves the live element. Emits `input` on each stepped value change; `change` is emitted
|
|
36
|
+
// by the base on blur when value has moved since focus (the base's commit-on-blur contract).
|
|
37
|
+
this._releaseDrag = valueDrag(this, {
|
|
38
|
+
track: () => this,
|
|
39
|
+
min: () => this.min ?? 0,
|
|
40
|
+
max: () => this.max ?? 100,
|
|
41
|
+
step: () => this.step ?? 1,
|
|
42
|
+
onValue: (v) => {
|
|
43
|
+
if (!Object.is(v, this.value ?? 0)) {
|
|
44
|
+
this.value = v;
|
|
45
|
+
this.emit('input');
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
disconnected() {
|
|
51
|
+
// Explicitly release the valueDrag binding so its closure is marked released — the slider-multi
|
|
52
|
+
// shape, adopted here by the TKT-0068 item 1 ruling (the connection AbortController removes the
|
|
53
|
+
// outer pointerdown listener, but `released = true` is a belt-and-suspenders guard against any
|
|
54
|
+
// edge case where the outer listener fires after abort). Idempotent: releasing twice is a no-op.
|
|
55
|
+
this._releaseDrag();
|
|
56
|
+
this._releaseDrag = () => { };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!customElements.get('ui-slider'))
|
|
60
|
+
customElements.define('ui-slider', UISliderElement);
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/* slider-multi.css — ui-slider-multi, the dual-thumb Range-class control stylesheet (decomp S2 · ADR-0042 · ADR-0041).
|
|
2
|
+
* Two clearly-SECTIONED blocks:
|
|
3
|
+
*
|
|
4
|
+
* [1] TOKEN BLOCK — `:where(ui-slider-multi)` (specificity 0,0,0) DECLARES the `--ui-slider-multi-*`
|
|
5
|
+
* chain from the colour roles (`--md-sys-color-{family}-{role}`) and the compact widget ramp
|
|
6
|
+
* (`--md-sys-compact-{size}` / ADR-0041). `[size]` repoints the box token (the widget ramp step);
|
|
7
|
+
* `[disabled]` mutes the rail/fill/thumb colours to the neutral muted family.
|
|
8
|
+
* No `color-mix` — colour opinions live in the token layer (ADR-0008).
|
|
9
|
+
* [2] STYLES BLOCK — `@scope (ui-slider-multi)` CONSUMES only `--ui-slider-multi-*` for its OWN
|
|
10
|
+
* tokens. The host is a block-level flex column that sizes to its content; the RAIL (.rail) is a
|
|
11
|
+
* thin horizontal bar inside the interactive-box-height container; the FILL (.fill) spans lo→hi
|
|
12
|
+
* using `--value-pct-lo`/`--value-pct-hi` (set by the JS geometry seam); each THUMB (.thumb) is
|
|
13
|
+
* a circle `box − 4px` = `box − 2×--md-sys-widget-inset` (ADR-0041 cl.3: the 2px-inset law) positioned
|
|
14
|
+
* absolutely on the rail by `left: calc(var(--value-pct-{lo|hi}) * 1%)`.
|
|
15
|
+
* `:focus-visible` ring (keyboard-only, ADR-0009). Unconditional transitions + reduced-motion
|
|
16
|
+
* (the Indicator/Range family pattern — NO :state(ready) gate; ADR-0042 LLD-C6). Forced-colors safe.
|
|
17
|
+
* Density-invariant (box/thumb scale with `--md-sys-compact-{size}`, never a raw px override here).
|
|
18
|
+
*
|
|
19
|
+
* Standalone stylesheet — linked via the host page / component-styles barrel (Phase 2); NEVER injected
|
|
20
|
+
* from slider-multi.ts (behaviour-only `.ts`, no runtime style injection — plan §2).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/* ════════════════ [1] TOKEN BLOCK — :where(ui-slider-multi) declares --ui-slider-multi-* ════════════════ */
|
|
24
|
+
|
|
25
|
+
:where(ui-slider-multi) {
|
|
26
|
+
/* Box geometry — the compact widget-box ramp step (ADR-0041; default size = md). `[size]` repoints.
|
|
27
|
+
The box drives the thumb size (box − 4px) and the host's interactive-area height. */
|
|
28
|
+
--ui-slider-multi-box: var(--md-sys-compact-md);
|
|
29
|
+
|
|
30
|
+
/* Rail colour — the unfilled background track (muted neutral outline, same as switch unchecked track). */
|
|
31
|
+
--ui-slider-multi-rail: var(--md-sys-color-neutral-outline-variant);
|
|
32
|
+
|
|
33
|
+
/* Fill colour — the lo→hi active range (primary, matching the single-slider convention). */
|
|
34
|
+
--ui-slider-multi-fill: var(--md-sys-color-primary);
|
|
35
|
+
|
|
36
|
+
/* Thumb colours — surface + border. The thumb is a bright circle outlined in primary. */
|
|
37
|
+
--ui-slider-multi-thumb: var(--md-sys-color-neutral-surface-brightest);
|
|
38
|
+
--ui-slider-multi-thumb-border: var(--md-sys-color-primary);
|
|
39
|
+
|
|
40
|
+
/* Focus ring — tunnelled through a component token so the full token chain is local.
|
|
41
|
+
The fleet ring constant (--md-sys-color-focus-ring) flows through here; override only this token
|
|
42
|
+
to remap the focus colour for this component (WHCM: --md-sys-color-focus-ring → Highlight). */
|
|
43
|
+
--ui-slider-multi-focus-ring: var(--md-sys-color-focus-ring);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* [size] → compact widget ramp repoint (ADR-0041: the LITERAL per-[scale] table in dimensions.css
|
|
47
|
+
re-tables --md-sys-compact-{sm,md,lg}; `[size]` on the host picks which step the slider box targets). */
|
|
48
|
+
:where(ui-slider-multi[size='sm']) { --ui-slider-multi-box: var(--md-sys-compact-sm); }
|
|
49
|
+
:where(ui-slider-multi[size='lg']) { --ui-slider-multi-box: var(--md-sys-compact-lg); }
|
|
50
|
+
|
|
51
|
+
/* [disabled] → muted neutral roles (overrides all states). HOLDS at idle: the disabled host is
|
|
52
|
+
pointer-inert (styles block), so `:hover` never matches it (ADR-0008 disabled pattern). */
|
|
53
|
+
:where(ui-slider-multi[disabled]) {
|
|
54
|
+
--ui-slider-multi-rail: var(--md-sys-color-neutral-surface-high);
|
|
55
|
+
--ui-slider-multi-fill: var(--md-sys-color-neutral-surface-high);
|
|
56
|
+
--ui-slider-multi-thumb: var(--md-sys-color-neutral-on-surface-variant);
|
|
57
|
+
--ui-slider-multi-thumb-border: var(--md-sys-color-neutral-outline-variant);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* ════════════════ [2] STYLES BLOCK — @scope (ui-slider-multi) consumes ONLY --ui-slider-multi-* ════════════════ */
|
|
61
|
+
|
|
62
|
+
@scope (ui-slider-multi) {
|
|
63
|
+
/* Host: block container that centres the rail vertically within the interactive box height.
|
|
64
|
+
display:flex + align-items:center means the rail aligns to the middle of the box,
|
|
65
|
+
and the absolutely-positioned thumbs extend out from the rail's midline. */
|
|
66
|
+
:scope {
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
/* A range slider is a HORIZONTAL track — without an intrinsic width it collapses to the thumbs (dots)
|
|
70
|
+
in a shrink-wrapping flex/inline context (ui-text-field #74 failure class). Floor the inline-size. */
|
|
71
|
+
min-inline-size: var(--ui-slider-multi-min-inline-size, 12rem);
|
|
72
|
+
block-size: var(--ui-slider-multi-box); /* interactive box = compact widget ramp step (ADR-0041) */
|
|
73
|
+
position: relative;
|
|
74
|
+
-webkit-user-select: none;
|
|
75
|
+
user-select: none;
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
/* The host is the outer sizing container for left:X% on the thumbs (via .rail containing block). */
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* RAIL (.rail) — the thin horizontal track bar. Fills the full inline width of the host. Position:
|
|
81
|
+
relative makes the rail the containing block for the fill and thumb absolute children.
|
|
82
|
+
block-size = box/4 ≈ 4px at md scale — the slot height, not the interactive area. */
|
|
83
|
+
.rail {
|
|
84
|
+
position: relative;
|
|
85
|
+
flex: 1;
|
|
86
|
+
block-size: calc(var(--ui-slider-multi-box) / 4);
|
|
87
|
+
border-radius: 999px; /* pill — fully rounded ends */
|
|
88
|
+
background: var(--ui-slider-multi-rail);
|
|
89
|
+
overflow: visible; /* thumbs extend beyond the rail's block-size */
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* FILL (.fill) — the lo→hi active range highlight. Absolutely positioned within the rail.
|
|
93
|
+
* inset-block: 0 stretches it to the full rail height; inset-inline-start + inline-size
|
|
94
|
+
* map the [lo, hi] interval:
|
|
95
|
+
* left edge = --value-pct-lo × 1% (of the rail width)
|
|
96
|
+
* right edge = (100 − --value-pct-hi) × 1% from the end
|
|
97
|
+
*
|
|
98
|
+
* Transitions: the fill endpoints animate on value change (unconditional; the Indicator/Range
|
|
99
|
+
* pattern — no :state(ready) gate, ADR-0042 LLD-C6). Suppressed under prefers-reduced-motion. */
|
|
100
|
+
.fill {
|
|
101
|
+
position: absolute;
|
|
102
|
+
inset-block: 0;
|
|
103
|
+
inset-inline-start: calc(var(--value-pct-lo, 0) * 1%);
|
|
104
|
+
/* inline-size spans from lo% to hi% of the rail */
|
|
105
|
+
inline-size: calc((var(--value-pct-hi, 100) - var(--value-pct-lo, 0)) * 1%);
|
|
106
|
+
background: var(--ui-slider-multi-fill);
|
|
107
|
+
border-radius: 999px;
|
|
108
|
+
pointer-events: none; /* the fill is purely decorative — thumbs capture pointer */
|
|
109
|
+
transition:
|
|
110
|
+
inset-inline-start var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
|
|
111
|
+
inline-size var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
|
|
112
|
+
background-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* THUMBS (.thumb) — circles inset 2px (--md-sys-widget-inset) inside the widget box (ADR-0041 cl.3).
|
|
116
|
+
*
|
|
117
|
+
* Size: thumb = box − 2×inset = box − 4px (the ADR-0041 2px-inset law).
|
|
118
|
+
* Block: top:50% + transform:translateY(-50%) centres the thumb on the rail's midline.
|
|
119
|
+
* Inline: left is set per data-thumb (lo/hi) using the --value-pct-* geometry custom property:
|
|
120
|
+
* `left: calc(var(--value-pct-{thumb}) * 1%)`
|
|
121
|
+
* translateX(-50%) on the transform centres the circle on the computed position.
|
|
122
|
+
*
|
|
123
|
+
* Motion: `translate` / `left` transitions animate the thumb slide — paint-only transforms.
|
|
124
|
+
* Suppressed under prefers-reduced-motion. */
|
|
125
|
+
.thumb {
|
|
126
|
+
position: absolute;
|
|
127
|
+
|
|
128
|
+
/* ADR-0041 cl.3: thumb = box − 2×inset = box − 4px */
|
|
129
|
+
--ui-slider-multi-thumb-size: calc(var(--ui-slider-multi-box) - 2 * var(--md-sys-widget-inset));
|
|
130
|
+
inline-size: var(--ui-slider-multi-thumb-size);
|
|
131
|
+
block-size: var(--ui-slider-multi-thumb-size);
|
|
132
|
+
border-radius: 50%; /* circle */
|
|
133
|
+
box-sizing: border-box;
|
|
134
|
+
|
|
135
|
+
top: 50%;
|
|
136
|
+
transform: translateY(-50%) translateX(-50%);
|
|
137
|
+
|
|
138
|
+
border: 2px solid var(--ui-slider-multi-thumb-border);
|
|
139
|
+
background: var(--ui-slider-multi-thumb);
|
|
140
|
+
cursor: pointer;
|
|
141
|
+
|
|
142
|
+
transition:
|
|
143
|
+
left var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
|
|
144
|
+
background-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard),
|
|
145
|
+
border-color var(--md-sys-motion-duration-fast) var(--md-sys-motion-easing-standard);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Per-thumb inline position — driven by the JS geometry seam (--value-pct-lo / --value-pct-hi set via
|
|
149
|
+
element.style on the host; they cascade into this @scope as inherited custom properties). */
|
|
150
|
+
.thumb[data-thumb='lo'] { left: calc(var(--value-pct-lo, 0) * 1%); }
|
|
151
|
+
.thumb[data-thumb='hi'] { left: calc(var(--value-pct-hi, 100) * 1%); }
|
|
152
|
+
|
|
153
|
+
/* Focus ring (ADR-0009) — shared fleet ring on keyboard-focused thumbs. Applied via :focus-visible
|
|
154
|
+
(keyboard-only) directly on the thumb element since the thumbs ARE the focusable nodes.
|
|
155
|
+
Consumes the local --ui-slider-multi-focus-ring (declared in the token block, chained from
|
|
156
|
+
--md-sys-color-focus-ring) — the full token chain is now component-local, no direct fleet reference here. */
|
|
157
|
+
.thumb:focus-visible {
|
|
158
|
+
outline: var(--md-sys-state-focus-ring-width) solid var(--ui-slider-multi-focus-ring);
|
|
159
|
+
outline-offset: var(--md-sys-state-focus-ring-offset);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Reduced motion — zero all transitions on the fill and thumbs (accessibility non-negotiable;
|
|
163
|
+
geometry.md: NEVER animate the ramp; the Indicator/Range unconditional-transitions contract). */
|
|
164
|
+
@media (prefers-reduced-motion: reduce) {
|
|
165
|
+
.fill { transition: none; }
|
|
166
|
+
.thumb { transition: none; }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* Disabled host — pointer-inert so :hover/:active never lift the muted neutral tokens. */
|
|
170
|
+
:scope[disabled] {
|
|
171
|
+
pointer-events: none;
|
|
172
|
+
cursor: default;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* forced-colors — keep the rail, fill, and thumbs visible as system-colour elements.
|
|
176
|
+
forced-color-adjust:none preserves our explicit system-colour mappings so the browser does not
|
|
177
|
+
re-override them (matches switch.css / checkbox.css pattern for the range family). */
|
|
178
|
+
@media (forced-colors: active) {
|
|
179
|
+
.rail {
|
|
180
|
+
border: 2px solid ButtonText;
|
|
181
|
+
background: Canvas;
|
|
182
|
+
forced-color-adjust: none;
|
|
183
|
+
}
|
|
184
|
+
.fill {
|
|
185
|
+
background: Highlight;
|
|
186
|
+
forced-color-adjust: none;
|
|
187
|
+
}
|
|
188
|
+
.thumb {
|
|
189
|
+
border: 2px solid ButtonText;
|
|
190
|
+
background: ButtonFace;
|
|
191
|
+
forced-color-adjust: none;
|
|
192
|
+
}
|
|
193
|
+
.thumb:focus-visible {
|
|
194
|
+
outline-color: Highlight;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|