Living Documentation

Accessible, token-driven sliders for ranges, volumes, and stepped controls

Sliders are ideal for compact numeric input with immediate feedback. This page demonstrates single-value, stepped, and dual-handle patterns with keyboard support and value preview.

Real-time feedback Min-max range Stepped controls Keyboard compatible
Volume 42 single slider value
Price Range 20 - 80 dual handle span
Brightness 60 step 10 increments
Interactions 0 input + change events

1. Slider Anatomy

Track, progress fill, thumb, value label, and helper text

Volume 42%

Use arrow keys for fine adjustments. Hold Shift+Arrow in some browsers for faster movement.

2. Design Rules

Guidelines for predictable slider UX

  • Always show current value next to the control.
  • Maintain a minimum hit area of 20px for the thumb.
  • Use step intervals for business values, not raw floats.
  • Provide bounds text when range meaning is not obvious.
  • Never hide disabled state; reduce contrast but keep visible.
  • Pair color with text so state is not color-only.

3. Slider Playground

Try single, dual, and stepped sliders with live values

Dual Range (Budget)
Min-Max $20 - $80

Handles are constrained so minimum cannot exceed maximum.

Stepped Slider (Brightness)
Step = 10 60%

Useful for coarse controls where precision is unnecessary.

4. Accessibility Notes

Keyboard and screen reader behavior

Input Behavior
Arrow Left/Right Decrease / increase by step
Home Jump to minimum
End Jump to maximum
Page Up/Down Larger jumps (browser dependent)
Screen reader Announces role, current value, min, max

5. Core Logic

Shared utility to paint progress and sync labels

slider-utils.js
function paint(slider) {
  const min = Number(slider.min || 0);
  const max = Number(slider.max || 100);
  const val = Number(slider.value);
  const pct = ((val - min) / (max - min)) * 100;
  slider.style.setProperty('--pct', pct + '%');
}

function bind(slider, out, fmt) {
  const sync = () => {
    paint(slider);
    out.textContent = fmt(slider.value);
  };
  slider.addEventListener('input', sync);
  slider.addEventListener('change', sync);
  sync();
}

6. Production Snippet

Drop-in HTML + CSS + JS starter for branded sliders

sliders-full.html
<label class="slider-label" for="s1">Volume</label>
<input id="s1" class="slider-control" type="range" min="0" max="100" value="40" />
<span id="s1Out" class="slider-value">40%</span>

<style>
.slider-control {
  --pct: 40%;
  appearance: none;
  width: 100%;
  height: 8px;
  border-radius: 999px;
  background: linear-gradient(to right, var(--brand) 0 var(--pct), #d7deea var(--pct) 100%);
}
.slider-control::-webkit-slider-thumb {
  appearance: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--brand);
  border: 2px solid #fff;
}
</style>

<script>
const s1 = document.getElementById('s1');
const o1 = document.getElementById('s1Out');
const sync = () => {
  const p = (Number(s1.value) / Number(s1.max)) * 100;
  s1.style.setProperty('--pct', p + '%');
  o1.textContent = s1.value + '%';
};
s1.addEventListener('input', sync);
sync();
</script>
© Lumora — Hand-crafted with ❤️ care & passion.
v 1.0.0
Copied.