28 CSS Input Fields24 / 28
Range Slider Pro
A native <input type="range"> with custom track/thumb styling and a live value bubble that follows the thumb. Honest accessibility — keyboard arrows, screen-reader announcement, real form value.
Published
The code
<label class="if-24" for="if-24-input">
<span class="if-24-label">Volume <span class="if-24-bubble" data-if-bubble>50</span></span>
<input
id="if-24-input"
type="range"
name="volume"
min="0"
max="100"
value="50"
data-if-24
/>
</label><label class="if-24" for="if-24-input">
<span class="if-24-label">Volume <span class="if-24-bubble" data-if-bubble>50</span></span>
<input
id="if-24-input"
type="range"
name="volume"
min="0"
max="100"
value="50"
data-if-24
/>
</label>.if-24 {
display: grid;
gap: 8px;
width: 100%;
max-width: 280px;
}
.if-24-label {
display: flex;
justify-content: space-between;
align-items: center;
font-family: "JetBrains Mono", monospace;
font-size: 10px;
letter-spacing: 0.12em;
color: #b8b6d4;
text-transform: uppercase;
}
.if-24-bubble {
font: 700 11px/1 "JetBrains Mono", monospace;
color: #f0eeff;
background: #7c6cff;
padding: 3px 7px;
border-radius: 999px;
letter-spacing: 0;
}
.if-24 input {
width: 100%;
height: 6px;
-webkit-appearance: none;
appearance: none;
background: linear-gradient(
90deg,
#7c6cff 0%,
#7c6cff var(--if-24-fill, 50%),
rgba(255, 255, 255, 0.08) var(--if-24-fill, 50%),
rgba(255, 255, 255, 0.08) 100%
);
border-radius: 999px;
outline: none;
cursor: pointer;
}
.if-24 input::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
background: #fff;
border: 0;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(124, 108, 255, 0.5);
cursor: pointer;
transition: transform 0.15s;
}
.if-24 input::-webkit-slider-thumb:hover {
transform: scale(1.12);
}
.if-24 input::-moz-range-thumb {
width: 18px;
height: 18px;
background: #fff;
border: 0;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(124, 108, 255, 0.5);
cursor: pointer;
}.if-24 {
display: grid;
gap: 8px;
width: 100%;
max-width: 280px;
}
.if-24-label {
display: flex;
justify-content: space-between;
align-items: center;
font-family: "JetBrains Mono", monospace;
font-size: 10px;
letter-spacing: 0.12em;
color: #b8b6d4;
text-transform: uppercase;
}
.if-24-bubble {
font: 700 11px/1 "JetBrains Mono", monospace;
color: #f0eeff;
background: #7c6cff;
padding: 3px 7px;
border-radius: 999px;
letter-spacing: 0;
}
.if-24 input {
width: 100%;
height: 6px;
-webkit-appearance: none;
appearance: none;
background: linear-gradient(
90deg,
#7c6cff 0%,
#7c6cff var(--if-24-fill, 50%),
rgba(255, 255, 255, 0.08) var(--if-24-fill, 50%),
rgba(255, 255, 255, 0.08) 100%
);
border-radius: 999px;
outline: none;
cursor: pointer;
}
.if-24 input::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
background: #fff;
border: 0;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(124, 108, 255, 0.5);
cursor: pointer;
transition: transform 0.15s;
}
.if-24 input::-webkit-slider-thumb:hover {
transform: scale(1.12);
}
.if-24 input::-moz-range-thumb {
width: 18px;
height: 18px;
background: #fff;
border: 0;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(124, 108, 255, 0.5);
cursor: pointer;
}// Range Slider Pro — sync the live value bubble + paint the gradient fill via custom property
document.querySelectorAll("[data-if-24]").forEach(function (input) {
var bubble = input.closest(".if-24").querySelector("[data-if-bubble]");
function update() {
var min = Number(input.min) || 0;
var max = Number(input.max) || 100;
var pct = ((Number(input.value) - min) / (max - min)) * 100;
input.style.setProperty("--if-24-fill", pct + "%");
if (bubble) bubble.textContent = String(input.value);
}
input.addEventListener("input", update);
update();
});// Range Slider Pro — sync the live value bubble + paint the gradient fill via custom property
document.querySelectorAll("[data-if-24]").forEach(function (input) {
var bubble = input.closest(".if-24").querySelector("[data-if-bubble]");
function update() {
var min = Number(input.min) || 0;
var max = Number(input.max) || 100;
var pct = ((Number(input.value) - min) / (max - min)) * 100;
input.style.setProperty("--if-24-fill", pct + "%");
if (bubble) bubble.textContent = String(input.value);
}
input.addEventListener("input", update);
update();
});Here's a working CSS Input Field from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Range Slider Pro
Source: https://codefronts.com/components/css-input-fields/range-slider-pro/
A native <input type="range"> with custom track/thumb styling and a live value bubble that follows the thumb. Honest accessibility — keyboard arrows, screen-reader announcement, real form value.
## HTML
```html
<label class="if-24" for="if-24-input">
<span class="if-24-label">Volume <span class="if-24-bubble" data-if-bubble>50</span></span>
<input
id="if-24-input"
type="range"
name="volume"
min="0"
max="100"
value="50"
data-if-24
/>
</label>
```
## CSS
```css
.if-24 {
display: grid;
gap: 8px;
width: 100%;
max-width: 280px;
}
.if-24-label {
display: flex;
justify-content: space-between;
align-items: center;
font-family: "JetBrains Mono", monospace;
font-size: 10px;
letter-spacing: 0.12em;
color: #b8b6d4;
text-transform: uppercase;
}
.if-24-bubble {
font: 700 11px/1 "JetBrains Mono", monospace;
color: #f0eeff;
background: #7c6cff;
padding: 3px 7px;
border-radius: 999px;
letter-spacing: 0;
}
.if-24 input {
width: 100%;
height: 6px;
-webkit-appearance: none;
appearance: none;
background: linear-gradient(
90deg,
#7c6cff 0%,
#7c6cff var(--if-24-fill, 50%),
rgba(255, 255, 255, 0.08) var(--if-24-fill, 50%),
rgba(255, 255, 255, 0.08) 100%
);
border-radius: 999px;
outline: none;
cursor: pointer;
}
.if-24 input::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
background: #fff;
border: 0;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(124, 108, 255, 0.5);
cursor: pointer;
transition: transform 0.15s;
}
.if-24 input::-webkit-slider-thumb:hover {
transform: scale(1.12);
}
.if-24 input::-moz-range-thumb {
width: 18px;
height: 18px;
background: #fff;
border: 0;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(124, 108, 255, 0.5);
cursor: pointer;
}
```
## JavaScript
```js
// Range Slider Pro — sync the live value bubble + paint the gradient fill via custom property
document.querySelectorAll("[data-if-24]").forEach(function (input) {
var bubble = input.closest(".if-24").querySelector("[data-if-bubble]");
function update() {
var min = Number(input.min) || 0;
var max = Number(input.max) || 100;
var pct = ((Number(input.value) - min) / (max - min)) * 100;
input.style.setProperty("--if-24-fill", pct + "%");
if (bubble) bubble.textContent = String(input.value);
}
input.addEventListener("input", update);
update();
});
```Here's a working CSS Input Field from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Range Slider Pro
Source: https://codefronts.com/components/css-input-fields/range-slider-pro/
A native <input type="range"> with custom track/thumb styling and a live value bubble that follows the thumb. Honest accessibility — keyboard arrows, screen-reader announcement, real form value.
## HTML
```html
<label class="if-24" for="if-24-input">
<span class="if-24-label">Volume <span class="if-24-bubble" data-if-bubble>50</span></span>
<input
id="if-24-input"
type="range"
name="volume"
min="0"
max="100"
value="50"
data-if-24
/>
</label>
```
## CSS
```css
.if-24 {
display: grid;
gap: 8px;
width: 100%;
max-width: 280px;
}
.if-24-label {
display: flex;
justify-content: space-between;
align-items: center;
font-family: "JetBrains Mono", monospace;
font-size: 10px;
letter-spacing: 0.12em;
color: #b8b6d4;
text-transform: uppercase;
}
.if-24-bubble {
font: 700 11px/1 "JetBrains Mono", monospace;
color: #f0eeff;
background: #7c6cff;
padding: 3px 7px;
border-radius: 999px;
letter-spacing: 0;
}
.if-24 input {
width: 100%;
height: 6px;
-webkit-appearance: none;
appearance: none;
background: linear-gradient(
90deg,
#7c6cff 0%,
#7c6cff var(--if-24-fill, 50%),
rgba(255, 255, 255, 0.08) var(--if-24-fill, 50%),
rgba(255, 255, 255, 0.08) 100%
);
border-radius: 999px;
outline: none;
cursor: pointer;
}
.if-24 input::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
background: #fff;
border: 0;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(124, 108, 255, 0.5);
cursor: pointer;
transition: transform 0.15s;
}
.if-24 input::-webkit-slider-thumb:hover {
transform: scale(1.12);
}
.if-24 input::-moz-range-thumb {
width: 18px;
height: 18px;
background: #fff;
border: 0;
border-radius: 50%;
box-shadow: 0 2px 6px rgba(124, 108, 255, 0.5);
cursor: pointer;
}
```
## JavaScript
```js
// Range Slider Pro — sync the live value bubble + paint the gradient fill via custom property
document.querySelectorAll("[data-if-24]").forEach(function (input) {
var bubble = input.closest(".if-24").querySelector("[data-if-bubble]");
function update() {
var min = Number(input.min) || 0;
var max = Number(input.max) || 100;
var pct = ((Number(input.value) - min) / (max - min)) * 100;
input.style.setProperty("--if-24-fill", pct + "%");
if (bubble) bubble.textContent = String(input.value);
}
input.addEventListener("input", update);
update();
});
```How this works
A native type="range" input is styled by removing -webkit-appearance and painting the track as a hard-stop linear-gradient. The fill percentage is stored in a --if-24-fill custom property so JavaScript only writes one value; the gradient rereads it on every paint. On input events the handler recomputes ((value - min) / (max - min)) * 100 and mirrors the value into the bubble span.
Because the element is a real range input, keyboard arrows, Page Up/Down, screen-reader announcements, and form.submit serialization all work without extra code. For the dual-thumb range slider e-commerce price filter pattern used on Zillow, Redfin, Airbnb, and Vrbo, overlay two type="range" inputs and coordinate pointer-events so both thumbs stay draggable.
Make it yours
- Change
min,max,step, andvaluein HTML — no JS edit needed;step="5"for coarse tuning. - Swap the track gradient stops or replace with
accent-color: #7c6cfffor a one-line native theme. - Position the bubble above the thumb by translating
left: calc(var(--if-24-fill) - 12px)on an absolutely-positioned span. - Upgrade to dual-thumb: two overlaid inputs, one for min and one for max, clamped against each other in the input handler.
Gotchas — read before shipping
- Firefox and WebKit need separate thumb selectors (
::-moz-range-thumbvs::-webkit-slider-thumb) — you cannot group them in one rule. - For dual-thumb, the top input's
pointer-events: noneon the track pluspointer-events: autoon the thumb is the only reliable way to keep both handles draggable. accent-colorshortcuts styling but disables custom thumb sizing — pick one approach per project, not both.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 90+ | 14+ | 90+ | 90+ |
accent-color is chrome 93+, safari 15.4+, firefox 92+ — the manual gradient pattern shown here works everywhere.