Liquid rises and settles just under the brim, where two wavy surfaces keep rolling past each other while you hover. The waves are tiled radial gradients, so there is no SVG.
Also known as: liquid button hover, wave fill button, water fill button.
<button class="hfx-liquid-wave-fill">Try it free</button>
.hfx-liquid-wave-fill {
--c: var(--hfx-color, #3b82f6);
position: relative;
isolation: isolate;
overflow: hidden;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5em;
padding: 0.75em 1.75em;
border-radius: 0.5em;
font: 600 var(--hfx-size, 16px)/1.2 system-ui, sans-serif;
white-space: nowrap;
cursor: pointer;
border: 2px solid var(--c);
background: transparent;
color: var(--c);
transition: color 0.3s ease 0.25s;
}
/* one wave = a crest arc above the midline, a trough cut below it, solid under both */
.hfx-liquid-wave-fill::before,
.hfx-liquid-wave-fill::after {
content: "";
position: absolute;
inset: 0.16em -2px -2px;
z-index: -1;
background:
radial-gradient(0.65em at 0.45em 0.65em, var(--tint) 97%, transparent) 0 0 / 1.8em 0.18em repeat-x,
radial-gradient(0.65em at 1.35em -0.47em, transparent 97%, var(--tint)) 0 0.18em / 1.8em 0.18em repeat-x,
linear-gradient(var(--tint) 0 0) 0 0.36em / 100% 100% no-repeat;
transform: translateY(104%);
transition: transform 0.7s cubic-bezier(0.3, 0.6, 0.3, 1);
}
.hfx-liquid-wave-fill::before { --tint: var(--c); }
.hfx-liquid-wave-fill::after {
--tint: color-mix(in srgb, var(--c) 40%, transparent);
top: 0;
z-index: -2;
background-position: 0.9em 0, 0.9em 0.18em, 0 0.36em;
}
.hfx-liquid-wave-fill:is(:hover, :focus-visible) { color: #fff; }
.hfx-liquid-wave-fill:is(:hover, :focus-visible)::before,
.hfx-liquid-wave-fill:is(:hover, :focus-visible)::after {
transform: translateY(0);
animation: hfx-liquid-wave-fill-flow 1.1s linear infinite;
}
.hfx-liquid-wave-fill:is(:hover, :focus-visible)::after {
transition-delay: 0.05s;
animation-name: hfx-liquid-wave-fill-back;
animation-duration: 1.6s;
}
@keyframes hfx-liquid-wave-fill-flow {
to { background-position: 1.8em 0, 1.8em 0.18em, 0 0.36em; }
}
@keyframes hfx-liquid-wave-fill-back {
to { background-position: -0.9em 0, -0.9em 0.18em, 0 0.36em; }
}
@media (prefers-reduced-motion: reduce) {
.hfx-liquid-wave-fill, .hfx-liquid-wave-fill *, .hfx-liquid-wave-fill::before, .hfx-liquid-wave-fill::after, .hfx-liquid-wave-fill *::before, .hfx-liquid-wave-fill *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--hfx-color on the element or a parent.
Currently previewing #3b82f6.--hfx-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 16px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.