Market
Pricing
Sign inStart
Field GuideCSS and web
CSS and web

CSS gradient hover effect: four ways to animate it

Write a new gradient in :hover and it snaps instead of easing. Browsers can't transition one gradient into another, but they can move one, fade one or animate the colours inside it. Here is each method, with the code.

GradientlyVerified Gradiently account·October 1, 2026·5 min read
Cover: Snow Sanctuary · GR·5830·QE

The short version

  • A CSS gradient hover effect cannot be made by transitioning background-image, because browsers swap gradients instantly rather than interpolating them.
  • The most compatible technique is an oversized gradient with a transition on background-position, which slides new colours into view.
  • Registering colour stops or angles with @property lets the browser transition them smoothly, and works in all current major browsers.
  • Fading the opacity of a second gradient on a pseudo-element is the cheapest technique to render, because opacity changes do not repaint.
  • Hover effects should sit inside a hover media query and respect prefers-reduced-motion.
On this page
Why transition doesn't work on gradientsTechnique 1: slide an oversized gradientTechnique 2: animate colour stops with @propertyTechnique 3: crossfade two gradientsTechnique 4: a glow that follows the cursorChoosing a techniqueTouch screens and reduced motion

A CSS gradient hover effect needs a workaround, because browsers don't transition background-image: change the gradient in :hover and it snaps. Four techniques give a smooth result. Slide an oversized gradient with background-position, animate registered colour stops with @property, crossfade a second gradient on a pseudo-element, or move a radial glow with the cursor. Each suits different elements, and all of them are below with code.

Why transition doesn't work on gradients

To the browser, a gradient is an image. It knows how to interpolate a number or a colour, but not one image into another, so background-image is not interpolated and the new gradient replaces the old one instantly. Custom properties used inside the gradient behave the same way unless you register them, because an unregistered property is just text.

