Tailwind + JSMIT licensed

Tailwind CSS Liquid Glass Slider with Gooey Morphing Value Bubble

The iOS-style fluid control: the value bubble grows out of the track itself, fused by a gooey SVG filter, then floats over the thumb as you drag. Everything except the filter primitives is a utility — and the show condition is has-[input:active]:, so hover, drag, and keyboard focus all trigger the same morph.

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

<section class="trs-17 w-full min-h-svh grid place-items-center px-6 py-10 font-trs17-sans" id="trs-17-root" style="--v:.6">
  <svg class="absolute size-0" aria-hidden="true"><filter id="trs-17-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9"/></filter></svg>

  <article class="w-[min(370px,100%)] rounded-3xl isolate p-6 text-white bg-white/11 border border-white/35 backdrop-blur-[22px] saturate-180 shadow-[inset_0_1px_0_oklch(1_0_0/0.45),0_30px_60px_-30px_oklch(0_0_0/0.7)]">
    <header class="flex items-baseline justify-between gap-3 mb-2">
      <h3 class="text-base font-extrabold tracking-wide m-0">Display</h3>
      <span class="text-[11px] font-semibold text-white/60">Drag the thumb →</span>
    </header>

    <div class="mt-3.5">
      <div class="flex items-center justify-between gap-2 mb-3">
        <label for="trs-17-input" class="inline-flex items-center gap-1.5 text-[13px] font-semibold text-white/90"><span aria-hidden="true">☀︎</span> Brightness</label>
        <span class="text-sm font-extrabold tabular-nums min-w-[3ch] text-right" id="trs-17-out">60</span>
      </div>

      <div class="group/slider relative h-8 flex items-center has-[input:focus-visible]:outline-2 has-[input:focus-visible]:outline-offset-6 has-[input:focus-visible]:outline-white rounded-full">
        <div class="absolute inset-x-0 h-2 rounded-full bg-white/16 border border-white/25" aria-hidden="true"></div>
        <span class="absolute inset-0 [filter:url(#trs-17-goo)] pointer-events-none" aria-hidden="true">
          <span class="absolute left-0 top-1/2 -translate-y-1/2 h-2 rounded-full w-[calc(var(--v)*100%)] bg-gradient-to-r from-trs17-acc/60 to-trs17-acc shadow-[0_0_12px_-2px_var(--color-trs17-acc)]"></span>
          <span class="absolute top-1/2 left-[calc(var(--v)*100%)] -translate-x-1/2 -translate-y-1/2 grid place-items-center size-5.5 rounded-full text-[0px] text-[#3a2a05] font-extrabold bg-[radial-gradient(circle_at_35%_30%,#fff,var(--color-trs17-acc))] border border-white/60 shadow-[inset_0_2px_4px_oklch(1_0_0/0.9),inset_0_-3px_5px_oklch(0_0_0/0.18),0_6px_14px_oklch(0_0_0/0.35)] transition-[width,height,font-size] duration-250 ease-[cubic-bezier(0.34,1.6,0.5,1)] group-hover/slider:size-8 group-hover/slider:text-[12px] group-has-[input:active]/slider:size-8 group-has-[input:active]/slider:text-[12px] group-has-[input:focus-visible]/slider:size-8 group-has-[input:focus-visible]/slider:text-[12px] motion-reduce:transition-none" id="trs-17-bubble">60</span>
        </span>
        <input type="range" min="0" max="100" value="60" id="trs-17-input" aria-label="Brightness"
               class="absolute inset-0 w-full h-full m-0 appearance-none bg-transparent opacity-0 cursor-pointer z-10">
      </div>
    </div>
  </article>
</section>
@import "tailwindcss";

