Atmosphere✨ Premium

Liquid Button

Pure-CSS liquid fill: a circular pseudo-element expands from center on hover, turning the button's color into a fluid wave — zero JavaScript.

CSSHover

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Atmosphere has 57 effects, including 10 free. Explore the category →

3 usage examples

Hero / Landing — Primary CTA on a SaaS landing page — Liquid Button example 1

① Hero / Landing — Primary CTA on a SaaS landing page

WhenThe main call-to-action occupies the hero zone — 'Try for free', 'Get started', 'Book a demo'.
WhyThe fill animation draws the eye to the CTA without additional distraction. It signals interactivity on hover and reinforces trust before the click.
SettingsBackground #6366f1 (brand indigo), fill #8b5cf6 (complementary violet), padding --space-4:1.1rem; --space-8:2.4rem, radius --radius-lg:0.75rem, font-size 1rem bold.
Product component — 'Choose this plan' button on a pricing card — Liquid Button example 2

② Product component — 'Choose this plan' button on a pricing card

WhenPricing cards display several plans side by side — the user compares and hovers each button before choosing.
WhyThe liquid effect visually distinguishes the hovered plan and keeps attention on the active button in a dense interface.
SettingsBackground #0f172a (dark slate), fill #6366f1 (indigo) for the standard plan; fill #d946ef (fuchsia) for the Pro plan; transition .5s ease, circle size 200×200px for a compact button.
Conversion micro-interaction — Order confirmation button — Liquid Button example 3

③ Conversion micro-interaction — Order confirmation button

WhenThe user is at the end of a checkout or form flow — the 'Confirm', 'Pay', or 'Sign up' button must express the finality of the action.
WhyAn emerald green fill reinforces the 'Go' color convention and provides immediate visual feedback before the redirect, reducing perceived latency.
SettingsBackground #10b981 (emerald), fill #34d399 (light emerald), transition .4s ease, circle 300×300px, font-weight 700.

How it works

The effect relies entirely on a ::before pseudo-element positioned absolutely at the button's center (top:50%; left:50%; transform:translate(-50%,-50%)). At rest it measures 0×0 px and is invisible. On :hover, it grows to 300×300 px with border-radius:50%: the circle expands from the center, and its overflow is clipped by overflow:hidden on the parent button — creating the illusion of a liquid fill rising inside.

Two independent CSS transitions overlap: transition:.3s on .liquid-btn covers property changes on the button itself, while transition:.5s on the ::before controls the circle's expansion. The label text lives in a <span> with position:relative; z-index:1 to stay above the fill throughout the animation.

The effect writes nothing to the DOM, starts no requestAnimationFrame, and exposes no JavaScript API — it is pure declarative CSS. Modern browsers GPU-accelerate width/height transitions via compositing, keeping the animation smooth at negligible CPU cost. Padding and border-radius use custom properties (--space-4, --space-8, --radius-lg) defined on :root.

Accessibility

  • No prefers-reduced-motion in the code: the fill animation always plays on hover regardless of the OS motion setting. For sensitive users, add @media (prefers-reduced-motion:reduce) { .liquid-btn::before { transition:none } }.
  • The native <button> element is keyboard-focusable (Tab) and activatable with Enter or Space — no additional configuration needed.
  • No :focus-visible ring is defined in the code; the browser applies its default outline (may be barely visible on some OS/themes). Recommended: add .liquid-btn:focus-visible { outline:2px solid #818cf8; outline-offset:2px }.
  • White text on #6366f1 (indigo) contrast ratio ≈ 3.9:1 — passes WCAG AA for large text (≥ 18 pt) but fails for body-size text (< 18 pt). Adjust the background color or font weight if needed.
  • overflow:hidden on the button clips the pseudo-element precisely to the border radius — no visual leakage regardless of button size.

Browser compatibility

Requires only ::before, transition, border-radius, and overflow:hidden — available from CSS2/3 in all modern browsers.

Chrome 88+✓ Full
Firefox 87+✓ Full
Safari 14+✓ Full
Edge 88+✓ Full
Mobile iOS✓ Touch — hover on tap+hold
Android Chrome✓ Touch — hover on tap+hold

Without pseudo-element support (very old browsers), the button displays normally with its indigo background — no errors, no JavaScript involved.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<button class="liquid-btn">
  <span>Button label</span>
</button>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
background on .liquid-btn #6366f1 Initial button color. Replace with your brand color — the fill color should contrast with it.
background on .liquid-btn::before #d946ef Color of the liquid circle on hover. Choose a complementary or analogous color for a harmonious effect.
--radius-lg (CSS var on :root) 0.75rem Button border-radius. Set to 100px for a pill shape, 0 for a sharp rectangle.
--space-4 / --space-8 (CSS vars on :root) 1rem / 2rem Vertical / horizontal button padding. Increase for a hero CTA, reduce for a compact context.
transition on .liquid-btn::before .5s Fill expansion duration. .3s = snappy response, .8s = slow and dramatic reveal.
width / height on .liquid-btn:hover::before 300px Maximum diameter of the liquid circle. Increase to 400px or more if your button exceeds 250 px width — otherwise corners remain unfilled.
z-index on .liquid-btn span 1 Keeps the label above the fill. Do not go below 1 or the text will disappear during the animation.

FAQ

No. The effect is entirely CSS: a ::before pseudo-element, a transition on width and height, and overflow:hidden for clipping. No script is loaded, no npm dependency. Drop the CSS into any framework (React, Vue, Svelte) — it just works.
The code does not include a prefers-reduced-motion clause natively. Add this CSS block after the effect: @media (prefers-reduced-motion:reduce) { .liquid-btn::before { transition:none; width:100%; height:100%; border-radius:0; opacity:.15; } }. This disables the animation and shows a subtle static tint instead.
The width and height on .liquid-btn:hover::before are fixed at 300 px. If your button is wider (e.g. full-width), the circle won't cover the corners. Calculate the button's diagonal (Math.sqrt(w²+h²)) and use that value as the diameter. Alternatively, replace the width/height expansion with a transform approach: transform:translate(-50%,-50%) scale(4) on hover is more robust for variable-size buttons.