Use CSS3 transitions with gradient backgrounds

Viewed 393747

I'm trying to transition on hover with css over a thumbnail so that on hover, the background gradient fades in. The transition isn't working, but if I simply change it to an rgba() value, it works fine. Are gradients not supported? I tried using an image too, it won't transition the image either.

I know it's possible, as in another post someone did it, but I can't figure out how exactly. Any help> Here's some CSS to work with:

#container div a {
  -webkit-transition: background 0.2s linear;
  -moz-transition: background 0.2s linear;
  -o-transition: background 0.2s linear;
  transition: background 0.2s linear;
  position: absolute;
  width: 200px;
  height: 150px;
  border: 1px #000 solid;
  margin: 30px;
  z-index: 2
}

#container div a:hover {
  background: -webkit-gradient(radial, 100 75, 100, 100 75, 0, from(rgba(0, 0, 0, .7)), to(rgba(0, 0, 0, .4)))
}
18 Answers

2021: It is now possible to animate gradients (NOT Firefox or Safari yet)

With Chrome 85, Edge, and Opera adding support for @property rule, now we can do this in CSS:

@property --myColor1 {
  syntax: '<color>';
  initial-value: magenta;
  inherits: false;
}

@property --myColor2 {
  syntax: '<color>';
  initial-value: green;
  inherits: false;
}

The rest is regular CSS.
Set the variables as initial gradient colors and also set the transition of those variables:

div {
  /* Optional: change initial value of the variables */
  /* --myColor1: #f64; --myColor2: brown; */

  background: linear-gradient(var(--myColor1), var(--myColor2));
  transition: --myColor1 3s, --myColor2 3s;
}

Then, on the desired rule, set new values for the variables:

div:hover {  
  --myColor1: #f00;
  --myColor2: yellow;
}

@property --myColor1 {
  syntax: '<color>';
  initial-value: #0f0;
  inherits: false;
}

@property --myColor2 {
  syntax: '<color>';
  initial-value: rgb(40, 190, 145);
  inherits: false;
}

div {
  width: 200px;
  height: 100px;
  background: linear-gradient(var(--myColor1), var(--myColor2));
  transition: --myColor1 3s, --myColor2 3s;
}

div:hover {
  --myColor1: red;
  --myColor2: #E1AF2F;
}
<div>Hover over me</div>

See the full description and example here and refer here for @property specification.
The @property rule is part of the CSS Houdini technology. For more information refer here and here and see this video.

For what it's worth, here's a Sass mixin:

Usage:

@include gradientAnimation(red, blue, .6s);

Mixin:

@mixin gradientAnimation( $start, $end, $transTime ){
    background-size: 100%;
    background-image: linear-gradient($start, $end);
    position: relative;
    z-index: 100;
    &:before {
        background-image: linear-gradient($end, $start);
        content: "";
        display: block;
        height: 100%;
        position: absolute;
        top: 0; left: 0;
        opacity: 0;
        width: 100%;
        z-index: -100;
        transition: opacity $transTime;
    }
    &:hover {
        &:before {
            opacity: 1;
        }
    }
}

Taken from this awesome post on Medium from Dave Lunny: https://medium.com/@dave_lunny/animating-css-gradients-using-only-css-d2fd7671e759

::before, the CSS pseudo-element can easily do the trick!

.element {
  position: relative;
  width: 200px;
  height: 150px;
  background-image: linear-gradient(45deg, blue, aqua);
  z-index: 2;
}

.element::before {
  position: absolute;
  content: "";
  inset: 0; /* same as { top: 0; right: 0; bottom: 0; left: 0; } */
  background-image: linear-gradient(to bottom, red, orange);
  z-index: 1;
  opacity: 0;
  transition: opacity 0.25s linear;
}

.element:hover::before {
  opacity: 1;
}
<body>
  <div class="element"></div>
</body>

All you have to do is use the ::before pseudo-element with zero opacity.

On :hover, switch ::before's opacity to 1 and if you follow a few simple steps, you should get your transition working.

  1. Set the element's background gradient using background-image
  2. Use the ::before pseudo-element with opacity zero to setup your next gradient
  3. set opacity to 1 inside :hover::before
  4. make sure your ::before has:
    • position absolute
    • content: ""
    • a lower z-index than the default element
    • zero top, bottom, left, and right (or simply inset: 0)
    • transition targeting opacity with a time interval of your preference

And that's it! Now you should be able tweak your transition with whatever duration / delay / timing-function you like!

A much cleaner solution would be to set the background color and use a mask-image.

#container div a {
  background-color: blue;
  transition: background 0.2s linear;
  width: 200px;
  height: 150px;
  mask-image: radial-gradient(circle at 50% 50%, rgba(0, 0, 0, .7), rgba(0, 0, 0, .4));
}

#container div a:hover { 
  background-color: red;
}
Related