Tailwind + JSMIT licensed

Tailwind Range Slider

A native type="range" with a painted fill and a live value chip. The script writes one custom property; the gradient and the chip both read it — so dragging touches a single number, not a stylesheet.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<div class="tif-24 grid min-h-svh w-full place-items-center bg-tif24-bg px-6 py-14 font-tif24-mono" aria-label="Range slider demo">
  <div class="grid w-full max-w-70 gap-2">
    <label for="tif24-input" class="flex items-center justify-between text-[10px] tracking-[0.12em] text-tif24-mut uppercase">
      Volume
      <output for="tif24-input" data-tif24-out class="rounded-full bg-tif24-acc px-1.75 py-0.75 text-[11px] font-bold tracking-normal text-tif24-ink">50</output>
    </label>
    <input id="tif24-input" type="range" name="tif24-volume" min="0" max="100" value="50" data-tif24
      class="h-1.5 w-full cursor-pointer appearance-none rounded-full bg-[linear-gradient(90deg,var(--color-tif24-acc)_0%,var(--color-tif24-acc)_var(--tif24-fill,50%),oklch(1_0_0/0.08)_var(--tif24-fill,50%),oklch(1_0_0/0.08)_100%)] outline-none focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tif24-acc [&::-moz-range-thumb]:size-4.5 [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:bg-white [&::-webkit-slider-thumb]:size-4.5 [&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-0 [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-[0_2px_6px_oklch(0.62_0.2_288/0.5)] [&::-webkit-slider-thumb]:transition-transform [&::-webkit-slider-thumb]:duration-150 [&::-webkit-slider-thumb:hover]:scale-112">
  </div>
</div>
@import "tailwindcss";

@theme {
  --color-tif24-bg: #12121a;
  --color-tif24-bg: oklch(0.19 0.015 285);
  --color-tif24-ink: #f0eeff;
  --color-tif24-ink: oklch(0.95 0.02 290);
  --color-tif24-mut: #b8b6d4;
  --color-tif24-mut: oklch(0.77 0.03 290);
  --color-tif24-acc: #7c6cff;
  --color-tif24-acc: oklch(0.62 0.2 288);
  --font-tif24-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tif-24 {
  width: 100%;
}

.tif-24 ::selection {
  background: var(--color-tif24-acc);
  color: #12121a;
}
(function () {
  document.querySelectorAll('.tif-24').forEach(function (root) {
    if (root.dataset.bound) return; root.dataset.bound = '1';
    root.querySelectorAll('[data-tif24]').forEach(function (input) {
      var out = root.querySelector('[data-tif24-out]');
      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('--tif24-fill', pct + '%');
        if (out) out.value = input.value;
      }
      input.addEventListener('input', update);
      update();
    });
  });
})();

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-tif24-bg: #12121a;
  --color-tif24-bg: oklch(0.19 0.015 285);
  --color-tif24-ink: #f0eeff;
  --color-tif24-ink: oklch(0.95 0.02 290);
  --color-tif24-mut: #b8b6d4;
  --color-tif24-mut: oklch(0.77 0.03 290);
  --color-tif24-acc: #7c6cff;
  --color-tif24-acc: oklch(0.62 0.2 288);
  --font-tif24-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tif-24 {
  width: 100%;
}