css
/* This snaps, with no easing */
.btn {
  background-image: linear-gradient(135deg, #1e1b4b, #6d28d9);
  transition: background-image 400ms ease;
}
.btn:hover {
  background-image: linear-gradient(135deg, #6d28d9, #f472b6);
}
The transition line is ignored for gradients. Every technique below works around this.
The two states used in the examples. The goal is a smooth journey from the left swatch to the right one.

Technique 1: slide an oversized gradient

Make the background larger than the element and paint both states into it, then move it. background-position can be transitioned, so the new colours glide in. It works in every browser and needs no registration.

css
.btn {
  background-image: linear-gradient(90deg, #1e1b4b 0%, #6d28d9 50%, #f472b6 100%);
  background-size: 200% 100%;
  background-position: 0% 50%;
  transition: background-position 500ms ease;
}

.btn:hover {
  background-position: 100% 50%;
}
At rest you see the left half, indigo to violet. On hover the right half, violet to pink, slides into place.
The full 200% striplinear-gradient(90deg, #1e1b4b 0%, #6d28d9 50%, #f472b6 100%)
This is the whole background. The button only ever shows half of it at a time.

The movement reads as a sweep, which suits buttons and links. It is less convincing on large cards, where you can see the gradient travel. Animating background-position repaints, which is fine for small elements; for a big surface, use technique 3. Gradient buttons in CSS has more button patterns built on this.

Technique 2: animate colour stops with @property

Register the stop colours as typed custom properties and the browser can interpolate them. The gradient then changes colour in place, with no sliding. @property works in all current major browsers; older ones simply snap, which is an acceptable fallback for a hover. The registration rules are covered in CSS @property.

css
@property --stop-a {
  syntax: "<color>";
  inherits: false;
  initial-value: #1e1b4b;
}

@property --stop-b {
  syntax: "<color>";
  inherits: false;
  initial-value: #6d28d9;
}

.card {
  background-image: linear-gradient(135deg, var(--stop-a), var(--stop-b));
  transition: --stop-a 450ms ease, --stop-b 450ms ease;
}

.card:hover {
  --stop-a: #6d28d9;
  --stop-b: #f472b6;
}
Each stop eases independently. Give them different durations and the colour seems to flow from one corner to the other.

The same trick animates the angle. Register --angle with syntax: "<angle>", use it in linear-gradient(var(--angle), ...) and change it on hover: a gradient that turns from 135 to 225 degrees as the pointer arrives is a subtle, polished touch for cards and gradient borders.

Both techniques so far also work on gradient text. Text clipped with background-clip: text shows whatever the background does, so a slide or a stop transition on a heading makes the colour run through the letters on hover. Keep the change gentle on headings people need to read, and make sure both states meet contrast against the page.

Technique 3: crossfade two gradients

Put the hover gradient on a pseudo-element above the base and fade its opacity. Opacity changes are handled by the compositor without repainting, so this is the smoothest choice for large cards, hero panels and anything in a long grid.

css
.tile {
  position: relative;
  isolation: isolate;
  background-image: linear-gradient(135deg, #1e1b4b, #6d28d9);
}

.tile::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background-image: linear-gradient(135deg, #6d28d9, #f472b6);
  opacity: 0;
  transition: opacity 400ms ease;
}

.tile:hover::before {
  opacity: 1;
}
z-index: -1 with isolation: isolate keeps the pseudo-element behind the tile's text but above its base gradient.

Technique 4: a glow that follows the cursor

A radial gradient positioned with custom properties can track the pointer. Set --x and --y from a small script and the light moves with the hand. Keep it soft and keep it away from text: it should feel like light on a surface, never a halo around words.

css
.panel {
  background-image:
    radial-gradient(circle 220px at var(--x, 50%) var(--y, 50%), rgb(244 114 182 / 0.35), transparent 70%),
    linear-gradient(135deg, #1e1b4b, #312e81);
}

/* JavaScript:
   panel.addEventListener("pointermove", (e) => {
     const r = panel.getBoundingClientRect();
     panel.style.setProperty("--x", `${e.clientX - r.left}px`);
     panel.style.setProperty("--y", `${e.clientY - r.top}px`);
   });
*/
The glow is a top layer over a dark base. Updating two custom properties is cheap, but each move repaints, so use it on a few panels, not every card on a page.

Choosing a technique

Slide with background-position

Looks like

A sweep of new colour

Rendering cost

Repaints during the transition

Best for

Buttons, links, tags
@property colour stops

Looks like

Colours shifting in place

Rendering cost

Repaints during the transition

Best for

Cards, badges, borders
Opacity crossfade

Looks like

One gradient dissolving into another

Rendering cost

Composited, no repaint

Best for

Large panels, long grids
Cursor glow

Looks like

Light following the pointer

Rendering cost

Repaints on every move

Best for

A few feature panels
TechniqueLooks likeRendering costBest for
Slide with background-positionA sweep of new colourRepaints during the transitionButtons, links, tags
@property colour stopsColours shifting in placeRepaints during the transitionCards, badges, borders
Opacity crossfadeOne gradient dissolving into anotherComposited, no repaintLarge panels, long grids
Cursor glowLight following the pointerRepaints on every moveA few feature panels
When in doubt, crossfade. It is the cheapest and works everywhere.

For gradients that move on their own rather than on hover, see CSS animated gradients. For the wider picture of what costs paint time, see CSS gradient performance.

Touch screens and reduced motion

Avoid

  • Hover styles that stick after a tap on phones
  • Information that only appears on hover
  • Long, looping hover animations
  • Ignoring the reduced motion setting

Do

  • Wrap hover rules in @media (hover: hover)
  • Show the same state on :active for touch
  • Keep transitions between 200 and 500ms
  • Cut movement under prefers-reduced-motion, keep the colour change
css
@media (hover: hover) {
  .btn:hover { background-position: 100% 50%; }
}

@media (prefers-reduced-motion: reduce) {
  .btn { transition: none; }
}
The colour still changes for everyone; only the movement is removed for people who asked for less.

Hover effects bring a website to life. On social posts there is no pointer, so motion has to live in the design itself. Gradiently's Marks can carry their own gentle motion, and with Pro a design exports as MP4, WebM or GIF where the browser supports recording, ready for a story or a post.

Find a gradient that’s only yours

Browse thousands of Marks, design with any of them for free, and claim the one that feels like you.

Browse the Market

Questions people ask

Can you transition a CSS gradient on hover?

Not directly. background-image changes instantly. Transition background-position on an oversized gradient, animate registered stops with @property, or fade a second gradient's opacity.

How do I make a gradient button change colour smoothly on hover?

Use background-size: 200% 100% with a three stop gradient, then transition background-position from 0% 50% to 100% 50% on hover.

Does @property work in all browsers?

It works in current versions of Chrome, Edge, Safari and Firefox. Older browsers snap between states instead of easing.

What is the most performant gradient hover effect?

An opacity crossfade: the hover gradient sits on a pseudo-element and only its opacity changes, which avoids repainting.

Should gradient hover effects respect reduced motion?

Yes. Under prefers-reduced-motion: reduce, remove the movement and keep a simple colour change.

Share this guide

Written by GradientlyVerified Gradiently account

The team behind Gradiently, a design tool built around Marks: living gradients that make everything you design look like yours.

See our profile

On this page

Why transition doesn't work on gradientsTechnique 1: slide an oversized gradientTechnique 2: animate colour stops with @propertyTechnique 3: crossfade two gradientsTechnique 4: a glow that follows the cursorChoosing a techniqueTouch screens and reduced motion

Share this guide

Next in CSS and web

CSS @property: animating gradients and custom properties

Keep reading

All CSS and web guides
CSS and web

CSS animated gradient backgrounds, done properly

4 min read

Gradients cannot be animated directly, but everything around them can. Four techniques, what each costs the browser, and how to keep motion calm and optional.

CSS and web

CSS @property: animating gradients and custom properties

6 min read

Gradients refuse to transition because the browser cannot blend one image into another. Give a custom property a type and that limit disappears. Here is how @property works, with code you can paste.

CSS and web

Gradient buttons in CSS that feel premium

4 min read

Most gradient buttons look cheap for the same few reasons: too many hues, a hover that jumps, and text that fades into the light end. Here is the recipe that avoids all three.

Find the look that’s only yours.

Every Mark is a living background with one owner. Try an unclaimed Mark, and claim yours when it feels right.

Start designing

Check a certificate

Type a certificate number or a Mark’s code to see who owns it.

Works with ChatGPT and Claude

Ask your AI for a design. It makes it in your Mark, ready to edit.

ChatGPTClaude
How to connect

Product

  • Market
  • Explore
  • Pricing
  • Certificates

Learn

  • Field Guide
  • Help centre
  • What’s new

Developers

  • Overview
  • ChatGPT and Claude
  • API reference
  • API keys

Company

  • About
  • Contact us
  • Sign in

Legal

  • Terms
  • Privacy
  • Refunds
  • Mark protection
  • Cookies
  • All policies
Move through the letters. Each one wears a real Mark.Touch a letter to see its Mark.One name. Ten Marks inside it.
© 2026 Gradiently