Dual Range Slider
Two-thumb range selector for defining a value band
Size
Material
<div class="dual-range-wrap">
<div class="dual-range-track">
<div class="dual-range-fill" style="left:25%;width:40%"></div>
<div class="dual-range-thumb" style="left:25%"></div>
<div class="dual-range-thumb" style="left:65%"></div>
</div>
</div> .dual-range-wrap { display: flex; flex-direction: column; align-items: center; gap: 6px; width: 180px; }
.dual-range-track {
width: 100%; height: 6px; border-radius: 3px;
background: var(--bg-inset); border: 1px solid var(--border-subtle);
position: relative; box-shadow: var(--shadow-inset);
}
.dual-range-fill {
position: absolute; top: 0; height: 100%; border-radius: 2px;
background: var(--blue-info);
}
.dual-range-thumb {
position: absolute; top: 50%; width: 14px; height: 14px;
border-radius: 50%; transform: translate(-50%,-50%);
background: linear-gradient(155deg, var(--bg-surface), var(--bg-panel));
box-shadow: 0 1px 3px rgba(0,0,0,0.3), inset 0 1px 0 var(--glossy-hi);
cursor: grab; z-index: 2;
} API
| Class | Type | Description |
|---|---|---|
.dual-range-track | Base | Primary component class |
.md | Size | Medium (default) variant |
.glossy | Material | Glossy surface variant |
Design Notes
Physical Analog
Reference devices: Parametric EQ frequency range selectors, audio compressor attack/release range, frequency crossover points. Mechanism: Two independent fader wiper contacts on a single resistive track, defining a range (low/high bound). Fill between thumbs represents selected range. Rare in physical hardware -- UI innovation compressing two controls into one.
Geometry
| Property | Value |
|---|---|
| Track | 100% width x 6px height |
| Thumbs | 14px diameter circles (two of them) |
| Fill | Blue, positioned between thumbs |
| Container width | 180px |
CSS Recipe
Wrapper
.dual-range-wrap { display: flex; flex-direction: column; align-items: center; gap: 6px; width: 180px; }
Track
.dual-range-track {
width: 100%; height: 6px; border-radius: 3px;
background: var(--bg-inset); border: 1px solid var(--border-subtle);
position: relative; box-shadow: var(--shadow-inset);
}
Fill (Range Between Thumbs)
.dual-range-fill {
position: absolute; top: 0; height: 100%; border-radius: 2px;
background: var(--blue-info);
}
Thumbs
.dual-range-thumb {
position: absolute; top: 50%; width: 14px; height: 14px;
border-radius: 50%; transform: translate(-50%,-50%);
background: linear-gradient(155deg, var(--bg-surface), var(--bg-panel));
box-shadow: 0 1px 3px rgba(0,0,0,0.3), inset 0 1px 0 var(--glossy-hi);
cursor: grab; z-index: 2;
}
HTML Structure
<div class="dual-range-wrap">
<div class="dual-range-track">
<div class="dual-range-fill" style="left:25%;width:40%"></div>
<div class="dual-range-thumb" style="left:25%"></div>
<div class="dual-range-thumb" style="left:65%"></div>
</div>
</div>
Size Variants
No explicit size variants.
Material Variants
- Track: Recessed panel
- Thumbs: Glossy panel surface
- Fill: Solid blue
Theme Behavior
- Track and thumbs adapt via tokens
- Blue fill is fixed
Constraints
- Two thumbs MUST be independently draggable.
- Fill MUST span between the two thumbs (set via
leftandwidth). - Thumbs MUST NOT cross each other (low bound cannot exceed high bound).
- Both thumbs use identical styling.
Accessibility
- Each thumb should have
role="slider"with separatearia-valuenow aria-labelshould distinguish "Range minimum" and "Range maximum"- Keyboard: Arrow keys to adjust focused thumb
- Requires JS for dual drag interaction
♿
Accessibility
- Element
- Use semantic HTML with appropriate ARIA roles
- Keyboard
- Arrow keys to adjust, Home/End for min/max
- Focus
-
Visible focus indicator required. Use native browser focus ring or custom
:focus-visiblestyles.