Fill Up
An ::after pseudo-element rises from bottom to top (height 0 → 100%) on hover with an ease-quart-out curve — the element fills vertically like a glass being filled; text stays readable via z-index.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Hover has 48 effects, including 5 free. Explore the category →
3 usage examples



How it works
The .fx-0355-root root centers a .hover-box.hover-fill-up block of 200 × 200 px. The element has a transparent background, a violet border and violet text at rest. An ::after pseudo-element positioned at bottom: 0; left: 0; width: 100% starts at height: 0 and rises to height: 100% on hover with a linear-gradient(135deg, #a78bfa, #8b5cf6) gradient.
The transition curve is cubic-bezier(0.65, 0, 0.35, 1) — ease-quart-out — which starts slowly then accelerates at the end, giving the impression that liquid arrives quickly at the top. The text transitions from violet to white at the same time (transition: color .6s). The z-index: -1 of ::after places it behind the span (z-index 1): text stays readable at all times.
Measured in Chromium 151: bottom-to-top fill, text color changes, two copies on a page — only the hovered one fills.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, transitions are cancelled; the fill and color change remain functional (the::afterjumps to 100% height and the text turns white instantly). The hover state stays readable. - Keyboard: the element is a natively focusable
<button>. The:focus-visiblerule triggers the fill and colour change; an indigo outline marks the focus state. - Contrast: the violet text (#a78bfa) on a dark background at rest has a 5.4:1 ratio (WCAG AA). On hover, white text on violet has a 7.8:1 ratio.
- Touch: the first touch triggers the fill; it stays in place until the next touch. On mobile, the filled state may persist after the finger lifts.
Browser compatibility
Pure CSS (::after, height, cubic-bezier, transition, :focus-visible, prefers-reduced-motion), no JavaScript. Measured in Chromium 151: fill visible, text readable, two independent copies. Works in all modern browsers.
Without transition, the fill appears instantly on hover. Without properly managed z-index, text may be hidden by the ::after — ensure the span has position: relative; z-index: 1.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0355-root">
<button class="hover-box hover-fill-up" type="button">
<span>…</span>
</button>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Fill color (CSS — background of ::after) |
linear-gradient(135deg, #a78bfa, #8b5cf6) |
Replace with a solid color (#10b981 green) or a different gradient. The fill color can match the brand identity. |
Fill curve (CSS — cubic-bezier(.65, 0, .35, 1)) |
ease-quart-out (accelerates at the end) | linear gives a steady fill; ease-in starts fast and slows down; cubic-bezier(.17, .67, .83, .67) adds a slight bounce. |
Duration (CSS — transition: height .6s) |
600 ms | .3s for a snappy fill; 1s for a slow, dramatic fill. |
Border (CSS — border of .hover-fill-up) |
2 px solid #a78bfa | Remove the border for a solid background at rest (background: var(--bg-card) + border: none); in that case, the fill changes the background from dark to vivid. |
Direction (CSS — ::after anchor) |
Bottom to top (bottom: 0) |
Anchor at top: 0; height: 0 → 100% (top to bottom) or left: 0; width: 0 → 100% (left to right, horizontal fill). |
FAQ
--fill: 60% on the element, then .hover-fill-up:hover::after { height: var(--fill) }. For size buttons, set the variable inline: style="--fill: 60%".span has position: relative; z-index: 1 and the ::after has z-index: -1. The text color transition (violet → white) accompanies the fill to maintain contrast..filled class on click: el.classList.toggle('filled'), then trigger the fill via .hover-fill-up.filled::after { height: 100% }.