WebberUI

Liquid Fill Button

On hover, liquid rises from the bottom with a wave to fill the button and the text inverts.

On hover or keyboard focus, liquid rises from the bottom of the button at a spring rhythm: the SVG wave's top edge flows in two layers, bubbles rise, and the text inverts in sync with the liquid surface via clip-path — a good fit for a prominent CTA button.

Loading preview…
npx shadcn@latest add https://webberui.com/r/liquid-fill-button.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

0.14
10
2.2
<LiquidFillButton />

Installation

npx shadcn@latest add https://webberui.com/r/liquid-fill-button.json

Or, once registries are configured in components.json, install it as @webberui/liquid-fill-button.

Usage

import { LiquidFillButton } from "@/components/ui/liquid-fill-button";

<LiquidFillButton color="#4f46e5" onClick={() => console.log("clicked")}>
  Get started now
</LiquidFillButton>

Props

PropTypeDefaultDescription
childrenReact.ReactNodeButton content (text and icons), which is also rendered as an inverted duplicate layer
colorstring"#6366f1"Liquid color (any valid CSS color value)
fillTextColorstring"#ffffff"Text color once the liquid covers it
idleLevelnumber0.14Liquid level at rest (0 to 1)
waveHeightnumber10Wave amplitude (px); larger values mean higher crests
waveDurationnumber2.2Seconds for one cycle of the foreground wave; the background wave slows down and reverses automatically
classNamestringForwarded and merged onto the button's root element
...propsButtonHTMLAttributesAll other native <button> attributes (onClick, disabled, and so on)

Accessibility

  • When the user has "reduce motion" enabled at the system level, the liquid level switches height directly without bouncing, and the waves and bubbles stop flowing
  • The inverted text layer and the liquid layer are both aria-hidden and pointer-events-none, so assistive technology announces the button content only once
  • Focusing with Tab triggers the fill the same way, and the focus-visible ring is kept as the focus indicator
  • The root element is a native <button type="button"> with full support for disabled, form semantics, and every native attribute

On this page