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.jsonPlayground
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.jsonOr, 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
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Button content (text and icons), which is also rendered as an inverted duplicate layer |
color | string | "#6366f1" | Liquid color (any valid CSS color value) |
fillTextColor | string | "#ffffff" | Text color once the liquid covers it |
idleLevel | number | 0.14 | Liquid level at rest (0 to 1) |
waveHeight | number | 10 | Wave amplitude (px); larger values mean higher crests |
waveDuration | number | 2.2 | Seconds for one cycle of the foreground wave; the background wave slows down and reverses automatically |
className | string | — | Forwarded and merged onto the button's root element |
...props | ButtonHTMLAttributes | — | All 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-hiddenandpointer-events-none, so assistive technology announces the button content only once - Focusing with
Tabtriggers the fill the same way, and thefocus-visiblering is kept as the focus indicator - The root element is a native
<button type="button">with full support fordisabled, form semantics, and every native attribute