@theme {
  --color-trs17-acc: #f0b429;
  --color-trs17-acc: oklch(0.8 0.17 65);
  --font-trs17-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

@property --v {
  syntax: "<number>";
  inherits: true;
  initial-value: 0.6;
}

.trs-17 {
  transition: --v 75ms linear;
}

@media (prefers-reduced-motion: reduce) {
  .trs-17 {
    transition: none;
  }
}

.trs-17 {
  width: 100%;
  min-height: 100svh;
  background: radial-gradient(120% 120% at 70% 20%, #1e293b, #0f172a 70%);
}
/* The native thumb is a transparent hit target: the visible bubble is the
   glass span inside the gooey group. */

.trs-17 input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 32px;
  height: 32px;
  border-radius: 9999px;
  background: transparent;
  cursor: grab;
}

.trs-17 input[type="range"]:active::-webkit-slider-thumb {
  cursor: grabbing;
}

.trs-17 input[type="range"]::-moz-range-thumb {
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 9999px;
  background: transparent;
  cursor: grab;
}
(function(){
  var root = document.getElementById('trs-17-root');
  var inp = document.getElementById('trs-17-input');
  var bubble = document.getElementById('trs-17-bubble');
  var out = document.getElementById('trs-17-out');
  function update(){
    var v = +inp.value;
    root.style.setProperty('--v', v / 100);
    bubble.textContent = v;
    out.textContent = v;
    inp.setAttribute('aria-valuetext', v + ' percent brightness');
  }
  inp.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-trs17-acc: #f0b429;
  --color-trs17-acc: oklch(0.8 0.17 65);
  --font-trs17-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

@property --v {
  syntax: "<number>";
  inherits: true;
  initial-value: 0.6;
}

.trs-17 {
  transition: --v 75ms linear;
}

@media (prefers-reduced-motion: reduce) {
  .trs-17 {
    transition: none;
  }
}

.trs-17 {
  width: 100%;
  min-height: 100svh;
  background: radial-gradient(120% 120% at 70% 20%, #1e293b, #0f172a 70%);
}
/* The native thumb is a transparent hit target: the visible bubble is the
   glass span inside the gooey group. */

.trs-17 input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 32px;
  height: 32px;
  border-radius: 9999px;
  background: transparent;
  cursor: grab;
}

.trs-17 input[type="range"]:active::-webkit-slider-thumb {
  cursor: grabbing;
}

.trs-17 input[type="range"]::-moz-range-thumb {
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 9999px;
  background: transparent;
  cursor: grab;
}
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=Plus+Jakarta+Sans:wght@500;700;800&display=swap">

What changed in the port

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

  • The .slider:has(input:active) .bubble selector trio became group-has-[input:active]/slider: utilities on the bubble — same relational logic, stated on the element that morphs.
  • Size, position and label reveal are one transition on one element (transition-[width,height,font-size]), so the label cannot appear before the bubble has room for it.
  • UI upgrade: the focus-visible ring moved to the slider group via has-[input:focus-visible]:outline-2, so keyboard users get the same morph and a visible ring around the whole control rather than a bare input outline.
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 CSS Liquid Glass Slider with Gooey Morphing Value Bubble
Source: https://codefronts.com/components/tailwind-css-range-sliders/tailwind-liquid-glass-slider-gooey-value-bubble/

The iOS-style fluid control: the value bubble grows out of the track itself, fused by a gooey SVG filter, then floats over the thumb as you drag. Everything except the filter primitives is a utility — and the show condition is has-[input:active]:, so hover, drag, and keyboard focus all trigger the same morph.
## HTML
```html
<section class="trs-17 w-full min-h-svh grid place-items-center px-6 py-10 font-trs17-sans" id="trs-17-root" style="--v:.6">
  <svg class="absolute size-0" aria-hidden="true"><filter id="trs-17-goo"><feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b"/><feColorMatrix in="b" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 22 -9"/></filter></svg>

  <article class="w-[min(370px,100%)] rounded-3xl isolate p-6 text-white bg-white/11 border border-white/35 backdrop-blur-[22px] saturate-180 shadow-[inset_0_1px_0_oklch(1_0_0/0.45),0_30px_60px_-30px_oklch(0_0_0/0.7)]">
    <header class="flex items-baseline justify-between gap-3 mb-2">
      <h3 class="text-base font-extrabold tracking-wide m-0">Display</h3>
      <span class="text-[11px] font-semibold text-white/60">Drag the thumb →</span>
    </header>

    <div class="mt-3.5">
      <div class="flex items-center justify-between gap-2 mb-3">
        <label for="trs-17-input" class="inline-flex items-center gap-1.5 text-[13px] font-semibold text-white/90"><span aria-hidden="true">☀︎</span> Brightness</label>
        <span class="text-sm font-extrabold tabular-nums min-w-[3ch] text-right" id="trs-17-out">60</span>
      </div>

      <div class="group/slider relative h-8 flex items-center has-[input:focus-visible]:outline-2 has-[input:focus-visible]:outline-offset-6 has-[input:focus-visible]:outline-white rounded-full">
        <div class="absolute inset-x-0 h-2 rounded-full bg-white/16 border border-white/25" aria-hidden="true"></div>
        <span class="absolute inset-0 [filter:url(#trs-17-goo)] pointer-events-none" aria-hidden="true">
          <span class="absolute left-0 top-1/2 -translate-y-1/2 h-2 rounded-full w-[calc(var(--v)*100%)] bg-gradient-to-r from-trs17-acc/60 to-trs17-acc shadow-[0_0_12px_-2px_var(--color-trs17-acc)]"></span>
          <span class="absolute top-1/2 left-[calc(var(--v)*100%)] -translate-x-1/2 -translate-y-1/2 grid place-items-center size-5.5 rounded-full text-[0px] text-[#3a2a05] font-extrabold bg-[radial-gradient(circle_at_35%_30%,#fff,var(--color-trs17-acc))] border border-white/60 shadow-[inset_0_2px_4px_oklch(1_0_0/0.9),inset_0_-3px_5px_oklch(0_0_0/0.18),0_6px_14px_oklch(0_0_0/0.35)] transition-[width,height,font-size] duration-250 ease-[cubic-bezier(0.34,1.6,0.5,1)] group-hover/slider:size-8 group-hover/slider:text-[12px] group-has-[input:active]/slider:size-8 group-has-[input:active]/slider:text-[12px] group-has-[input:focus-visible]/slider:size-8 group-has-[input:focus-visible]/slider:text-[12px] motion-reduce:transition-none" id="trs-17-bubble">60</span>
        </span>
        <input type="range" min="0" max="100" value="60" id="trs-17-input" aria-label="Brightness"
               class="absolute inset-0 w-full h-full m-0 appearance-none bg-transparent opacity-0 cursor-pointer z-10">
      </div>
    </div>
  </article>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-trs17-acc: #f0b429;
  --color-trs17-acc: oklch(0.8 0.17 65);
  --font-trs17-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}

@property --v {
  syntax: "<number>";
  inherits: true;
  initial-value: 0.6;
}

.trs-17 {
  transition: --v 75ms linear;
}

@media (prefers-reduced-motion: reduce) {
  .trs-17 {
    transition: none;
  }
}

.trs-17 {
  width: 100%;
  min-height: 100svh;
  background: radial-gradient(120% 120% at 70% 20%, #1e293b, #0f172a 70%);
}
/* The native thumb is a transparent hit target: the visible bubble is the
   glass span inside the gooey group. */

.trs-17 input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 32px;
  height: 32px;
  border-radius: 9999px;
  background: transparent;
  cursor: grab;
}

.trs-17 input[type="range"]:active::-webkit-slider-thumb {
  cursor: grabbing;
}

.trs-17 input[type="range"]::-moz-range-thumb {
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 9999px;
  background: transparent;
  cursor: grab;
}
```

## JavaScript
```js
(function(){
  var root = document.getElementById('trs-17-root');
  var inp = document.getElementById('trs-17-input');
  var bubble = document.getElementById('trs-17-bubble');
  var out = document.getElementById('trs-17-out');
  function update(){
    var v = +inp.value;
    root.style.setProperty('--v', v / 100);
    bubble.textContent = v;
    out.textContent = v;
    inp.setAttribute('aria-valuetext', v + ' percent brightness');
  }
  inp.addEventListener('input', update);
  update();
})();
```

How this works

The goo is one filter. feGaussianBlur plus a high-contrast feColorMatrix turns two blurred shapes into one fused silhouette; put the fill and the bubble inside that filtered group and the bubble appears to be made of the same liquid as the track.

The input stays real. It sits on top at opacity-0, so arrows, Home/End and screen-reader announcements are untouched; the glass is decoration layered beneath.

One property drives both. --v sizes the fill and positions the bubble, and the bubble's numerals fade in with the size change so the label never appears before there is room for it.

Make it yours

  • Accent: --color-trs17-acc retints fill, bubble and glow.
  • Neck length: the filter's stdDeviation — higher fuses further, lower detaches sooner.
  • Always-on bubble: drop the has-[…] variants and keep the resting size at size-8.

Gotchas — read before shipping

  • The real input must stay ON TOP and opacity-0; moving it under the glass costs keyboard and screen-reader support.
  • The fill and the bubble must be inside the SAME filtered group, or they blur separately and never fuse.
  • SVG filter ids are document-global. This one is namespaced trs-17-goo precisely so two copies on a page cannot collide.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

The gooey morph is an SVG-filter enhancement over a native range input; where filters or :has() are unavailable it degrades to a plain glass bubble.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Theme tokensset in @theme, used by a utility

Plain CSSno Tailwind surface

Search CodeFronts

Loading…