Background-clip, Text-shadow, & Gradients; Oh My!

Trevor Davis, Former Front-End Development Technical Director

Article Categories: #Design & Content, #Code

Posted on

I’ve had a couple of designs recently where I have been able to play around with background-clip: text in combination with text-shadow and gradients, and I wanted to share my experience.

Before we get too far, background-clip: text is not supported in all browsers. I believe it is still only supported in Webkit browsers, so make sure you have sufficient fallbacks. The demos on this page will also only show up with all the fanciness in webkit, so I’ll wait for you to switch over.

Okay, ready?

First example: background-clip + text-shadow

Background clip end result

The image above is an example of the heading I needed to create. It has a gradient over the text and a box shadow behind it. Since these headings were going to be custom controlled by the CMS, we didn't want them to be images. CSS3 to the rescue!

First, we start by adding a CSS3 gradient as the background.

h1 {
    background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #91a9cb));
    background-image: -webkit-linear-gradient(#ffffff,#91a9cb);
}

Next, we add background-clip and a text fill color. This is also a good opportunity to add a fallback text color for browsers that don’t support background-clip: text.

h1 {
    background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #91a9cb)); background-image: -webkit-linear-gradient(#ffffff,#91a9cb);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    color: #fff;
}

We are looking pretty good so far, now we just needed to add a text-shadow.

h1 {
    background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #91a9cb)); background-image: -webkit-linear-gradient(#ffffff,#91a9cb);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    color: #fff;
    text-shadow: 6px 6px 9px rgba(0, 0, 0, 0.33);
}

Doesn’t look exactly as we wanted. It might be hard to tell, but since we set the text to have a transparent fill-color, the text-shadow bleeds through. I then decided that this would be a good opportunity to use a data attribute & generated content to duplicate the text and position it behind the gradiated text.

<h1 data-text="Oh heeey!">Oh heeey!</h1>
h1 {
    background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #91a9cb)); background-image: -webkit-linear-gradient(#ffffff,#91a9cb);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    color: #fff;
    position: relative;
}

h1:after {
    background: none;
    content: attr(data-text);
    left: 0;
    position: absolute;
    text-shadow: 6px 6px 9px rgba(0, 0, 0, 0.33);
    top: 0;
    z-index: -1;
}

That looks pretty great and has fallback for other browsers. Success!

Second example: background-clip + generated content

Background clip end result

In this example, we flip what we did in the first and use background-clip on the generated content. This image above shows what we need the end result to look like.

First, we just style how we want the heading to look.

h1 {
    color: #fff;
    font: 52px/52px Georgia, Times, serif;
    text-transform: uppercase;
}

Next, let’s use generated content and add a background image.

<h1 data-text="Hey buddy!">Hey buddy</h1>
h1 {
    color: #fff;
    font: 52px/52px Georgia, Times, serif;
    position: relative;
    text-transform: uppercase;
}

h1:after {
    background: url(images/structure/bg-heading-diag.png);
    content: attr(data-text);
    color: transparent;
    left: 4px;
    position: absolute;
    top: 4px;
    z-index: -1;
}

Then, let’s add background-clip to that bro.

h1 {
    color: #fff;
    font: 52px/52px Georgia, Times, serif;
    position: relative;
    text-transform: uppercase;
}

h1:after {
    background: url(images/structure/bg-heading-diag.png);
    -webkit-background-clip: text;
    content: attr(data-text);
    color: transparent;
    left: 4px;
    position: absolute;
    top: 4px;
    z-index: -1;
}

Finally, since background-clip: text doesn’t work in non-webkit browsers, we will need to add some sort of check to test if it’s webkit or not. When I previously checked, there was no way of testing if a certain value was supported for a property, so I just rolled with the jQuery browser object.

if($.browser.webkit) { $('html').addClass('webkit'); }

Then, we just need to modify the styling of our generated content a bit to have a solid color for non-webkit browsers.

h1 {
    color: #fff;
    font: 52px/52px Georgia, Times, serif;
    position: relative;
    text-transform: uppercase;
}

h1:after {
    color: #948972;
    content: attr(data-text);
    left: 2px;
    position: absolute;
    top: 2px;
    z-index: -1;
}

.webkit h1:after {
    background: url(images/structure/bg-heading-diag.png);
    -webkit-background-clip: text;
    color: transparent;
    left: 4px;
    top: 4px;
}

And that’s it! Just a few tricks I’ve come across while playing with this stuff on some projects. Let me know if you all have any other tricks with background-clip.

Related Articles