← Back to gallery
CSS

Background-position Fill Sweep

An interaction fill effect that uses an oversized gradient background layer and transitions background-position on hover or focus-visible. The element keeps its size fixed while the color field sweeps across or up from one edge.

background-positionbackground-sizehoverfocus-visiblefill-sweepno-layout-shift

Interaction / background-position / fill sweep

Background-position Fill Sweep

Three state-driven fill sweeps that move an oversized gradient background layer inside the target: a bottom-rise inline link, a horizontal command button sweep, and a diagonal chip wash. The target box never changes size; only background-position and color move.

Bottom edge · vertical fill

Inline Link Rise

Bottom-to-top fill for inline links. The text metrics stay fixed while the background layer rises.

  • bottom rise
  • link focus
  • fixed line box

Left edge · horizontal sweep

Command Button Sweep

Left-to-right command fill. The sweep is background-position, not a translated overlay.

  • horizontal sweep
  • CTA
  • no overlay

Diagonal · angled wash

Diagonal Chip Wash

Diagonal chip wash using both x and y overscan so the angled band has room to travel.

  • diagonal wash
  • chip state
  • overscan

Fill sweep inspector

Inline Link Rise

hover/focus target
  • bottom rise
  • link focus
  • fixed line box

A text-like link fills from the underline edge upward. The background is taller than the line box, so moving background-position reveals the accent color without animating underline thickness or line-height.

Helped you ship something? 🐟 Send my cat a churu

