Over the weekend I noticed an interesting design for a progress meter in a videogame. The % complete was listed in text in the middle of the bar and didn’t move. But that text was the same color as the background of the bar that was filling up from left to right. It seemed like the background was going to make the text invisible once they overlapped, but instead, the text color reversed to be white instead anywhere it overlapped with the background.
My first thought was this: how can we replicate this design pattern and what might we learn along the way?
Here’s what I came up with, but make sure to check this demo on the latest version of Chrome to see everything working correctly:
See the Pen A pure CSS loading bar by Robin Rendle (@robinrendle) on CodePen.
Pretty cool, huh? This is possible with the awesome magic of mix-blend-mode
in CSS.
Let’s take a look at the markup first
<div class="wrapper">
<div class="bg">
<div class="text"></div>
</div>
</div>
The .wrapper
will hold our elements in place, .bg
will be our loading bar that increases over time and our .text
element will be used as the percentage information.
Let’s make the whole thing CSS-only
A “real” loader on the web would likely be powered by JavaScript and reacting to actual data somehow. But while we’re having fun here, let’s make the whole thing, even the counting, happen just in CSS (SCSS for the looping help).
We’ll set up our variables and style the .bg
element:
$loadingTime: 10s;
$color: rgb(255, 0, 0);
.bg {
background-color: $color;
animation: loading $loadingTime linear infinite;
}
With the loading
animation we’ll be changing the width of the element, like so:
@keyframes loading {
0% {
width: 0;
} 100% {
width: 100%;
}
}
Perhaps we could have hidden the overflow and moved the background box with transform
property instead (for performance reasons) but in this little demo I think it’s fine to animate the width
property alone.
To update the content of the .text
element with the correct percentage value we have to be a little dastardly and use a mix of pseudo elements and animations. First we’ll keep the <div>
empty set the content
of its after
pseudo element to 0%
before defining an animation:
.text {
color: $color;
width: 200px;
border: 1px solid $color;
&:after {
padding-left: 20px;
content: "0%";
display: block;
text-align: center;
animation: percentage $loadingTime linear infinite;
}
}
So what we want to do with the percentage
animation above is update our content
property with each value from 1 to 100, like this:
@keyframes percentage {
0% {
content: "0%";
}
1% {
content: "1%";
}
/* ... */
100% {
content: "100%";
}
}
But instead of making all those @keyframe
selectors by hand we can familiarise ourselves with the @for
loop syntax in Sass:
@keyframes percentage {
@for $i from 1 through 100 {
$value: $i + "%";
#{$value} {
content: $value;
}
}
}
If this looks a little scary then not to worry! On the third line we add whichever number is currently in the loop (which we call $i
) and make that a string by appending %
to it and assigning it to a variable. Then we can use interpolation to make each @keyframe
selector update the content
property to the right value.
The Color Swap
Finally, all we have to do is set the color and the mix-blend-mode
of our pseudo element and there we go; a pure CSS loader where the background color influences the foreground text:
.text:after {
/* This value is the OPPOSITE color of our background */
color: rgb(0, 255, 255);
mix-blend-mode: difference;
}
See the Pen A pure CSS loading bar by Robin Rendle (@robinrendle) on CodePen.
With the difference
blend mode we have to set the text element’s color value to the opposite of the background. So if our background is rgb(0, 0, 0)
we’ll need to set the text pseudo element to rgb(255, 255, 255)
.
I think this little demo helps us recognise how useful the mix-blend-mode
property can be. There are going to be all sorts of instances like this in the future where interfaces can reveal information in ways we’d never have thought possible before.
Changing Text Color Entirely
The cool part of this technique is the fact that some of the text is one color and other parts of the text is another color. The reversing happens just based on what is covered and what isn’t, even if it’s just a part of a letter.
If you were looking for more of an accessibility-based “change the text color to make sure it has enough contrast” thing, Sass can also help with that.
Another Example
The XOXO site used mix-blend-mode: darken;
quite a bit to to have backgrounds, shapes, and text all interact in subtle/interesting/beautiful ways that we haven’t seen a whole lot on the web yet.

