data-range
Cross-browser styled range slider with optional value bubble, prefix/suffix formatting, and tick markers. Covers data-range, data-bubble, and data-markers.
Overview
The data-range attribute enhances a native <input type="range"> with cross-browser styling, an optional value bubble, prefix/suffix formatting, and tick markers. This is a combined page covering data-range, data-bubble, and data-markers.
<label for="volume">Volume</label><input type="range" id="volume" data-range min="0" max="100" value="50">
How It Works
Add data-range to any <input type="range">. The init script:
- Wraps the input in a
.range-wrapperdiv for layout control - Normalizes cross-browser track and thumb styling via
appearance: none - Sets a
--range-pctCSS custom property (0–100%) on everyinputevent - If
data-bubbleis present, creates an<output>element positioned above the thumb - If
data-markersis present, renders tick marks along the track based on thestepattribute - If a
<datalist>is linked vialist, renders labeled tick marks from the option elements
The underlying input remains a real form control. It submits with the form and fires native input and change events.
Attributes
| Attribute | Type | Description |
|---|---|---|
data-range |
boolean | Enables cross-browser range styling and the --range-pct custom property. |
data-bubble |
boolean | Shows a floating value bubble above the thumb that updates on input. |
data-prefix |
string | Text prepended to the bubble value (e.g., "$"). |
data-suffix |
string | Text appended to the bubble value (e.g., "%", "°"). |
data-markers |
boolean | Renders tick marks along the track at each step interval. |
data-range-init |
boolean | Set automatically to prevent double-binding. Do not set manually. |
Value Bubble
Add data-bubble to display a floating value readout above the thumb. The bubble tracks the thumb position and updates in real time as the user drags.
<label for="brightness">Brightness</label><input type="range" id="brightness" data-range data-bubble min="0" max="100" value="75">
Prefix and Suffix
Use data-prefix and data-suffix to format the bubble value. The prefix appears before the number and the suffix after it.
<label for="price">Price</label><input type="range" id="price" data-range data-bubble data-prefix="$" min="0" max="500" value="150"> <label for="opacity">Opacity</label><input type="range" id="opacity" data-range data-bubble data-suffix="%" min="0" max="100" value="80">
Tick Markers
Add data-markers to render tick marks along the track. Ticks are generated at each step interval between min and max.
<label for="temp">Temperature</label><input type="range" id="temp" data-range data-bubble data-markers data-suffix="°" min="60" max="90" step="5" value="72">
Datalist Labels
Link a <datalist> via the native list attribute to render labeled tick marks. Each <option> in the datalist becomes a labeled marker on the track.
<label for="rating">Rating</label><input type="range" id="rating" data-range data-bubble min="1" max="5" step="1" value="3" list="rating-labels"><datalist id="rating-labels"> <option value="1" label="Poor"></option> <option value="2" label="Fair"></option> <option value="3" label="Good"></option> <option value="4" label="Great"></option> <option value="5" label="Excellent"></option></datalist>
With Form Field
Wrap in <form-field> for helper text and consistent spacing with other form controls.
<form-field> <label for="quality">Quality</label> <input type="range" id="quality" data-range data-bubble data-suffix="%" min="0" max="100" value="60"> <small slot="help">Adjust the encoding quality.</small></form-field>
CSS Custom Property
The --range-pct custom property is set on the input element and updated on every input event. Its value ranges from 0% to 100%, representing the thumb position relative to the track. Use it for filled-track effects or other dynamic styling.
/* The --range-pct custom property is updated on every input event */input[data-range] { --range-pct: 50%; /* default, updated dynamically */} /* Use it for a filled-track effect */input[data-range][data-range-init] { background: linear-gradient( to right, var(--color-primary) var(--range-pct), var(--color-border) var(--range-pct) );}
DOM Structure
After initialization, the following DOM structure is created:
.range-wrapper— layout container wrapping the input<output for="inputId">— the value bubble (only withdata-bubble).range-markers— tick mark container (only withdata-markers).range-labels— label container (only with a linked<datalist>)
Styling
The track and thumb are fully customizable via CSS custom properties. All styles are gated on [data-range-init] so the input renders normally without JavaScript.
/* Track styling */input[data-range][data-range-init] { --range-track-height: 0.375rem; --range-track-bg: var(--color-border); --range-track-radius: var(--radius-pill);} /* Thumb styling */input[data-range][data-range-init] { --range-thumb-size: 1.25rem; --range-thumb-bg: var(--color-primary); --range-thumb-border: 2px solid white; --range-thumb-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);} /* Bubble styling */.range-bubble { --range-bubble-bg: var(--color-surface-raised); --range-bubble-color: var(--color-text); --range-bubble-radius: var(--radius-s); --range-bubble-padding: var(--size-3xs) var(--size-xs);}
Dynamic Elements
Range inputs added to the DOM after page load are automatically enhanced via a MutationObserver. No manual initialization is needed.
<section> <h2>Accessibility</h2> <ul> <li>The <code><output for="inputId"></code> element semantically links the bubble to the input for screen readers</li> <li>Native keyboard support is preserved — arrow keys adjust the value, Home/End jump to min/max</li> <li>A visible <code><label></code> is required for the input</li> <li>Tick labels from <code><datalist></code> provide additional context for sighted users</li> <li><code>prefers-reduced-motion</code>: bubble transitions are disabled when reduced motion is preferred</li> <li>Without JavaScript, the range input renders as a standard browser slider — progressive enhancement</li> </ul> </section>