• Replace text with an image using CSS

    Nov 7 2006

    Let's say you want to have a logo on a page, but you'd really like to use an <h1> with some text for the header in the HTML. Or maybe you like to use images for all your titles, but would still like to have plain text inside header tags in your HTML.

    There are a number of reasons for wanting to do this, namely accessibility and search engine optimization. I talk more about this in my post Writing Semantic HTML.

    The problem is: How do you hide the text? I think the most popular technique is to wrap the text with a <span> inside the header tag, then use some CSS to 1) hide the text, and 2) use the header tag as an image. Something like this:

    <style type="text/css">
    h1 {
        width: 500px;
        height: 100px;
        background: url(my_header_image.gif);
    }
    
    h1 span {
        display: none;
    }
    </style>
    
    <h1><span>My Great Header</span></h1>
    

    Unfortunately, this technique makes us add an extra tag to our markup, and we all know that every time we use an unnecessary tag, a puppy dies. Either that, or we end up with ugly, unnecessarily bloated HTML.

    Well here's another technique which hides the text just by using CSS. The text will still be readable by screenreaders and search engine spiders, but will disappear like magic for everyone else:

    <style type="text/css">
    h1 {
        width: 500px;
        height: 100px;
        background: url(my_header_image.gif);
        overflow: hidden;
        line-height: 500px;
    }
    </style>
    
    <h1>My Great Header</h1>
    

    This technique works by pushing the text down inside the header with a rather large line-height (it must be at least twice the height). Then the overflow: hidden hides the text since it's overflowing.

    Now isn't that better? No puppies were harmed, and we end up with slightly cleaner and shorter markup.

  • Comments

    1. Realazy at 8:44am on November 8, 2006

    Why not use <code>text-indent: -999em;</code>(any large value), it's more simple.

    2. Jesse Skinner at 9:10am on November 8, 2006

    Yep, good call. I like that even better. Thanks!

    3. Tom at 8:34pm on November 8, 2006

    Personally I prefer a solution with which I can have a virtually limitless amount of text in the <h1> tag - neither of your solutions allow for that.  I tried both using about four paragraphs of text and had problems with both.

    Instead I use the following:

    h1 {
        width: 500px;
        height: 0px;
        background: url(my_header_image.gif);
        padding-top: 100px;
        overflow: hidden;
    }

    4. trovster at 12:21pm on November 20, 2006

    Why would you use height: 0px then a padding-top?

    I prefer the Phark method (text-indent: -9999px; or any arbitrary large negative figure) to the Gilder/Levin image replacement technique (the one mentioned with the extraneous span).

    I don't know why you'd have problems with 'limitless amount of text', but the method below allows for that.

    Personally this is how I do it:

    h1, p.welcome, ul#nav li a {
      display: block;
      overflow: hidden;
      font-size: 0.0; line-height: 0.0;
      text-decoration: none; text-indent: -9999px;
      background: transparent no-repeat 0 0;
    }
    h1 {
      height: 20px; width: 500px;
      background-image: url(header.img);
    }
    p.welcome {
      height: 150px; width: 350px;
      background-image: url(welcome.img);
    }

    This code easily allows for more elements to be replaced. Simply add the selector to the main replacement code, then create a separate declaration with the height, width and path to the appropriate image.

    The display: block allows for the image replacement to be used on inline elements (such as anchors) with no extra code. The 0.0 on font-size and line-height are for bugs in the validator (!). And the overflow: hidden; trims the unsightly long 'focus halo' introduced in Firefox 1.5, so that is only the width of the content.

    5. Jesse Skinner at 2:49am on December 17, 2006

    I've been using Tom's method for some time now and it's really fantastic. It avoids relying on any browser bugs or using arbitrarily large numbers, and just does exactly what you want. Thanks, Tom!

    6. trovster at 6:44am on December 18, 2006

    Not sure how the method I suggested is relying upon browser bugs. Also, the 'arbitrarily large number' doesn't have to be -9999px;, just the width of the area you're replacing. Because these sizes vary, you use a number which you're unlikely to exceed. You could, infact, use -1024px; if you so wished. As I doubt you're going to replace elements with widths larger than that size.

    7. Bliss at 5:52am on December 27, 2006

    In case, i wish to give hyperlink to the h1?

    8. trovster at 1:54pm on January 24, 2007

    @ Bliss: Then simple use the selector h1 a {} in the code I provided above. The generic code adds display: block, so you can automatically use it on inline elements and it still works...

    9. peter at 12:15am on January 25, 2007

    brilliant!
    i like your solving problem method.

    Commenting is now closed. Come find me on Twitter.