Divya Manian

RSS Feed Youtube Channel Github

Using background clip for text with CSS fallback

I was looking at the lovely design of polygon when their use of background-clip & background: linear-gradient(…) immediately suggested a JS-free fallback for using the non-standard background-clip: text.

What it is

background-clip is a CSS property that was originally introduced in WebKit (in 2008) that allows backgrounds specified on elements to clip to the text content of the element like so:

by merely using the following code:

body{
 color: transparent;
 background: url(path/to/bg/image) repeat;
 -webkit-background-clip: text;
}

However, background-clip: text is not part of any standard yet – but other values such as border-box or content-box are.

The Problem

It is no surprise to find people using non-standard properties in HTML, and it is no big deal if it fails gracefully in other browsers. Unfortunately, background-clip: text fails rather spectacularly in non-WebKit browsers:

The above is how it appears (when you use placekitten to generate the background image) in Firefox. You cannot even read the text! This is clearly a disastrous situation.

Current Solutions

The most commonly used solution is to use Modernizr to detect if background-clip: text is available, and then use a class backgroundcliptext to apply background-clip:text selectively – Polygon does this too.

Purely CSS solution

While looking at the code, I suddenly realized that if you use only the -webkit- prefixed value for background property, then background-clip: text would only apply when -webkit- prefixed values are supported in browsers – effectively curtailing its use to WebKit browsers:

body {
 color: transparent;
 background: -webkit-linear-gradient(transparent, transparent),
             url(path/to/bg/image) repeat;
 -webkit-background-clip: text;
}

Hurray! We are almost there! The background no longer appears in non-WebKit browsers:

…until you test in Opera, which also supports -webkit- prefixes for site compatibility. But, it is easy to solve for by merely adding -o- prefixed background value:

body {
 color: transparent;
 background: -webkit-linear-gradient(transparent, transparent),
             url(path/to/bg/image) repeat;
 background: -o-linear-gradient(transparent, transparent);
 -webkit-background-clip: text;
}

But, wait, the color: transparent would make the text invisible in all browsers! Let us change that to a readable color:

body {
 color: red;
 background: -webkit-linear-gradient(transparent, transparent),
             url(path/to/bg/image) repeat;
 background: -o-linear-gradient(transparent, transparent);
 -webkit-background-clip: text;
}

But now the color fills the text, and the clipped background is no longer shown in WebKit browsers :(

text-fill-color to the rescue

text-fill-color (introduced in 2006) fills text with a color when used in conjunction with a text-stroke. It behaves exactly like color except we now have the advantage of having it apply only to WebKit browsers! Nirvana!

body {
 color: red;
 -webkit-text-fill-color: transparent;
 background: -webkit-linear-gradient(transparent, transparent),
             url(path/to/bg/image) repeat;
 background: -o-linear-gradient(transparent, transparent);
 -webkit-background-clip: text;
}

Here is how it appears in WebKit browsers and non-WebKit browsers respectively:

You only have to use 2 extra css properties, to make it fail-safe in all browsers and need not run a feature-detection script. Currently, only Opera requires the background reset using the -o- prefix, but if more browsers start supporting -webkit- prefix then each of those browsers need a reset (e.g. background: -moz-linear-gradient(transparent, transparent);). But I don’t think this will happen.

Comments