Try Tuts+ Premium, Get Cash Back!
Quick Tip: Nonintrusive CSS Text Gradients
videos

Quick Tip: Nonintrusive CSS Text Gradients

Tutorial Details
  • Topic: CSS Text Gradients
  • Difficulty: Moderate
  • Tutorial Format: Video

Though not completely cross browser compatible, there are ways to nonintrusively create pure CSS text-gradients with a bit of trickery. The key is to use a mix of attribute selectors, webkit-specific properties, and custom HTML attributes.


Final Simple HTML

Hello World

By using custom attributes, we can then hook into these values from within our stylesheet by using the attr() function.


Final CSS

/* Select only h1s that contain a 'data-text' attribute */
h1[data-text] {
	position: relative;
        color: red;
}

h1[data-text]::after {
	content: attr(data-text);
	z-index: 2;
	color: green;
	position: absolute;
	left: 0;
	-webkit-mask-image: -webkit-gradient(
		linear, 
		left top, left bottom, 
		from(rgba(0,0,0,1)), 
		color-stop(40%, rgba(0,0,0,0))
	);	

Note: Paul referenced an even more succinct method in the comments. Be sure to check that out as well!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://rommelcastro.me Rommel

    thanks for this!!!!

  • Randito

    Cool video. But I have to say… this is more a love-letter for VIM than anything else. Holycow… that guy is crazy fast with his VIM.

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      haha – It’s not me specifically. Once you learn how to use Vim, anyone can be lightning fast.

  • Christophor S. Wilson

    Good tut. Only thing is the demo looks goofy on the iPad lol

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      Hehe – probably because I used a massive 200px font size. :)

  • Michael P.

    You have to be careful that your browser window is wide enough. When I first loaded the demo, the actual text was wrapped, and the :after text started on the second line.

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      Yeah – but that’s not a fault of this technique. It’s just the result of using a crazy massive font size for the demo. :)

  • Webkitz

    The gradient syntax is outdated! =(
    http://www.webkit.org/blog/1424/css3-gradients/

    -webkit-linear-gradient(rgba(0,0,0,1), rgba(0,0,0,0) 40%)

    -moz-linear-gradient(rgba(0,0,0,1), rgba(0,0,0,0) 40%)

    • http://andredublin.com Andre

      Not for mobile browsers that still use older versions of webkit.

  • http://barrywalsh-portfolio.com Barry

    Nice follow up, simple idea with a lot of possibilities, You might want to change up the font-size on the demo, it breaks the effect on smaller resolutions when the text breaks to the next line.

  • http://www.smartenough.org DNABeast

    When the text jumps to a new line (for instance if the display is too narrow the hi-light jumps down on Chrome. To prevent this you can use…

    white-space: nowrap;

    Here’s another fun oddity. I was able to fake a 3d effect by dropping the font size of the overlay slightly. It takes some finessing and experimenting with sizes and margins but it could come in handy.

    <link href=’http://fonts.googleapis.com/css?family=Luckiest Guy’ rel=’stylesheet’ type=’text/css’>

    <style type=”text/css”>
    body{
    background: #888;
    }
    /* Select only h1s that contain a ‘data-text’ attribute */
    h1[data-text] {
    font-family: “Luckiest Guy” ,sans-serif;
    position: relative;
    color: #569;
    font-size: 101px;
    text-shadow: -4px 4px 2px rgba(0,0,0,0.2);
    white-space: nowrap;
    letter-spacing:2px;
    }

    h1[data-text]::after {
    content: attr(data-text);
    z-index: 2;
    font-size: 96px;
    margin: 1px 0 0 2px;
    letter-spacing: 5px;
    color: white;
    position: absolute;
    left: 0;
    -webkit-mask-image: -webkit-gradient(
    linear,
    left top, left bottom,
    from(rgba(0,0,0,0.2)),
    color-stop(43%, rgba(0,0,0,0.1)),
    color-stop(43.1%, rgba(0,0,0,0)),
    to(rgba(0,0,0,0.4))

    );

    </style>

    • Mudassir

      while white-space: nowrap; does indeed stop the line jump, it also clips the text if the font-size is large.

  • NoName

    What plugin do you use for CSS completion in vim?
    good tut
    as usual :)

  • http://www.maisui99.com 小豪

    em…pretty funny

  • http://tommasoraspo.com Raspo

    Is it me or the text is not selectable either way?

    Nice tip anyway.

    • http://www.code.my devlim

      Ya, the text is not selectable, it cause by h1[data-text]::after cause when u turn off h1[data-text]::after, the text become selectable again.

    • http://net.tutsplus.com Jeffrey Way
      Author

      It actually is selectable, but you have to highlight a bit below the text to “find” it. I think there’s a way around this — but I haven’t figured it out yet.

  • http://www.customicondesign.com custom icon design

    I dont know why there is no effect in my firefox browser. Just with #666 background and #a6a6a6 the text color.

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      Webkit only.

    • http://www.aaron.im Aaron

      webkit – ‘-webkit-’ safari and chrome
      moz – ‘-moz-’ firefox

      hope this clears up

  • http://joshriser.com Josh

    Great tutorial, again, Jeffery! I always love your tutorials, and they are always so helpful!

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      Thanks, Josh!

  • http://www.maughan.net.au/ Ryan Maughan

    Why dont you add a before element to your CSS, which also calls the content of data-text, and prepend it:

    h1::before{
    content: attr(data-text);
    }

    This way you dont ever have to duplicate text. The isn’t selectable, but your version I believe is the same.

    Ryan

    • http://net.tutsplus.com Jeffrey Way
      Author

      Sorry if I’m missing something – but what’s the difference in your version compared to mine?

      • http://www.maughan.net.au/ Ryan Maughan

        Instead of parsing the same data between the tags, and in the data-text attribute, you just pass it to the data-text attribute. Eg:

        Instead of:

        Hello World

  • http://www.paulgrock.com Paul Grock

    Jeff – is there a reason you didn’t do something along the lines of

    h1 {
    background-image: -webkit-gradient(
    linear,
    left top, left bottom,
    from(rgba(0, 0, 0, 1)),
    to(rgba(0, 0, 255, 1))
    );
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    };

    Essentially what this does is it makes the background of the h1 tag just be the text and when you make the text transparent it shows through.

    I do really like the grabbing an attr from ::after. I had no idea you could do that.

    -Paul

    • http://net.tutsplus.com Jeffrey Way
      Author

      That’s definitely a cool idea that I haven’t played around with yet. :) Will check it out this weekend.

      • http://net.tutsplus.com Jeffrey Way
        Author

        Okay – so that’s an excellent technique, after playing around with it! The only thing I noticed is that you, of course, can’t then use things like shadows on the text.

        But other than that – much cleaner than my method!!

      • http://www.smartenough.org DNABeast

        The shadows don’t work like normal but they can create some really fun bevel effects if used properly.

        text-shadow: 2px 2px 2px rgba(0,0,0,0.05),-2px -2px 2px rgba(255,255,255,0.05);

    • http://css-tricks.com Chris Coyier

      One reason to use Jeffrey’s method is because you get an extra background to use. You can set the background on the h1 if needed, which you can’t with the method above () as it’s needed for the gradient itself.

      • http://www.paulgrock.com Paul Grock

        You can get around the no background issue by assigning a background to a pseudo element and giving it a z-index of -1

        h1::before {
        content: “”;
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        background: red;
        z-index: -1;
        }

        The key is the z-index to put the pseudo element behind the text which mostly replaces the background of the h1. Not exactly the same but I think it covers most situations.

    • http://www.jeffrey-way.com Jeffrey Way
      Author

      Quick little note on this — if the background declaration comes *after* the -webkit stuff, it won’t work. It must come before.

  • Sleeman

    thanks Jeffrey , very helpful indeed.

  • http://www.thedevelopertuts.com Bratu Sebastian

    Thanks for the ideea. How about using a transparent png gradient for :after ? I think that would work in fireworks too, I have to try this …

  • RZV

    Hello Jeff,

    i see that webkit-mask-image works only Chrome. About IEE,Firefox,Safari is any solution.
    Thanks you
    Rzv

    • http://net.tutsplus.com Jeffrey Way
      Author

      It works in Chrome and Safari — or all webkit-based browsers.

  • dan

    Nice VIM setup Jeffry, guess I need to fix mine so it runs better :/

  • RZV

    Hello Jeffrey,

    i attached the next css:

    body{
    background:#CCC;
    }

    h1{
    font-size:400px;
    color:#green;
    position:relative;
    font-family:sans-serif;
    }

    h1::after{
    content:”Text”;
    color:yellow;
    position:absolute;
    left:0px;
    -webkit-mask-image: -webkit-gradient(linear, right top, right bottom, from(rgba(0,0,0,0)),to(rgba(0,0,0,0)),color-stop(50%,rgba(0,0,0,1)));
    -webkit-mask-position: 10 0;
    )
    }

    It works only Chrome, in Firefox,IE,Safari,Opera takes the color 100%:yellow;

    Where is the problem with this ?

    Thank you ,
    RZV

    • M Burke

      RZV – webkit gradients only work in webkit browsers such as Chrome and Safari. They will not work in Firefox, IE, or Opera. There may be alternative solutions, such as -moz-mask-image but I’m not an expert on that, maybe someone else can chime in.

  • http://devignsight.com Tim

    A quick, semi-hack fix for not being able to select the text is by giving the h1 selector a z-index property of -1, and remove the z-index property from the “h1::after” selector. Don’t ask me why this works.

  • http://creativecart.blogspot.com/ Dinesh Verma

    What’s the use of this ::after.

  • RZV

    Hello Jeffrey,

    I am looking forward to hearing your solution for css(-webkit-mask-image) i wrote upper.

    Thank you ,
    Rzv

  • http://filcp.com Filipe Carrano Pacheco

    Thanks for the tip. Very useful to me.

  • w1sh

    I’m pretty fond of the cross-browser use of the white .png gradient overlay on top of text on top of a white bg. Works beautifully on white bg’s.

  • Alex Devine

    Thanks for providing this great tut :)

  • http://www.css3files.com Chris

    Nice idea but with only Webkit supported it’s too limited to be more than a nice, little goodie for “progressive” users.

  • arafat

    I want to learn css slide show.

  • http://www.my-html-codes.com HTML Codes Dude

    This is brilliant. Very clever stuff. I’ve never worked with the before and after selectors. Theres some good home work for me to do over the weekend to get me up to speed. Thanks.

  • http://mitchmckenna.com Mitchell McKenna

    What are you using for auto completion in Vim?

  • http://www.duzmath-tamas.hu/honlapkeszites Honlapkészítés

    I like it! I’ll use it on my next projket :)
    Thank you

  • http://deveedutta.net63.net/ Deveedutta

    This data-text-thing does not work with Line Breaks. Do you know any way to overcome this ?
    I have tried & \r in the data-text string. but could not find the solution