Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
Cross-browser styled range slider with optional value bubble, prefix/suffix formatting, and tick markers. Covers data-range, data-bubble, and data-markers.
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">
Add data-range to any <input type="range">. The init script:
.range-wrapper div for layout controlappearance: none--range-pct CSS custom property (0–100%) on every input eventdata-bubble is present, creates an <output> element positioned above the thumbdata-markers is present, renders tick marks along the track based on the step attribute<datalist> is linked via list, renders labeled tick marks from the option elementsThe underlying input remains a real form control. It submits with the form and fires native input and change events.
| 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. |
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">
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">
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">
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>
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>
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) );}
After initialization, the following DOM structure is created:
.range-wrapper — layout container wrapping the input<output for="inputId"> — the value bubble (only with data-bubble).range-markers — tick mark container (only with data-markers).range-labels — label container (only with a linked <datalist>)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);}
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>