A button half full of liquid, with two waves rolling through it in opposite directions while the surface bobs. Each wave is an inline SVG path twice the button width, sliding by half.
Also known as: css liquid button animation, water wave button, wave button css.
<button class="btn-liquid" type="button"> <svg class="btn-liquid-wave" viewBox="0 0 240 40" preserveAspectRatio="none" aria-hidden="true"><path d="M0 20 Q15 6 30 20 T60 20 T90 20 T120 20 T150 20 T180 20 T210 20 T240 20 V40 H0z"/></svg> <svg class="btn-liquid-wave" viewBox="0 0 240 40" preserveAspectRatio="none" aria-hidden="true"><path d="M0 20 Q15 6 30 20 T60 20 T90 20 T120 20 T150 20 T180 20 T210 20 T240 20 V40 H0z"/></svg> Stay hydrated </button>
.btn-liquid {
--c: var(--btn-color, #3b82f6);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5em;
padding: 0.75em 1.6em;
border: 0;
border-radius: 0.6em;
font: 600 var(--btn-size, 18px)/1.1 system-ui, sans-serif;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
position: relative;
isolation: isolate;
overflow: hidden;
width: 11.5em;
height: 3.2em;
padding: 0;
border: 2px solid var(--c);
border-radius: 2em;
background: color-mix(in srgb, var(--c) 22%, #0a0f1d);
color: #fff;
text-shadow: 0 0.05em 0.25em color-mix(in srgb, var(--c) 40%, #000);
transition: transform 0.2s ease;}
.btn-liquid-wave {
position: absolute;
bottom: -0.3em;
left: 0;
z-index: -1;
width: 200%;
height: calc(80% + 0.3em);
fill: var(--c);
animation:
btn-liquid-roll 2.8s linear infinite,
btn-liquid-bob 2.2s ease-in-out infinite alternate;
}
.btn-liquid-wave:first-child {
height: calc(96% + 0.3em);
fill: color-mix(in srgb, var(--c) 45%, transparent);
animation-duration: 4.2s, 2.8s;
animation-direction: reverse, alternate;
}
.btn-liquid:active { transform: scale(0.97); }
@keyframes btn-liquid-roll {
to { transform: translateX(-50%); }
}
@keyframes btn-liquid-bob {
from { translate: 0 0.12em; }
to { translate: 0 -0.12em; }
}
@media (prefers-reduced-motion: reduce) {
.btn-liquid, .btn-liquid *, .btn-liquid::before, .btn-liquid::after, .btn-liquid *::before, .btn-liquid *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--btn-color on the element or a parent.
Currently previewing #3b82f6.--btn-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 18px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.