Browser support
The mix-blend-mode
property isn’t well supported at the moment and neither is the animatable content
property. So make sure to provide fallbacks if you decide to use either of these tricks.
Nice trick. The changing of the content within the animation loop is a neat idea too. The blend mode stuff is nice too, it’s just a shame about the browser support.
Very informative
Nice technique, but an article like this really needs to include support information. This isn’t really feasible for real-world use yet without including a (visually much different) fallback for IE and anything but the very latest version of Android. http://caniuse.com/#feat=css-mixblendmode
100% agree and an easy improvement, IMO.
I started reading this article on my phone today, got interrupted, and then came back to it on my laptop only to find that the demos didn’t work (Chrome). Initially, I was thinking that this is one of those CSS properties that isn’t widely used, but completely useful and well supported. I was wrong and disheartened at the same time.
Browser support up front or at least an indication that this isn’t widely supported would be fantastic.
Lack of any version of IE support pretty much rules it out completely for me, might as well not have read it!
For mobile/Electron apps, perhaps it has some value though. It’s nice to know features exist, but it’s beyond frustrating when you discover it’s practically unusable in the real world.
wonder full information……
I’ve been waiting for the
mix-blend-mode
feature in CSS for years and years. All my designers want to use fancy blend modes on their transparency and shadow effects. Shame IE doesn’t support it yet, the Edge/IE platform status is also pretty discouraging, if they even added it to version 14 I’d be suprisedYou could also reach this effect using CSS clip, a library like https://github.com/salsita/jq-clipthru could do that for you.
http://salsita.github.io/jq-clipthru/demo/
Actually, just a few months ago it wasn’t able to animate the content property by CSS. Great!
As a newb to css, I found this interesting, stumbled across how it won’t work in stable release Chrome v46.0.2490.7a m Win 7 64 , but would work in Firefox, then Chrome Canary (48.02540.0), and then… Chrome Android!
If I understand Scott’s link correctly, it should be working in Chrome 46, though it is not for me. Not in “regular” mode, nor in incognito or guest mode.
Also using Chrome v46.0.2490.7a and no go for me… :P
Seems the automatic percentage is only work in Chrome Canary currently.
First stop, caniuse.com.
Checking for mix-blend-mode…. hmmmm. No support even in IE11
Forget article. Come back in ten years time to re-read it.
Seems a lot of people say ‘no browser support – can’t use it’.
Why not add this is as progressive enhancement? Create a basic version with CSS that works – then add mix-blend-mode afterwards, so anything that can handle it gets the jazz – something that can’t gets a solid basic version.
The problem is that it’s kind of sort of supported on Safari 9 in the sense that the figure alters color to be readable. But the values of the figure never changes on Safari – it’s 0% as the bar progresses. ON Chrome, you see the values increment properly.
Using something like this means I need to test a bunch of cases, figure out when support isn’t there (if there’s partial support how much partial is OK?) and then code, test and maintain all of that. Or, in a case like this, I can go look for a JS component that will work on any browser using JS.
Don’t get me wrong, I love knowing about this stuff but in real world use things like this are problematic when support is not only iffy, but not a bright works/doesn’t work line.
It’s not just the browser support but the fallback that matters – what happens if this property isn’t supported? Usually they’re just ignored.
Having checked the two examples, I can see that a certain amount of advanced visualisation is lost, but the function of the site is not affected.
Before writing something off as unusable check it in browsers where it doesn’t work and see how you can make it gracefully degrade, you may even find it gracefully degrades anyway (think border-radius on IE7)
I also think some indication of support up front would be useful. Not so that I can decide whether or not to read the article; I would still read it because it might be useful to know what I might consider in the future.
But in this case I would like support information because the article is so much more difficult to make sense of without a working demo. The author shows us a demo and says ‘pretty cool huh’ But I don’t know what I’m supposed to be seeing, or what the demo is supposed to show me. I have to work twice as hard to follow the article as a result.
With support information up front, I would be able to fire up the right browser to view the article, without trying them all, or resorting to caniuse.
You’re absolutely right, David. I’ve updated the post with a note about browser support and made it a bit clearer before the demo too.
Hey – i thought it would be cool to have a version without any blend-modes.
What do you think ?
This is a pretty neat implementation of the same idea. I like it!
Hi,
Could you elaborate on why a difference value for mix-blend-mode causes the two affected colors to kinda be merged together to become 255,255,255.
Just a bit of color theory would be handy here !
Thanks
I also share a slide about strands of web design. I discuss about the basic topic that use most of the premium website.
Heh, about two weeks ago I used the difference blend mode to have a similar hover effect on flat UI buttons. It also shows a decent fallback (just no transition) and how to use @supports to ensure good fallbacks.
You say “be sure to use the latest version of Chrome to ensure the demo works properly”.
Ironically, it works just fine in Firefox but in Chrome 46 I just see a black void in the codepen widget.
(and off topic, but “best viewed in Chrome” is one of my pet peeves of today’s web… it’s like we’ve forgotten all the lessons learned from those “best viewed in Netscape” and “best viewed in IE” buttons back in the day)
What this guy said, I am in the most recent version of Chrome aaaaand all I see is black but works fine in Firefox, maybe a little to premature for this article seeing as the majority of the people reading it are not seeing anything?
Following on from @ludwig’s comment above, here’s an IE9 compatible version without the blend-mode magic
TV subtitling is where I started for an this idea. With text-shadow more widely supported now, it is a way to keep text readable and with all the colors available you can set a color that fits with the design.
I’ve created a similar type effect using svgs and masks a while ago. It worked well with the slider and transitions and older browers. http://documenta60.de