HTML & CSS
Animate a Linear Gradient by Moving Its Background
Animate a CSS linear gradient with an oversized background, pause control, and reduced-motion fallback. Diagnose why background movement is invisible.
Move the painted background inside a stationary box
To animate a linear gradient with broadly familiar CSS, make its background image wider than the element and animate background-position. The element and its text stay in place while a different part of the color blend moves behind them. This technique does not require the browser to interpolate between two different gradient functions.
The important detail is background-size. If the gradient is exactly as wide as its positioning area, moving a percentage position can produce no visible horizontal travel. An image three times the box width leaves room to slide. Start with a conspicuous two-color blend so you can see the motion before tuning a subtler palette.
Use a complete banner with a pause control
This example uses a native checkbox before the banner. Checking it pauses the CSS animation at its current position; unchecking it resumes. The label is visible, and keyboard users can toggle the checkbox with Space. Keep the control outside the animated surface so its own contrast and location do not change.
<div class="gradient-demo">
<input class="motion-toggle" id="pause-gradient" type="checkbox">
<label for="pause-gradient">Pause background animation</label>
<section class="moving-banner">
<h2>A moving background</h2>
<p>The content stays still.</p>
</section>
</div>
.gradient-demo { max-width: 48rem; margin: 1rem auto; }
.motion-toggle:focus-visible { outline: 3px solid #111827; }
.moving-banner {
box-sizing: border-box;
margin-top: .75rem;
padding: 2rem;
color: #ffffff;
background-color: #172554;
background-image: linear-gradient(90deg, #172554, #581c87);
background-size: 300% 100%;
background-position: 0% 50%;
animation: banner-pan 8s ease-in-out infinite alternate;
}
@keyframes banner-pan {
to { background-position: 100% 50%; }
}
.motion-toggle:checked ~ .moving-banner {
animation-play-state: paused;
}
@media (prefers-reduced-motion: reduce) {
.moving-banner { animation: none; background-position: 50% 50%; }
}
The eight-second duration is one outward trip. Alternate reverses the next trip, so the background returns without a sudden reset to its starting position. Changing the duration to four seconds makes each trip faster; changing background-size alters how much of the blend is visible at once. Those controls solve different design problems.
Choose the initial colors in the generator
Open the CSS Gradient Generator, select Linear, set the angle to 90, and choose #172554 and #581c87. Its output supplies the static background declaration. Transfer the gradient value into background-image in this recipe, then keep the separate size, position and animation rules shown above.
The local generator exports a two-color linear, radial or conic background. It does not create keyframes, timing controls or pause buttons. Those are additions in your stylesheet and markup. Avoid replacing the whole example with a later background shorthand, because that shorthand can reset the background size and position that make the motion visible.
Verify the motion without moving the content
Inspect the banner at the beginning, halfway through a trip, and at its far end. Its width and text position should remain constant while background-position changes. Toggle Pause during the trip and verify that the position stops advancing. Resume it and confirm that it continues from the paused point rather than restarting immediately.
Repeat the check at a narrow phone width and after increasing text size. Padding belongs inside the box because box-sizing is border-box. If your page still overflows, inspect its parent width or a long unbroken child rather than compensating with a negative margin on the banner.
Check preferences and the full color range
The reduced-motion rule intentionally leaves a static midpoint view. Test that mode independently from the pause checkbox: a motion preference should work even if the visitor never finds the control. MDN documents the reduced-motion media feature and its relationship to device preferences.
Check text against the lightest area that can pass behind it, not just the first frame. The dark endpoints in this recipe keep the white text on a dark surface; changing either endpoint to a pale color requires a fresh check. For more complex content, an opaque text panel can make the background’s movement independent of text readability.
If the animation seems absent, verify that the stylesheet loaded, its keyframe name matches, the checkbox is not checked, and reduced motion is not active. MDN’s background-position reference explains percentage positioning, while the CSS Animations specification defines the timing and iteration behavior used here.
When reusing the component more than once, give each checkbox its own unique id and matching label target. Otherwise a second label may toggle the first banner’s control, which makes a correctly written animation appear to have a broken pause button.


