SVG Fill Animation
An SVG bottle whose gradient liquid rises to an adjustable level and drains again, on a loop — or rises once and stays at that level.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. SVG has 30 effects, including 3 free. Explore the category →
3 usage examples



How it works
The drawing fits a 100×120-unit SVG: the bottle outline (.flb-outline, 3-wide stroke), and a liquid rectangle (.flb-liquid) filled with a vertical indigo-to-magenta gradient, cut to the bottle's shape by a clipPath that reuses the same outline. The liquid is drawn after the outline, so it covers the inner half of it.
The flbFill animation moves the rectangle vertically: it starts 105 units lower (empty bottle) and rises to the level set by --flb-level (1 = full, 0.5 = half). It lasts --flb-speed (1.25 s, ease-in-out curve) and runs back and forth: the bottle fills then empties, 2.5 s per cycle. With --flb-repeat: 1, it fills once and stays at the chosen level. The gradient is attached to the liquid: halfway up, you mostly see its upper part.
The gradient and the clip need an id: with fixed ids, a second bottle on the page would pick up the first one's and lose its fill as soon as the first was hidden (tab, accordion). The short script therefore gives unique ids to every copy present at load. --flb-play: paused freezes the liquid wherever it is.
Accessibility
- Decorative: the SVG carries
aria-hidden="true". If it serves as a gauge, write the value as text next to it ("62%"): the bottle alone tells a screen reader nothing. - Reduced motion: with
prefers-reduced-motion: reduce, the animation is removed and the liquid shows straight away at its level (full by default) — measured in all three engines, 0 running animations. - No interaction: nothing to click or reach with the keyboard; the effect is the same with a mouse, a finger or a keyboard.
- Pace: one fill every 1.25 s, no flashing; for a long-running waiting indicator, a longer
--flb-speedstays calmer.
Browser compatibility
SVG, CSS and 0.9 KB of JavaScript. Reduced motion needs Chrome 74, Firefox 63 and Safari 10.1. Measured in Chromium 151, Firefox 153 and WebKit 26.5: 0 errors, nothing added to window, two copies each with its own ids, and the 2nd keeps its fill when the 1st is hidden (2% coloured pixels in both cases).
Without JavaScript, a single bottle per page works normally; from two on, they share the same ids. Without CSS animations, the bottle shows filled to its level.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0602-root">
<svg class="flb-svg" viewBox="0 0 100 120" aria-hidden="true">
<defs>
<linearGradient id="flb-grad" …>
<stop class="flb-stop-1" …/>
<stop class="flb-stop-2" …/>
</linearGradient>
<clipPath id="flb-clip"><path d="…"/></clipPath>
</defs>
<path class="flb-outline" d="…"/>
<g class="flb-inside" clip-path="url(#flb-clip)">
<rect class="flb-liquid" … fill="url(#flb-grad)"/>
</g>
</svg>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
--flb-level |
1 |
Level reached, from 0 (empty) to 1 (full): .62 for 62%. |
--flb-repeat |
infinite |
1: the bottle fills once and stays at its level; an even number: it ends empty. |
--flb-speed |
1.25s |
Length of one fill (a fill-and-drain cycle lasts twice as long). |
--flb-play |
running |
paused freezes the liquid at its current height. |
--flb-color-1 and --flb-color-2 |
#6366f1 and #d946ef |
Colours of the bottom and the top of the liquid. |
--flb-line and --flb-size |
#6366f1 and 100px |
Outline colour and drawing width (the height follows, 1.2 times the width). |
--flb-bg and --flb-min-h |
#0a0a0f and 300px |
Frame background and minimum height; transparent and 0 inside a button or a card. |
FAQ
d of the .flb-outline outline and of the clipPath path with the same outline. Keep the shape between heights 10 and 115 of the viewBox, otherwise the levels no longer land right.data-flb-ready). Run it again after inserting the new ones: it skips those already handled.--flb-color-1 and --flb-color-2 the same value.