Basics of CSS Blend Modes

Chris Coyier //

Bennett Feely has been doing a good job of showing people the glory of CSS blend modes. There are lots of designerly effects that we're used to seeing in static designs (thanks to Photoshop) that we don't see on the web much, with dynamic content. But that will change as CSS blend modes get more support. I'd like to look at the different ways of doing it, since it's not exactly cut and dry.

CSS Multiple Backgrounds Blend Modes

You can blend background-images together, or blend them with background-color. It's a simple as:

.blended {
  background-image: url(face.jpg);
  background-color: red;
  background-blend-mode: multiply;
}

See the Pen Background Blending by Chris Coyier (@chriscoyier) on CodePen.

Multiply is a nice and useful one, but there is also: screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, hue, saturation, color, and luminosity. And also normal which reset it.

Adobe (who works on the spec for this stuff, of course) created this Pen for playing with the different possiblities here:

See the Pen CSS Element Background Blend Modes by Adobe Web Platform (@adobe) on CodePen.

A single element can have more than one background, stacked up. Like:

.graphic-or-whatever {
  background:
    url(grid.png),
    url(building.jpg)
}

Those can blend too simply by adding a background-blend-mode.

See the Pen Multiple Background Blending by Chris Coyier (@chriscoyier) on CodePen.

Here's a cool and practical example by Bennett Feely:

See the Pen rxoAc by Bennett Feely (@bennettfeely) on CodePen.

Here's another, which cleverly re-combines a color image separated into Cyan / Magenta / Yellow / Black parts (CMYK). You know that's how offset lithography works in print, right? =)

See the Pen CMY/CMYK Color printing with background-blend-mode by Bennett Feely (@bennettfeely) on CodePen.

Arbitrary HTML Elements Blend Modes

Blending backgrounds together is pretty cool, but personally I'm less excited about that than I am about blending arbitrary HTML elements together. Like a <h1> title over a background, for example. Or even text over text.

I saw this at an airport the other day and snapped a pic because I thought it looked neat and figured I could figure out how to do it on the web:

My first attempt to recreate it, I used opacity. But opacity really dulls the colors and doesn't make those overlapping bits have the extra darkness they should have. CJ Gammon showed me there is a blending property precicely for this purpose: mix-blend-mode.

So to reproduce this:

<h1>hungry?</h1>

Then break it up into s with Lettering.js:

$("h1").lettering();

Then squeeze the letters together with negative letter-spacing, set the mix-blend-mode, and colorize:

h1 {
  font-size: 7rem;
  font-weight: 700;
  letter-spacing: -1.25rem;
}
h1 span {
  mix-blend-mode: multiply;
}
h1 span:nth-child(1) {
  color: rgba(#AB1795, 0.75);
}
/* etc, on coloring */

Compare:

See the Pen Overlapping Letters by Chris Coyier (@chriscoyier) on CodePen.

Like I mentioned, real web text over image is a pretty sweet use case if you ask me:

See the Pen GxlBm by Chris Coyier (@chriscoyier) on CodePen.

Canvas Blend Modes

The DOM blend mode stuff is most interesting to me, but it should be noted that <canvas> has blend modes as well and it has a bit deeper support (see below for all that).

You set it on the canvas context. So like:

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

ctx.globalCompositeOperation = 'multiply';

That value can be any of those I listed above. Here's a simple demo:

See the Pen canvas blend modes by Chris Coyier (@chriscoyier) on CodePen.

And a fancy one where you can see the blending give the demo life:

See the Pen sketch.js Demo by Justin Windle (@soulwire) on CodePen.

SVG Blend Modes

As you might suspect, SVG does have its own mechanism for doing this. One way to do it is to define it within the <svg> itself, and it's fairly complicated:

<svg>
  
  <defs>
    
    <filter id="f1" x="0" y="0" width="1" height="1">
      <feImage xlink:href="#p1" result="p1"/>
      <feImage xlink:href="#p2" result="p2"/>
      <feBlend mode="multiply" in="p1" in2="p2" />
    </filter>
    
    <path id="p1" d='M100 100 L200 100 L200 200 L100 200 Z' fill='#00FFFF'/>
    <path id="p2" d='M150 150 L250 150 L250 250 L150 250 Z' fill='#CC3300'/>
    
  </defs>
  
  <rect width="100%" height="100%" filter="url(#f1)"/>
  
</svg>

See the Pen Fnvdp by Chris Coyier (@chriscoyier) on CodePen.

And a more complex example.

The good news is that mix-blend-mode will work on inline SVG. So if you're using SVG that way, you can target the shapes themselves with classes or whatever and give them whatever blend mode you want.

Here's an example by Bennet that does just that:

See the Pen KDkCj by Bennett Feely (@bennettfeely) on CodePen.

Browser Support

For canvas: Firefox 20+, Chrome 30+, Safari 6.1+, Opera 17+, iOS 7+, Android 4.4+. Worst bad news: No IE.

For HTML/CSS: Firefox 30+, Chrome 35+, Safari 6.1 (apparently not 7?). Not quite as supported as canvas.

At this second, for Chrome, you'll have to run Canary, go to chrome://flags/ and enable "experimental Web Platform features".

This actually it a bit more complicated, so if you really want to dig in and know about support, check out the Support Matrix from Adobe.

Progressive Enhancement

What is nice about blending is that the whole point is designerly effects. If they aren't supported, you can take care to make sure the fallback is still readable. E.g. the whole idea of progressive enhancement.

Here's a recent thought by Jeremy Keith:

It is entirely possible—nay, desirable—to use features long before they are supported in every browser. That’s how we move the web forward.

So one way to do support is to look at the design in a non-supporting browser and if it is still readable/usable, you're good, no further action required.

If the result ends up unreadable/unusable, you could tweak things around until they are, or run a test to determine support and do something specific in the case of non-support.

To test for support, I guess you could do test for the property you want to use:

var supportsMixBlendMode = window.getComputedStyle(document.body).mixBlendMode;

var supportsBackgroundBlendMode = window.getComputedStyle(document.body).backgroundBlendMode;

If the returned value is "normal" (or anything other than undefined) support is there, otherwise not. Then probably apply a class to the <html> element so you can know that and use it anywhere in your CSS to adjust things, Modernizr style. Perhaps they'll even do a test for it in the future.

Unless it's not that simple in which case let me know.


Need more ideas? Check out this collection. Happy blending!