Blending Images with CSS cross-fade()
Compositing two images in CSS without extra markup or JavaScript used to mean stacking elements with position: absolute and juggling opacity values on each one. The cross-fade() function removes that overhead entirely by producing a single blended <image> value directly in your stylesheet.
Key Takeaways
cross-fade()is a CSS image function that composites two or more images at specified opacity weights into a single<image>value, usable anywhere CSS expects an image.- Percentage weights control each input’s opacity in the blend. Omitted percentages are auto-distributed from the remainder of 100%.
- Browser support is uneven: Chromium and Safari support the legacy
-webkit-cross-fade()syntax, while Firefox lacks any implementation as of early 2026. - Use
@supportsblocks to layer the prefixed legacy syntax and the spec syntax over a solid fallback image for progressive enhancement.
What CSS cross-fade() Actually Does
cross-fade() is an image-generating CSS function defined in the CSS Images Level 4 specification. It takes two or more images — bitmap files, gradients, SVGs, or solid colors — blends them at specified weights, and outputs a single <image> value. Because the output is an image type, you can use it anywhere CSS expects one: background-image, mask-image, or the content property on pseudo-elements.
That distinction matters. Unlike layering two background-image values and applying background-blend-mode, cross-fade() composites the inputs into one result before rendering. No extra DOM nodes, no stacking context side effects.
How the Percentage Weights Work
Each argument takes an optional percentage that controls how opaque that input is in the final blend. A few rules govern the math:
- No percentages declared: inputs share equally. Two images each get 50%; three images each get ~33.3%.
- Some percentages omitted: the browser sums the declared values, subtracts from 100%, and distributes the remainder equally among the unspecified inputs.
- Sum exceeds 100%: the remainder is negative, so any unspecified input is treated as 0% (fully transparent).
- Sum is below 100%: the shortfall acts as an invisible transparent layer filling the gap.
/* Equal blend — no percentages needed */
cross-fade(url(a.jpg), url(b.jpg)) /* 50% / 50% */
/* Weighted blend */
cross-fade(url(a.jpg) 70%, url(b.jpg) 30%)
/* Three inputs, one unspecified — gets the remainder (30%) */
cross-fade(url(a.jpg) 40%, url(b.jpg) 30%, url(c.jpg))
Spec Syntax vs. the WebKit Implementation
This is where things get uneven. The CSS Images Level 4 specification defines cross-fade() with support for multiple inputs and independent percentages per input. The older WebKit implementation — still what Chromium-based browsers and Safari actually render — accepts exactly two arguments and one percentage applied to the first image only.
As of early 2026: Chromium-based browsers support the -webkit- prefixed legacy syntax. Safari supports both the prefixed and unprefixed legacy syntax. Firefox does not implement cross-fade() at all. You can verify the current status on WebStatus.
Use @supports to layer both syntaxes with a solid fallback:
/* Base fallback — all browsers */
.hero {
background-image: url('fallback.jpg');
}
/* Legacy WebKit syntax — Chrome, Safari */
@supports (background-image: -webkit-cross-fade(url(a), url(b), 50%)) {
.hero {
background-image: -webkit-cross-fade(
url('photo.jpg'),
linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.6)),
60%
);
}
}
/* Spec syntax — future-facing */
@supports (background-image: cross-fade(url(a) 50%, url(b))) {
.hero {
background-image: cross-fade(
url('photo.jpg') 60%,
linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.6))
);
}
}
Discover how at OpenReplay.com.
Practical CSS Image Blending Use Cases
CSS image compositing with cross-fade() is most useful when you want a visual effect without adding markup:
- Image tinting: blend a photo with a solid color or gradient to apply a brand tint directly in CSS.
- Gradient overlays: layer a darkening gradient over a hero image for text legibility without a pseudo-element.
- Lightweight texture effects: blend an SVG texture with a flat color background.
What it is not well suited for: animated transitions between images on a timer. For that, opacity keyframe animations on stacked elements remain the more compatible and controllable approach.
cross-fade() vs. Related CSS Tools
| Need | Use |
|---|---|
| Blend two images by opacity weight | cross-fade() |
| Blend color values | color-mix() |
| Apply blend modes between background layers | background-blend-mode |
| Blend an element with what’s behind it | mix-blend-mode |
Accessibility Note
Background images — including those produced by cross-fade() — are invisible to screen readers. If the blended image conveys meaning critical to the page, represent that content in the HTML instead, and ensure sufficient color contrast between any text and the blended background.
Conclusion
cross-fade() is a focused tool: it composites images at the stylesheet level, keeps your markup clean, and works today in Chromium and Safari with the -webkit- prefix. Write the fallback first, layer the @supports block on top, and you have a progressively enhanced CSS image blending solution that degrades gracefully in Firefox until support lands.
FAQs
In practice this is unreliable across browsers. If you need a smooth crossfade animation between two images, stack them as separate elements or background layers and animate their opacity values instead.
Yes. The cross-fade() function accepts any valid CSS image type as an input, including linear-gradient(), radial-gradient(), conic-gradient(), and SVG references. This makes it useful for blending a photo with a gradient overlay in a single declaration.
The resulting image dimensions are calculated as a weighted average of the dimensions of the input images. Rendering behavior can vary slightly in browsers that implement the older WebKit syntax.
There is no widely adopted polyfill. The most reliable workaround for Firefox is to use multiple background-image layers combined with background-blend-mode, or to stack elements with absolute positioning and control their opacity individually.
Truly understand users experience
See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..