Testing a native vs custom range slider

A native <input type="range"> gives you a fully accessible slider for free. The moment you need to build a custom-styled version from scratch, because native range inputs can't be styled to the degree many designs require, every piece of that accessibility support has to be rebuilt by hand. This page shows both, side by side.

Example 1: native <input type="range">

Role, name, minimum, maximum, current value, keyboard interaction, and AT announcement are all handled automatically by the browser. Nothing below is ARIA, none of it is needed.

40

Expected result: Arrow keys, Home, End, and Page Up/Down should all work immediately, with no JavaScript. The <output> is a small, optional addition, purely visual, showing sighted users the numeric value, since a bare slider thumb position alone can be hard to read precisely. It has no effect on what's announced to AT, that comes from the input's own native state.

HTML markup
<label for="volume-native">Volume</label>
<input
  type="range"
  id="volume-native"
  min="0"
  max="100"
  value="40"
>
<output for="volume-native">40</output>

Example 2: custom slider, built from scratch

Starting point, before anything is resolved:

<div>Volume</div>
<div>
  <div></div>
</div>

Also worth noting: the native example's mouse and touch behaviour, dragging the thumb, clicking anywhere on the track to jump to that position, is entirely free with a native input. Both are rebuilt by hand below too.

Volume
40

Two plain, meaningless <div>s became the slider above. Everything the native input gave for free had to be deliberately resolved:

What needs to be resolved, and how
Question Resolved via
What kind of widget is this? role="slider"
What is its name? aria-labelledby, pointing at the visible "Volume" label
What is its lowest possible value? aria-valuemin="0"
What is its highest possible value? aria-valuemax="100"
What is its current value? aria-valuenow, updated by JavaScript on every change
How should the keyboard interact with it? Hand-written keydown handling: Arrow keys, Home, End, Page Up/Down
What should be passed to assistive technology when the value changes? Resolved: neither aria-live nor a hidden announcer element is needed. Real keyboard focus stays on the slider itself the entire time, unlike the character counter or timer examples elsewhere on this site, where a separate element was being updated. A focused widget's own value change should be announced automatically once aria-valuenow updates, worth confirming directly with your own AT set rather than assumed.

Expected result:

HTML markup
<span id="volume-custom-label">Volume</span>
<div class="slider-track" id="volume-track">
  <div class="slider-fill" id="volume-fill"></div>
  <div
    class="slider-thumb"
    id="volume-thumb"
    role="slider"
    tabindex="0"
    aria-labelledby="volume-custom-label"
    aria-valuemin="0"
    aria-valuemax="100"
    aria-valuenow="40"
  ></div>
</div>
<span id="volume-custom-output">40</span>

Worth testing directly: confirm with your standard screen reader and browser combinations that value changes are actually announced automatically while the thumb is focused, without any aria-live region. If any combination stays silent, that would be a genuinely useful, concrete finding, and would mean the "resolved" answer in the table above needs revisiting for that specific combination.