.tif-24 ::selection {
  background: var(--color-tif24-acc);
  color: #12121a;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • The value chip became an <output for> — the element that exists for exactly this, and which the original rendered as a span.
  • A focus-visible:outline was added: the original set outline: none on a keyboard-operable control with nothing in its place.
  • Both thumb pseudos are bracket variants sitting in the same class list, which is the first time the WebKit/Firefox split is visible at the element rather than fifty lines apart.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS demo 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: Tailwind Range Slider
Source: https://codefronts.com/components/tailwind-css-input-fields/tailwind-range-slider/

A native type="range" with a painted fill and a live value chip. The script writes one custom property; the gradient and the chip both read it — so dragging touches a single number, not a stylesheet.
## HTML
```html
<div class="tif-24 grid min-h-svh w-full place-items-center bg-tif24-bg px-6 py-14 font-tif24-mono" aria-label="Range slider demo">
  <div class="grid w-full max-w-70 gap-2">
    <label for="tif24-input" class="flex items-center justify-between text-[10px] tracking-[0.12em] text-tif24-mut uppercase">
      Volume
      <output for="tif24-input" data-tif24-out class="rounded-full bg-tif24-acc px-1.75 py-0.75 text-[11px] font-bold tracking-normal text-tif24-ink">50</output>
    </label>
    <input id="tif24-input" type="range" name="tif24-volume" min="0" max="100" value="50" data-tif24
      class="h-1.5 w-full cursor-pointer appearance-none rounded-full bg-[linear-gradient(90deg,var(--color-tif24-acc)_0%,var(--color-tif24-acc)_var(--tif24-fill,50%),oklch(1_0_0/0.08)_var(--tif24-fill,50%),oklch(1_0_0/0.08)_100%)] outline-none focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-tif24-acc [&::-moz-range-thumb]:size-4.5 [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-0 [&::-moz-range-thumb]:bg-white [&::-webkit-slider-thumb]:size-4.5 [&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-0 [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-[0_2px_6px_oklch(0.62_0.2_288/0.5)] [&::-webkit-slider-thumb]:transition-transform [&::-webkit-slider-thumb]:duration-150 [&::-webkit-slider-thumb:hover]:scale-112">
  </div>
</div>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tif24-bg: #12121a;
  --color-tif24-bg: oklch(0.19 0.015 285);
  --color-tif24-ink: #f0eeff;
  --color-tif24-ink: oklch(0.95 0.02 290);
  --color-tif24-mut: #b8b6d4;
  --color-tif24-mut: oklch(0.77 0.03 290);
  --color-tif24-acc: #7c6cff;
  --color-tif24-acc: oklch(0.62 0.2 288);
  --font-tif24-mono: 'JetBrains Mono', ui-monospace, monospace;
}

.tif-24 {
  width: 100%;
}

.tif-24 ::selection {
  background: var(--color-tif24-acc);
  color: #12121a;
}
```

## JavaScript
```js
(function () {
  document.querySelectorAll('.tif-24').forEach(function (root) {
    if (root.dataset.bound) return; root.dataset.bound = '1';
    root.querySelectorAll('[data-tif24]').forEach(function (input) {
      var out = root.querySelector('[data-tif24-out]');
      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('--tif24-fill', pct + '%');
        if (out) out.value = input.value;
      }
      input.addEventListener('input', update);
      update();
    });
  });
})();
```

How this works

One property, two readers. On input the handler computes the percentage and writes it once:

input.style.setProperty('--tif24-fill', pct + '%');
output.value = input.value;

The element stays native. Arrow keys, Page Up/Down, Home/End, screen-reader announcement of min, max and current value, and form serialisation all come free — which is the whole reason not to build a slider out of divs.

Make it yours

  • Bounds: min, max, step on the element; no JS change.
  • One-liner theme: replace the gradient with accent-tif24-acc and lose custom thumb sizing.
  • Thumb: [&::-webkit-slider-thumb]:size-4.5 and its Firefox twin — change both or one browser drifts.
  • Dual-thumb: overlay two inputs, pointer-events-none on the track and auto on the thumbs.

Gotchas — read before shipping

  • WebKit and Firefox thumb selectors cannot share a rule. An invalid selector in the list drops the whole thing — that is true in a bracket variant too.
  • accent-color and custom thumb sizing are mutually exclusive. Pick one per project.
  • A value chip is decoration for screen readers — the range input already announces its value, so keep the chip aria-hidden or use output as here.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

The manual gradient pattern works everywhere; accent-color is Chrome 93+, Safari 15.4+, Firefox 92+.

Techniques used in this demo

Theme tokensset in @theme, used by a utility

Search CodeFronts

Loading…