Get affordable and hassle-free WordPress hosting plans with Cloudways — start your free trial today.
Experimental: Check browser support before using this in production.
The CSS cross-fade()
function blends background and masked images at a specified transparency value. In other words, it’s like bleeding one image into another, sort of like you are transitioning between two images that are stacked on one top of the other, transposing the two.
You can use it anywhere an <image>
is an accepted value, including the background-image
, mask-image
and content
properties. And, yes, we can add fade between two or more images or colors. We aren’t limited to just two.
/* Cross-fade between a CSS gradient and an image file */
.scene {
background-image: -webkit-cross-fade(
conic-gradient(
from -110deg at 50% 0%,
rgb(120, 255, 210),
rgb(170, 130, 255),
rgb(40, 100, 255),
rgb(120, 255, 210)
/* Fade by this much */
) 60%,
url("./mountains.svg")
/* Fade by this much */
40%
);
}
Note: See that funny -webkit
prefix? That’s called a “vendor prefix” which is often used when browser are experimenting with a feature that isn’t quite done. That means we have to use the prefix in order to use the the cross-fade()
function where it is currently supported, which is Safari. We won’t need to use the prefix once the feature is broadly supported.
Think of it like a weighted mix where we determine how much each image is faded. We determine that with a percentage, where by default, each image is an even 50%
. That means we could make one image more transparent at, say, 60%
, and the other less transparent at 40%
just like the example above.
You might think this is a lot like using the opacity
property, and yes, it sort of is like that. We can indeed position one image on top of the other, then apply different opacity
values for each one. However, what’s really happening with cross-fade()
is that it outputs a single image from the two images you supply it. So, in essence, there’s no more need to work with two separate images in the HTML which makes things a little easier to manage both in the markup and in the styles.
Syntax
The CSS cross-fade()
function is broken down like this:
cross-fade( <cf-image># )
<cf-image> = [ <image> | <color> ] && <percentage [0,100]>?
What that’s basically saying in plain English is that the function takes a “cross-faded image” (<cf-image>
) that can either be image files (<image>
), color values (<color>
), or a combination of the two (|
). Then you set a percentage value (<percentage>
) between 0%
and 100%
([0,100]
) on both values to set how transparent they are.
Arguments
/* Default is 50% / 50% if not percentages are declared */
cross-fade(url(a), url(b)); /* 50% / 50% */
/* Four background images, only two percentages */
cross-fade(url(a) 30%, url(b), url(c) 10%, url(d)); /* Remaining 60% is evenly distributed */
/* Three background images, more than 100% */
cross-fade(url(a) 60%, url(b) 50%, url(c)); /* Remaining is -10%, which is the same as 0% */
Heads up: The following explanation is how things are defined in the official CSSWG specification. Please note that the current browser implementation in Safari is different than what is published in the Editor’s Draft version of the specification.
Like we talked about earlier, you can use the cross-fade()
function to “fade” between two images, or colors, or both. But we aren’t limited to working with just two at a time. The function actually accepts a comma-separated list of arguments, each containing an image followed by an optional percentage value, which must be within 0% and 100%. The percentage value determines how much of the image is retained when blended with other images.
So, what happens if our percentages don’t add up to exactly 100%? The function does some fancy math behind the scenes to distribute the remaining percentage.
- Some arguments don’t have a specified percentage. Here, the browser adds up the percentages that are specified and subtracts that result from 100% to determine how much transparency is left to distribute to the arguments that do not have specific values. For instance,
cross-fade(url(a) 20%, url(b), url(c) 25%, url(d))
has only two arguments with percentages, and they equal 45%. The browser subtracts that 45% from 100%, giving us 55% remaining that is divided equally between the remaining arguments,url(b)
andurl(d)
, each one getting 27.5%.
.fade {
background-image: cross-fade(
url(a) 20%,
url(b), /* 27.5% */
url(c) 25%,
url(d) /* 27.5% */
);
}
- All arguments don’t have a percentage. If no argument has a percentage — like
cross-fade(url(a), url(b))
— then the percentages are distributed equally. So, if we are using two images, they share the bounty and each get 50%. But if we were working with, say, three images, they would each get 33.3% of the share.
.two-images {
background-image: cross-fade(
url(a), /* 50% */
url(b) /* 50% */
);
}
.three-images {
background-image: cross-fade(
url(a), /* 33.3% */
url(b) /* 33.3% */
url(c) /* 33.3% */
);
}
- The sum of specified percentages is above 100%. This results in a negative remainder, so the browser treats it like 0% and sets that on any remaining arguments that do not have explicit percentages set on that. Like in
cross-fade(url(a) 60%, url(b) 50%, url(c))
, where the sum is 110%, and the subtraction equals -10%, sourl(c)
is set to 0%.
.fade {
background-image: cross-fade(
url(a) 60%,
url(b) 50%,
url(c) /* 0% */
);
}
- The sum of specified percentages is below 100% Finally, in a situation where each argument has a percentage value, but the sum is less than 100%, the missing percentage acts like an extra transparent layer filling the remainder. So, 30% + 30% behaves as if there’s also
transparent 40%
.
.fade {
background-image: cross-fade(
url(a) 30%,
url(b) 30%,
/* transparent layer set at 40% */
);
}
This is similar to how color-mix()
mixes colors, but this time, we are blending images instead of mixing colors.
The WebKit implementation
Again, what we’ve looked at so far is exactly how the CSS specification defines things. But the way that WebKit has actually implemented the feature is different than specification. The difference is essentially that the specification accepts more than two arguments, while the WebKit implementation is limited to two arguments.
- The specification supports more than two images with independent percentages, e.g.,
cross-fade(url(a) 40%, url(b), color(…))
. - The WebKit syntax supports exactly two arguments and one percentage value, which is applied to the first image, while the second image gets the remaining percentage. It’s currently supported in Chromium-based browsers (prefixed) and in Safari (un-prefixed). Firefox does not support
cross-fade()
at all as of this writing, but keep an eye on the Browser Support section as that will likely change.
It is recommended that you use the specification’s syntax and set a fallback using the WebKit syntax for browsers. This way, your code will be a progressive enhancement as browsers support the official syntax.
/* Specification syntax */
@supports (background-image: cross-fade(url(a) 40%, url(b))) {
.box {
background-image: cross-fade(
radial-gradient(circle at 30% 30%, #ff6b6b, #ffcc70) 60%,
linear-gradient(135deg, #3a86ff, #00f5d4) /* omitted → gets remainder: 40% */
);
}
}
/* Fallback: WebKit syntax */
@supports (background-image: -webkit-cross-fade(url(a), url(b), 40%)) {
.box {
background-image: -webkit-cross-fade(
radial-gradient(circle at 30% 30%, #ff6b6b, #ffcc70),
linear-gradient(135deg, #3a86ff, #00f5d4),
60% /* % applied to the FIRST image only */
);
}
}
In the next examples, we’ll use the WebKit syntax since that is what is supported today, giving you a chance to see how the feature works.
Basic usage
You can cross-fade between two gradient functions and have a slider increase the opacity of one to decrease the opacity of the other, and vice versa.
.box {
/* Cross-fade between two CSS gradients */
background-image: -webkit-cross-fade(
/* First gradient image */
radial-gradient(circle at 30% 30%, #ff6b6b, #ffcc70),
/* Second gradient image */
linear-gradient(135deg, #3a86ff, #00f5d4),
50%
);
background-size: cover;
background-position: center;
}
The cross-fade()
function blends the radial display of the radial gradient with a linear gradient, adjusting the transparency of one to enhance or reduce the other.
cross-fade()
Creating Photoshop-like effects with The (now deprecated) WebPlatform project provides a practical use case of the cross-fade()
function by cross-fading a full moon with a wolf’s silhouette. The result is a combination of a wolf silhouette and a full moon, combined as a single image that are cross-faded equally (50%
).
.wolf {
background-image: -webkit-cross-fade(url("fullmoon.jpg"), url("wolf.png"), 50%);
}

cross-fade()
vs. color-mix()
vs. background-blend-mode
The cross-fade()
function blends images in terms of opacity, the color-mix()
function blends colors in a given color space, and the background-blend-mode
property blends a stacked layer of backgrounds, one into another.
Aspect | cross-fade() | color-mix() | background-blend-mode |
---|---|---|---|
What it mixes | Images (bitmaps, gradients, SVG) | Color values | Multiple background values |
Outputs | An <image> value (you use it where an image is expected) | A <color> value | No value returned. The result is a blend between the background layers. |
Typical use | background-image , mask-image , content (for ::before/::after ) | color , background-color , border-color , etc. | With multiple background-image layers on the same element |
Example | cross-fade(url(a.jpg) 40%, url(b.jpg)) | color-mix(in srgb, red 40%, blue) | background-blend-mode: multiply; |
Number of inputs | Two or more image arguments, percentages total 100%. | Two or more color values, percentages total 100%. | Blends a pair of adjacent layers; supports multiple modes per layer |
Example usage | Turning two (or more) images into one blended asset | Making intermediate colors or subtle tints | Creative effects between layers (multiply, screen, etc.) |
Tutorial | N/A | “Color Mixing With Animation Composition” | “Chaining Multiple Blend Modes” |
Browser support
The cross-fade()
function is currently only supported in Safari browsers. However, the WebKit implementation is supported in Safari and Chrome at the time of this writing.
For now, you can use cross-fade()
with the @supports
rule to target browsers that support it:
@supports (background: cross-fade(url() 20%, url(), 50%)) {
/* Only apply if the latest version is supported */
}
@supports (background: -webkit-cross-fade(url(), url(), 50%)) {
/* Only apply if the older version is supported */
}
.demo {
background-image: blue; /* fallback if neither is supported */
}
Specification
The cross-fade()
function is defined in the CSS Images Module Level 4 specification, which is currently in Editor’s Draft.
More information
- CSS cross-fade() function by WebPlatform
- Maybe there kinda is background-opacity? by Chris Coyier
Related
background-blend-mode
.element { background-blend-mode: screen; }
background-image
.element { background-image: url(texture.svg); }
clip-path
.element { clip: rect(110px, 160px, 170px, 60px); }
content
div::before { content: url(image.jpg); }
mask-image
.element { mask-image: url(star.svg); }
conic-gradient()
.element { background-image: conic-gradient(from 45deg, white, black, white); }
color-mix()
.element { color: color-mix(in oklab, red 20%, bisque 80%); }