Hover fill
Also called fill hover effect, expanding background button, or Interactive Hover Button
A button whose background grows from a small dot or one edge on hover, flipping the text color while the label slides over to make room for an arrow.
import { useState, type ReactNode } from 'react';
import { motion, useReducedMotion } from 'motion/react';
const EASE = [0.23, 1, 0.32, 1] as const;
// The dot's slot and the arrow's slot are the same width, so the label slides
// exactly one slot and the button never changes size.
const SLOT = 26;
export function HoverFillButton({
children,
onClick
}: {
children: ReactNode;
onClick?: () => void;
}) {
const [active, setActive] = useState(false);
const reduceMotion = useReducedMotion();
const transition = reduceMotion
? { duration: 0.15 }
: { duration: 0.45, ease: EASE };
const slide = active && !reduceMotion;
return (
<button
type='button'
onClick={onClick}
onPointerEnter={(event) => setActive(event.pointerType === 'mouse')}
onPointerLeave={() => setActive(false)}
onFocus={(event) =>
setActive(event.currentTarget.matches(':focus-visible'))
}
onBlur={() => setActive(false)}
className='relative isolate flex h-14 items-center overflow-hidden rounded-full border px-7 text-lg font-medium outline-none focus-visible:ring-2 focus-visible:ring-ring'
>
{/* A circle centered on the dot, big enough to cover the farthest corner. */}
<motion.span
aria-hidden
className='absolute top-1/2 left-[33px] -z-10 -mt-[320px] -ml-[320px] size-[640px] rounded-full bg-foreground'
initial={false}
animate={
reduceMotion ? { opacity: active ? 1 : 0 } : { scale: active ? 1 : 0 }
}
transition={transition}
/>
<span aria-hidden className='shrink-0' style={{ width: SLOT }}>
<span className='block size-2.5 rounded-full bg-foreground' />
</span>
<motion.span
initial={false}
animate={{ x: slide ? -SLOT : 0 }}
transition={transition}
className={`transition-colors duration-200 ${active ? 'text-background' : 'text-foreground'}`}
>
{children}
</motion.span>
<motion.svg
viewBox='0 0 16 16'
aria-hidden
className='absolute top-1/2 right-7 -mt-2.5 size-5 text-background'
fill='none'
stroke='currentColor'
strokeWidth={1.8}
strokeLinecap='round'
initial={false}
animate={{ opacity: slide ? 1 : 0, x: slide ? 0 : -10 }}
transition={transition}
>
<path d='M3 8h10M9 4l4 4-4 4' />
</motion.svg>
</button>
);
}Make the "Start building" button a 56px-tall outlined pill with a 10px dark dot to the left of the label. On hover and on focus-visible, scale a dark circle up from the dot’s center until it covers the whole button (450ms, cubic-bezier(0.23, 1, 0.32, 1)), fade the label to white, slide it 26px left, and slide an arrow into the freed space on the right. Reverse it all on leave, clip the circle to the pill, keep the button width fixed, and switch only the colors with a 150ms fade when prefers-reduced-motion is set.
Paste into Layerwise or any AI coding tool.
What is Hover fill?
A hover fill starts with a quiet, outlined button and floods it with color when the pointer arrives. The fill is its own layer behind the label: a circle that scales up from a small dot, a bar that grows from one edge with scaleX, or a circle that starts where the pointer crossed the border. The text switches to the inverted color as the fill passes under it, and the label often shifts sideways while an arrow slides in.
It fits a single strong call to action on a landing page or portfolio. Size the circle so it covers the farthest corner, clip it with overflow: hidden and the button’s radius, and play the same motion in reverse on leave, so the button never snaps back. Keep the button’s width fixed while the label and arrow move, or the target shifts under the pointer.
Use about 300 to 500ms with an ease-out curve, give :focus-visible the same filled state, and check that both the outlined and the filled versions have enough contrast. Touch screens only see the press, so the resting button must be clear on its own. With reduced motion, swap the colors with a short fade and leave out the movement.
Often confused with
- Ripple effect
- A ripple spreads from the press point on click and fades away. A hover fill grows on hover and stays filled until the pointer leaves.
Related terms
All termsHover effect
A visual change, such as a lift, glow, or zoom, that happens when the mouse pointer moves over an element to show it can be clicked.
Button variants
A set of button styles, such as primary, secondary, outline, ghost, destructive, and link, that shows which action on a screen matters most.
Direction-aware transition
A transition that slides new content in from the side you are heading toward, so going forward and going back move in opposite directions.
Ripple effect
A circle that spreads out from the exact spot you tap or click on a button and then fades, made popular by Google's Material Design.
Build it with Layerwise
Describe your app, watch it run in a live preview, and publish it with a database, storage, and sign-in built in.