Grow your CSS skills. Land your dream job.

Show Image Under Text (with Acceptable Fallback)

Published by Chris Coyier

Updated April 2014 with more modern information.

WebKit supports the cool background-clip CSS3 property, which you can use to do some pretty neat stuff. The first time we touched on it was the iPhone Slide-to-unlock idea where we set a gradient to animate through the background of the text. Then we touched on it again for the transparent borders idea.

Let's take a look at using it to have an image be visibile only through the letters of the text. One more cool thing that we no longer need to drool over print designers ability to do.

View Demo

The Basic Idea

h1 {
   color: white;  /* Fallback: assume this color ON TOP of image */
   background: url(images/fire.jpg) no-repeat;
   -webkit-background-clip: text;
   -webkit-text-fill-color: transparent;
}

That's all there is to it. Set a background image on the element, then clip it out, and set the text fill color to transparent.

The Problem

As I'm sure everyone is painfully aware, this isn't going to work in all browsers. The current support is WebKit only. So what happens for a fallback? As you can see in the code above, you declare a color value as well. This value gets overridden by -webkit-text-fill-color in browsers that support it, so we're clear there. Then in browsers that don't support -webkit-background-clip: text we will see the background image in full, so we will see text on top of that background image. So if you read no further, set a color value that will be nice and visible on that background image.

So we get this going on:

Not the absolute end of the world, at least we prepared the text to be somewhat readable. But this is a long way from what we envision and what WebKit users will experience. Basically: that fallback sucks, let's do better.

A Better Fallback

The ultimate tool for better fallbacks is Modernizr. Simply include the (fairly small) JavaScript file on your page, and it adds classnames to the html tag of your page indicating what the current browser is capable of. It also provides an API for testing features in JavaScript, but we won't need that today.

Unfortunately, Modernizr doesn't have a test for background-clip right out of the box. I asked one of the creators, Paul Irish, who hooked me up with this quick way to add this test. The whole bit:

<script src="modernizr-1.6.min.js"></script>
<script>
  Modernizr.addTest('backgroundclip',function() {

    var div = document.createElement('div');

    if ('backgroundClip' in div.style)
      return true;

    'Webkit Moz O ms Khtml'.replace(/([A-Za-z]*)/g,function(val) { 
      if (val+'BackgroundClip' in div.style) return true;
    });

  });
</script>

Now we'll know if the current browser supports background-clip, or does not. If it does, the html tag will have a backgroundclip class, if it does not, it will have a no-backgroundclip class.

Now we only apply the background image if we are certain the browser supports background-clip.