/* Bottom-rise fill: the tall background layer moves upward behind a stable inline target. */
.fill-sweep {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-inline-size: 8rem;
  padding: 0.55rem 0.75rem;
  border: 1px solid color-mix(in srgb, #67e8f9 42%, transparent);
  border-radius: 999px;
  color: #c7d2fe;
  background-image: linear-gradient(0deg, #67e8f9 0 50%, rgba(8, 15, 28, 0.2) 50% 100%);
  background-size: 100% 210%;
  background-position: 0 0;
  background-repeat: no-repeat;
  transition:
    background-position 0.32s cubic-bezier(.2,.8,.2,1),
    color 0.32s ease;
}

.fill-sweep:is(:hover, :focus-visible) {
  color: #04111f;
  background-position: 0 100%;
}

.fill-sweep:focus-visible {
  outline: 2px solid #67e8f9;
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .fill-sweep {
    transition-duration: 0s;
    background-position: 0 100%;
  }
}

How to make this

A CSS fill sweep uses an oversized gradient background, then transitions background-position on hover and focus-visible while the target box stays the same size.

html
1<a class="fill-sweep" href="/docs">Read docs</a> <style>.fill-sweep {  display: inline-flex;  align-items: center;  justify-content: center;  min-inline-size: 10rem;  padding: 0.65rem 0.9rem;  border: 1px solid #2dd4bf;  border-radius: 999px;  color: #c7d2fe;  text-decoration: none;14  background-image: linear-gradient(    90deg,    #34d399 0 46%,    #67e8f9 50%,    #07111f 54% 100%  );20  background-size: 225% 100%;  background-position: 100% 0;  background-repeat: no-repeat;23  transition:    background-position .34s cubic-bezier(.2,.8,.2,1),    color .34s ease;} 28.fill-sweep:is(:hover, :focus-visible) {  color: #042014;  background-position: 0 0;} .fill-sweep:focus-visible {  outline: 2px solid #67e8f9;  outline-offset: 3px;} 38@media (prefers-reduced-motion: reduce) {  .fill-sweep {    transition-duration: 0s;    background-position: 0 0;  }}</style>

Annotated snippet

  1. Line 1Use the real interactive element as the paint surface. A separate overlay can work, but this pattern is about letting the target background own the state so no extra positioning layer needs to track it.
    PitfallShould a fill sweep use a pseudo-element overlay or the element background?

    Use the element background when the effect is a simple state fill. A pseudo-element is useful for more complex masks, but it adds stacking and pointer-event concerns that are unnecessary for a background-position sweep.

  2. Line 14The gradient contains both the active accent field and the resting surface. The hard color stop near the midpoint is what makes the fill read as a moving block instead of a soft shimmer.
  3. Line 20The background must be larger than the element. With a 100% background there is no off-screen color field to sweep into view, so changing background-position barely shows anything.

    Both examples animate background-position. BEFORE uses a 100% background, so the gradient has nowhere to travel. AFTER uses 225% background-size, leaving an off-screen accent field that can sweep through the target.

    PitfallWhy does my background-position fill sweep barely move?

    The background is probably the same size as the element. Increase background-size on the axis that should travel, then move background-position between the resting and active edges.

  4. Line 23Transition only paint state and text color. If the hover state changes width, padding, border thickness, or line-height, neighboring controls move and the sweep stops being a stable interaction affordance.

    BEFORE grows the button width to fake emphasis, pushing the neighboring target in the row. AFTER keeps both targets fixed and moves only background-position, so the fill is visible without layout movement.

    PitfallWhy should a fill sweep avoid changing width, padding, or border thickness?

    Those properties can reflow neighboring UI and make the hover state feel like a layout bug. Keep geometry fixed and animate only paint state, such as background-position and color.

  5. Line 28Hover and focus-visible share the same active state. Keyboard users should see the fill sweep when they tab to the control, and the outline should remain visible above the paint.
    PitfallHow do I make a fill sweep work for keyboard focus too?

    Pair :hover with :focus-visible and keep a real outline. The fill can carry the same state, but the outline is still the clearest keyboard location cue.

  6. Line 38Reduced motion should not remove the state. Pin the element to the active filled position with no transition so the interaction still has a visible result.
    PitfallHow should reduced motion handle a fill sweep?

    Remove the transition and show the filled state immediately. Avoid replacing one sweep with a fade or pulse; the goal is a static but still understandable state.

Other pitfalls

Is animating background-position as cheap as transform?
No. Background-position often repaints the element. It is fine for small buttons, links, and chips, but avoid running it continuously across large panels or long lists of controls.
How do I avoid hover-only effects on touch screens?
Do not make the sweep the only feedback. Keep a static active/pressed style and gate decorative hover loops with @media (hover: hover) when the effect depends on pointer hover.

Notes

Overview

A background-position fill sweep is a state-driven paint transition for buttons, links, chips, and compact navigation targets. The element owns an oversized linear-gradient() background. The resting state positions the accent outside the visible box; the hover or:focus-visible state moves background-position so the accent field sweeps through the target.

When to use it

Use it when the interaction needs a clear filled state but a ripple, tab indicator, or separate overlay would be too much. It works especially well for primary command buttons, inline links that need stronger emphasis, and status chips where the component geometry must remain stable. Avoid it for large panels or constantly looping backgrounds; that turns a compact state cue into a repaint-heavy ambient animation.

How it works

Give the target fixed padding, border, radius, and inline size. Then apply a gradient that is larger than the element via background-size. On state change, move only background-position and, if necessary, text color. The key implementation choice is overscan: a 100% background has nowhere to travel, while a200% or larger background leaves an off-screen color field ready to slide in.

Production gotchas

Do not fake the fill by animating width, padding, border thickness, underline thickness, or line-height. Those properties can push neighboring UI and make the interaction feel unstable. Background-position can also repaint, so keep the target small and avoid running the effect continuously across long lists. If the fill changes text contrast, test the active frame as carefully as the resting frame.

Accessibility

Pair :hover with :focus-visible and preserve a visible outline. The filled state should not be pointer-only. Under prefers-reduced-motion: reduce, remove the transition and pin the filled state so the interaction still communicates selection, emphasis, or focus without a sweep.

References

Implementation depth

This pattern differs from ripples, tab indicators, and gradient text sweeps because the interactive target itself owns the moving background layer. The reusable mechanism is an oversized gradient plus a state change in background-position; the element box, line metrics, and neighboring controls stay fixed.

The production limit is paint budget and input modality. Background-position can repaint the target, so keep it on small controls and avoid continuous loops in dense lists. Pair hover with focus-visible, preserve an outline, and use reduced motion to pin the filled state instead of running a substitute animation.