rangegroup proposal / Open UI Community Group

Open UI Proposal · Draft

The <rangegroup> element

A proof-of-concept for the proposed native multi-thumb range control. The baseline below renders with no custom CSS (approximates Chromium on macOS as an example for UA styles). Every other example demonstrates a different facet of the component’s API, constraints, and pseudo-element-based stylability. This is a proof-of-concept and the code at the moment is hypothetical.

Foundations

Baseline — native default

Two thumbs with no ::part overrides at all. The component’s built-in defaults aim for the Chromium-on-macOS input[type="range"] look: 16px blue thumbs, a 4px rounded track, and a blue fill between the thumbs.

Baseline price range
Show code

Foundations

Single thumb — group of one

A <rangegroup> with one child range still paints the composite slider: one thumb on the group track, not a hidden wrapper. Fill runs from the group minimum to that thumb.

Volume
Show code

Foundations

Empty group — no thumbs

A <rangegroup> with no child range renders an empty track: visible, not hidden and not disabled, with nothing to drag. Open UI resolved against having an empty group imply a default thumb, because that thumb would have no backing <input>, no IDL, and no submitted value. Adding inputs grows the same control from zero thumbs to one to many, which is what the buttons below do.

Stops
Show code

Foundations

Interactive price editor

Two-way binding between the component values and number inputs is author JavaScript — not a native reference API or built-in sibling fields. Linked value fields belong with a general data-binding / invoker discussion (#1459) and must also work for a plain input type=range. Drag the handles to update the fields, or type in the fields to move the handles.

Interactive price range
Show code

Foundations

Datalist integration

Pair the component with a <datalist> to snap values to discrete options. The component also enforces a minimum distance of 25 between handles via stepbetween="25".

Rating range
Show code

Foundations

Non-linear scale via datalist

Open UI resolved to first prototype non-linear scales with nothing more than a <datalist>, which is what a range input can already express, and to document where that runs out. Both controls below are the same filter band, 125 Hz–4 kHz over the same ten octaves, with the same octave-band spectrum drawn behind the track. The only difference is where a stop is placed: by its value, or by its position in the list. Audio is the honest test case, because no one has ever shipped a linear frequency control.

Stops placed by value — what the platform does today

The keyboard half works: focus a thumb and the arrow keys walk the octaves one at a time, which is exactly right. The pointer half does not. Because the track is linear and snapping picks the nearest stop by value, half of the audible spectrum lives in the first 3% of the track: 20 Hz through 500 Hz are a single smear you cannot aim at, while 8 kHz alone owns a third of the track. Drag the low cut and you can reach 1 kHz and up; everything below it is a pixel-hunt.

Filter band, stops placed by value

Stops placed by position — what an author has to fake

The same band and the same ten octaves, each given an equal stretch of track. Every stop is reachable by pointer, the spectrum is legible, and the band reads as a shape rather than a sliver. The catch is that the slider holds an octave index rather than a frequency: the real value exists only in script, so value, form submission, and validation all lose it. That is the cost the platform would remove.

Filter band, stops placed by position
Show code

Constraints and state

Stepbetween distance

stepbetween="10" enforces a minimum distance between adjacent handles, so they can never collide.

Stepbetween example
Show code

Constraints and state

Per-thumb constraints

Each thumb can have its own min and max. The thumb’s movement is restricted to the tighter of its own bounds and the overall component’s bounds.

Thumb constraints
Show code

Constraints and state

Parent override constraints

If a thumb’s bounds exceed the component’s range, the component clamps them. Here the track is 100–200 but the thumbs try to go 0–300.

Parent override constraints
Show code

Constraints and state

Multi-handle constraints

Three handles where the middle one is constrained to a small central zone (30–70), while the outer handles have full range and can push it.

Multi-handle constraints
Show code

Constraints and state

Fully disabled group

The disabled attribute disables the whole control. All thumbs become non-interactive and are removed from the tab order.

Fully disabled group
Show code

Constraints and state

Mixed disabled state

Individual thumbs can be disabled while the rest remain interactive. The button toggles the group-level disabled state to show that individual disabled flags persist on re-enable.

Mixed disabled
Group disabled: false
Show code

Interaction modes

Interaction mode (working name)

Compare endpoint-oriented vs range-oriented pointer behavior. The attribute interaction with keywords endpoints (default) and range is a working name for Open UI bikeshed on #1460 — not a committed HTML API. Do not use draggable. In range mode a track press never moves a single thumb: pressing between two thumbs drags that interval and keeps its width, and pressing outside it pulls the nearest interval to the pointer instead. Only dragging a thumb directly changes one end. Keyboard users get the same translate-with-width-preserved action via Shift+Arrow on a focused thumb (see #1511). The two keywords are deliberately different experiences so they can be compared in user testing, which is what the 10 September 2026 telecon resolved. It works with any thumb count — with three or more, the pointer picks which segment moves (see the segment-drag examples further down).

interaction (bikeshed)
Clip in and out points

Show code

Interaction modes

Video timeline trim

Drag the selected clip to slide in and out together while keeping duration. Pressing the timeline before or after the clip moves the clip there rather than trimming one end; only the in/out handles trim. interaction="range" is a working name pending bikeshed on #1460.

Clip in and out

Show code

Interaction modes

Meeting duration slot

A one-hour meeting on a workday calendar. Drag the block to a free slot without changing its length, or press anywhere on the day to send it there; the handles still resize. Busy times are shown behind the track. interaction="range" is a working name pending bikeshed on #1460.

Meeting time

Show code

Three-plus thumb patterns

Crossfade overlap editor

A 3-thumb pattern modeled on a DAW crossfade. The outer thumbs set when Track A starts fading out and when Track B reaches full volume; the middle thumb biases the crossover point that determines which track dominates the overlap.

Track A
Track B
Crossfade overlap editor
Start 2.0s
End 7.8s
Overlap 5.8s
A / B bias 50 / 50
Show code

Three-plus thumb patterns

Opening hours selector

Four handles define morning and afternoon shifts on a single 8 AM–10 PM timeline. The datalist provides hourly tick labels with half-hour granularity.

Opening hours
Morning shift 9:00 AM – 12:00 PM
Afternoon shift 1:00 PM – 5:00 PM
Show code

Three-plus thumb patterns

Opening hours selector (segment drag)

The same four-handle timeline with interaction="range". Pressing inside a shift drags that shift as a block: both of its bounding thumbs move together and the duration stays fixed, while the other two thumbs stay put. With more than two thumbs the pointer position is what decides which interval moves — the case the explainer previously called ambiguous. Pressing before or after all four thumbs pulls the closest shift to the pointer rather than jumping a thumb, and a dragged shift is clamped by its neighbours and by stepbetween.

Opening hours, draggable shifts
Morning shift 9:00 AM – 12:00 PM
Afternoon shift 1:00 PM – 5:00 PM
Show code

Three-plus thumb patterns

Multi-handle temperature

Three handles set boundaries for cold / mild / warm / hot zones, each segment rendered in its own colour and each thumb with a matching glow.

Multi-handle temperature
Cold -100° to -50°
Mild -50° to 0°
Warm 0° to 50°
Hot 50° to 100°
Show code

Three-plus thumb patterns

Three-handle process flow

Three handles and styled ticks represent the boundaries between phases in a process. Each major tick gets its own colour, signaling the phase it marks.

Process flow
Show code

Three-plus thumb patterns

Budget allocator

A complete visual transformation. Three thumbs split a 0–100 range into four colored budget bars, with category labels anchored to each segment via CSS Anchor Positioning. An aria-live region announces changes for assistive tech.

Budget allocator
Show code

Three-plus thumb patterns

Budget allocator (segment drag)

The same three-thumb split with interaction="range". Dragging one of the two interior bars shifts its share left or right without resizing it: the two thumbs bounding that bar move together, the categories on either side absorb the change, and the outer thumbs stay where they are. The first and last bars are bounded by a single thumb, so they are not draggable themselves; pressing there moves the closest interior bar to the pointer.

Budget allocator with draggable shares
Show code

Creative styling

Floating anchored tooltips

Invisible handles plus CSS Anchor Positioning makes external tooltips read as the draggable thumbs. The visible “handle” pills are just anchored DIVs.

Floating tooltips
Show code

Creative styling

Classic OS look

Heavy custom styling on all parts to recreate a classic Win-style slider, with anchored tooltips.

Classic look
Show code

Creative styling

Custom tick styling

Individual ticks and tick labels can be styled via CSS Parts. The 50% tick is highlighted with a different colour and height.

Custom tick styling
Show code

Creative styling

Histogram price filter

An Airbnb-style price filter. The histogram behind the slider previews item distribution; bars inside the active range light up in the brand accent. Pill thumbs with grip lines carry floating price tags via CSS Anchor Positioning.

Price range
$0 – $1,000 1,247items match
Show code

Creative styling

Star rating filter

A two-thumb 1–5 star rating filter. The five stars below the track act as labels and snap targets via list — stars inside the selected range fill in gold, the rest stay hollow.

Star rating filter
3 stars 5 stars
Show code

Creative styling

Color-stop gradient editor

Three color swatches author a linear-gradient(...) string. Each thumb's position becomes a gradient stop in the preview above and in the slider's own segments below. The CSS string updates live; preset chips drive the setRangeValue API.

Color stop positions
Presets
Show code

Creative styling

Styled timeline

Ticks dressed up as milestones on a project timeline. The start, middle, and end ticks are promoted with a distinct colour and height.

Styled timeline
Show code

Creative styling

Wavy track shape

The track is repainted as a wave and every thumb rides the curve — with no changes to the component. Each thumb exposes its position as --thumb-left (the proposed --slider-thumb-position), so an opt-in stylesheet can drive offset-path / offset-distance. Dragging and keyboard control are unchanged.

Wavy track values
Show code

Creative styling

Circular track shape

A two-thumb circular range dial, built entirely with opt-in CSS. Thumbs orbit the ring via offset-path + offset-distance: var(--thumb-left), and the filled arc is a conic-gradient driven by the segment’s exposed --segment-left / --segment-width. A tiny opt-in script maps pointer movement to angle so dragging feels truly circular. The value chips are purely presentational and intentionally omitted from the code sample. The component itself is unchanged.

Circular range dial
Show code

JavaScript API

Programmatic interaction

Use the component’s public methods and properties to get and set values from script.

JS API interaction
Show code

JavaScript API

Linked value fields (author JS)

Telecon outcome on #1459: linking a number field to a slider is not a <rangegroup>-only feature and must not be solved by putting number inputs in the group shadow tree. Both demos below use the same author-space input listeners — one against a native range, one against a single-thumb <range-group>.

Native input type=range

One-thumb <range-group>

Volume
Show code

JavaScript API

Add & remove thumbs (gradient builder)

A live gradient editor where each thumb is a color stop. addThumb(value, options) and removeThumb(index) grow and shrink the control on demand — new handles are real <input type="range"> elements, so they stay keyboard-accessible and form-friendly.

Gradient color stops
Show code