.backgroundclip h1 {
  background: url(images/west.jpg) -100px -40px no-repeat;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

h1 {
  color: orangered;
}

Badda bing, now the fallback is a straight up solid color instead of a messy-looking image knockout.

View Demo

But... there is no perfect system for this.

There is an issue with Android (up to and including 4.2) where it doesn't actually support -webkit-background-clip - even though any test will return that it does. The property, and any value it might have including text. Even Modernizr's testAllProps() can't catch it. But -webkit-text-fill-color does work, so essentially you get an image with no text. Pretty bad.

If you absolutely need to use this, you might just need to UA test for Android:

var NastyBrowserSniffing = {

  ua: navigator.userAgent.toLowerCase(),

  init: function() {
    var isAndroid = NastyBrowserSniffing.ua.indexOf("android") > -1;
    if (isAndroid) {
      $("html").addClass("android");
    }
  }

};

NastyBrowserSniffing.init();

Then revert the effect if it's ndroid:

html.android .gradient-text {
  color: white;
  background: none;
  -webkit-text-fill-color: white;
  -webkit-background-clip: border-box;
}
.gradient-text {
  background: -webkit-linear-gradient(gray, black);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

Related

Stephen Morley covers how to do it for IE with filters.

Comments

  1. Permalink to comment#

    Awesome ! So, Firefox have no luck yet ?

  2. Safari got crashed on my windows ?? Not sure scripts problem or safari in windows ??

    • Permalink to comment#

      Every time I open a demo on this site it seems Chromium stops responding and the tab crashes. It must be something that’s added on every page, but it’s breaking my browser.

  3. Is FF4 going to support this?

    • Permalink to comment#

      Currently, the latest FF4 beta (b7) doesn’t support this. Maybe (hopefully) in the future, it will.

  4. That’s really awesome.

    One thing though: won’t that replace method run around 9-10 times, because it will also run for the space between each vendor prefix? Would it be better to use the + instead of the *?

    'Webkit Moz O ms Khtml'.replace(/([A-Za-z]+)/g

    That way, the callback will only run if something was actually captured. As a space is equivalent to false, it will never run in those cases. (But petty, yeah).

    Very cool demo.

    • Rohit Vighne
      Permalink to comment#

      Even more succinctly:

      'Webkit Moz O ms Khtml'.replace(/\w+/g, ...callback...)

  5. Sebastiaan Deckers
    Permalink to comment#

    I don’t think the second “return true” statement will achieve what is intended, as it is simply returning from within the anonymous function passed to replace(). How about using a returnValue variable and setting it from within the closure, then returning that at the end?

  6. Permalink to comment#

    Seems like its broken in FF4 beta7 too. Fallback doesnt work :-/

  7. I just love coming to your site dude, I’m a CSS feather weight, so new stuff like CSS3 blows my mind, sometimes it’s so overwhelming it seems like too much for me to wrap my mind around it. It’s so awesome to find that there are sites like this who helps us through the process. Cheers!

  8. Permalink to comment#

    Nice trick.
    Unfortunately, on Android you get the background on the entire div, and no text whatsoever.

  9. Great work Chris! I dig your experiments a ton.

  10. bill
    Permalink to comment#

    This is one of those effects where my first reaction is why bother? It only works is a couple of browsers and what value does it add? The best solution here would be to create an actual image and not use CSS for this so it can work everywhere.

  11. Paul
    Permalink to comment#

    Bill,
    Although your point is valid, I would make the argument that Chris is simply presenting a proof of concept. Would you ever need to deploy this to a live site? Maybe so, or maybe not. The more important thing is that Chris is showcasing an experimental CSS3 approach and more importantly showing a graceful fallback.

  12. Excellent work there, Chris. I’m glad you bothered with this:) Baby steps towards a more beautiful web!

  13. Chris
    Permalink to comment#

    This looks super choppy on Chrome (on XP) when rotated, but fine when horizontal. -webkit-transform issue?

  14. Permalink to comment#

    This is really cool to see what’s possible with CSS now but I would really use Photoshop to work on this type of effect

  15. Permalink to comment#

    The problem with this test is that it tests for the background-clip property, not for the particular value (text). background-clip is supported by all modern browsers (even IE9), but the “text” value is a non-standard Webkit extension. So, essentially, the “better fallback” would only get applied in old browsers that don’t support background-clip at all, and will fail on modern ones that aren’t Webkit, so they only support the standard values for it.

  16. It is really good. If it works in all browsers, no need to work in Photoshop to create this kind of masking effects.

    -vara

  17. This will be good to use, just wondering about support for other browser than Webkit.

  18. Rogier Borst
    Permalink to comment#

    This (obviously) also works for gradient backgrounds (like Chris shows in this post.
    However, if you apply text-shadow now, the background won’t show, because apparently the shadow is a solid ‘layer’ underneath the whole text.
    One method to have both gradient text and text shadows is described by Jeffrey Way in this article on nettuts, which involves replicating content in your stylesheet, making that a big no-no for me.
    Webkit should either create a text-shadow-clip property (yeah right) or just allow us to set something like an image or gradient on text color properties instead of just on background properties.

  19. Permalink to comment#

    Hahahaha.. it’s amazing. Work in Chrome 7.0 (but ‘the rotated text’ is not, when mouse over, it become straight/ not rotated), not working in FF (just color, no image), and bad in Opera 10 (like div with image background). But nice try :D

  20. Brilliant, really nice use of CSS with text. LT

  21. I was hoping that, when using a moving div (scrolling) and changing the background image to a semi-transparent png this would then allow me to see the image over which the div is being scrolled.

    sadly it does not.

    does anyone know how to ‘punch out’ the text from a div that has a solid colot? Setting the text to transparent doesn’t do the trick.

  22. No Firefox use for these tricks? Works great in Safari, thanks for sharing!

  23. Permalink to comment#

    Demo is crashing my Safari every time trying to watch the demo. Maybe this funny looking feature is not perfect

  24. Permalink to comment#

    awesome
    does it work on IE too ?

  25. Permalink to comment#

    Great post! Nice trick! I like it!

  26. JOSE
    Permalink to comment#

    It crashed the Safari on my W7 as well …

  27. garrett
    Permalink to comment#

    you could also make a photoshop image if you have i think anything after cs2 and create a clipping mask

  28. Hi, I like the idea of the Modernizr tweak, though it didn’t work for me in FF 27.0.1 as it is showing backgroundclip in that browser.

Leave a Comment

Current day month ye@r *

*May or may not contain any actual "CSS" or "Tricks".