How to center a header's (i.e. h1, h2) background image along with its text

Hi all,

Just a quick question this time round.

How would I cause a header with a background image (i.e. h1, h2 etc.) to display this image just before where the text starts if the header itself is a text-align: center; ?

If we have a text-align: left; then all is fine but things go wrong as soon as the header’s text is meant to be centered:

I have this code:

h2.index_announcement_id1 {padding: 0px 0px 5px 40px; background: url(../images/find_memory_make.png) top left no-repeat; text-align: center;}

…and if I change the background image to be centered just as the text then the image gets displayed right in the middle of the parent container but not at the left hand side of the header, in this case a h2.

h2.index_announcement_id1 {padding: 0px 0px 5px 40px; background: url(../images/find_memory_make.png) top center no-repeat; text-align: center;}

Surely this is possible without extending the image itself to span the parent div’s width? The result should be something like this:

[header image][header text]

Thank you,

I’ve come up with the below method albeit it:
a) doesn’t let me vertically align the h2 text and insists on placing the h2 text at the bottom
b) In IE9 the image doesn’t get drawn at all

h2.index_announcement_id1 {text-align: center;}
h2.index_announcement_id1:before {content: url(../images/index_announcement_id1.png) top left no-repeat; padding: 0px 10px 10px 0px;}

Hmmm…

I think you are better off putting the image into the html:

<h2><img src="" alt=""> Text goes here</h2>

Then, in the CSS, you do this:

h2 img {vertical-align: middle}

That’s the easiest way, and will get the best result.

Ralphs on the right track, but I’m not sure whether IMG is the correct elemenet semantically – if that’s NOT content and is simply presentation, use a span sandbag… or :before could do the job as well… that might actually be the best approach, as then your not sticking things in the HTML that don’t belong there.


h2 {
	/* Legacy IE faking :before */
	zoom:expression(
		runtimeStyle.zoom=1,
		insertAdjacentHTML('afterBegin','<span class="before"></span>')
	);
}

h2:before,
h2 span.before {
	content:' ';
	display:inline-block;
	width:16px; /* image width */
	height:1.5em; /* make this == your line-height! */
	font-size:1px; /* prevent IE height oddities */
	vertical-align:middle;
	background:url(images/yourimage.png) center left no-repeat;
}

or something to that effect.

Thanks guys.

I tried the :before method but it doesn’t work well in all browsers.

I used the <h1><img></></h1> method until now but wanted to bring these headers inline with what I do for all the other headers and just assign a header its corresponding class (which displays the background image) but as stated above this method is great only for headers that are text-align: left.

I’m thinking of a div method now.

I found this way to do it and the good news is that it works in modern browsers, the bad is that yet again IE7 has to act differently for some odd reason. In IE7 it insists on moving the image and h2 header to the right before attempting to center it hence the whole div is skewed to the right instead of bang right in the middle of the parent div.

CSS:

.outer-center {
    float: right;
    right: 50%;
    position: relative;
}
.inner-center {
    float: right;
    right: -50%;
    position: relative;
}
.clear {
    clear: both;
}

HTML:

<div class="ie7-fix-attempt">
    <div class="outer-center">
        <div class="inner-center">			
			<h2 class="index-announcement-header-id1">Find laptop memory upgrades for your specific laptop brand and model</h2>				
        </div>		
    </div>
    <div class="clear"></div>
</div>

I had this problem with IE7 before where it wouldn’t center a row of buttons. In that instance I simply did the below and it worked, not so in this case.

CSS:

* + html .buttons-pane {float: left; width: 100%;}  /* IE7*/

I will probably never understand why Microsoft chose to be awkward with IE6 and IE7. At least IE9 is finally playing ball the same way as the other modern browsers have been for years now.

The below method works in all modern browsers (including IE8) but in IE7 it skews the div to the right before centering, which then corrupts the centering itself.

In other words, in modern browsers it looks like this:

<----------[header background image][header text]---------->

In IE7 it looks like this:

<---------------[header background image][header text]----->

<div class=“ie7-fix-attempt”>
<div class=“outer-center”>
<div class=“inner-center”>
<h2 class=“index-announcement-header-id1”>Sample header text goes here</h2>
</div>
</div>
<div class=“clear”></div>
</div>

[COLOR=“#006400”].outer-center {
float: right;
right: 50%;
position: relative;
}
.inner-center {
float: right;
right: -50%;
position: relative;
}
.clear {
clear: both;
}

h2.index-announcement-header-id1 {padding: 5px 0px 15px 55px; background: url(…/images/index_announcement_id1.png) top left no-repeat;}[/COLOR]

I had this problem before in IE7 where it wouldn’t center a row of buttons. In that instance I did the below and it worked, this time however things are different.

* + html .ie7-fix-attempt {float: left; width: 100%;} /* IE7*/

WAY too much HTML for something so simple… Why are you messing with floats when you’re trying to center something? That makes no sense and I’m kinda surprised that works anywhere – in any case that’s four div doing the job of one span.

At that point go with a span sandbag:

<h1>
<span></span>
Heading Text
</h1>

Styling it thus:

h1 {
text-align:center;
}

h1 span {
display:inline-block;
width:16px; /* image width /
height:1.5em; /
set to your line-height */
background:url(images/h1Image.png) center left no-repeat;
}

or a wrapping span:

<h1><span>Heading Text</span></h1>

with this for CSS:
h1 {
text-align:center;
}

h1 span {
padding-left:24px; /* image width plus any desired padding */
background:url(imaage/h1Image.png center left no-repeat;
}

Either method should work in just about everything assuming your line-height is at least as tall as the image… with the latter technique you could pad the top and bottom of the nested span to make sure you get the correct height… even so given the image interaction you may be forced into declaring fonts on said element in PX. (which is kinda bad, but being a h1 the text should be big enough for that not to be disastrous).

Or you could use :before to add the span sandbag as I said above, then there’s no extra HTML… though really one span isn’t the end of the world.

GAAH, what the hell! I had to delete the code blocks as it was completely screwing them up…

Thanks deathshadow60, excellent advice and explained as easy as 123. The second method worked but necessistated header ttext positioning to be added, which by then made it similar to the first method so I just used that. Works great even in IE7 except I had to add vertical-align: center; for the header’s text to not appear at the bottom of the preceding image

So logical once I see it in action. I’ve been using <span> no for some time and feel stupid for not considering it useful for this. It really does the trick and wit minimum code too. I really need to remember thinking of using spans more often in the future.

As for the over the top DIV method above it’s actually based on http://www.kensfi.com/how-to-align-center-a-div-with-no-width-declared/ and [URL=“http://www.tightcss.com/centering/center_variable_width.htm”]http://www.tightcss.com/centering/center_variable_width.htm

Thanks again

That is nonsense I’m afraid and should never be used.

and http://www.tightcss.com/centering/center_variable_width.htm

I believe the negative float method was first shown in my original and very old demos here. Unfortunately the one you linked to leaves you with a massive howizontal scrollbar in IE7 because they offset the element the wrong way to start with and didn’t use overflow:hidden.

The inline-block method is much neater though (as used in Deathshadows method) and you can control vertical-alignment on inline-block elements quite easily.

except I had to add vertical-align: center;

I think you meant vertical-align:middle as there is no value center for vertical-align :slight_smile:

Yes indeed, sorry, I meant vertical-align: middle above.

I’ve run into a problem with the above, only for header textsts that span two or more lines. When this is the case the following happens:
a) the second line of text is displayed at the left of the image underneath it rather than start from the right side of the image
b) due to specifying height: 48px (the height of the header images) this causes the gaps between the lines of text to exagerate and look rather wide.

Unfortunately this can’t be fixed with a display: block as the above method needs a display: inline-block;

Padding-top or padding-left also doesn’t work.

Hmm.

At the moment it looks like this:

<------------------ [header image][this is some sample header text ------------------>
< >
< which unfortunately, if too long, goes to the line on the second line and not only >
< >
< moves far left before the image but also creates these wide gaps between lines ] >

Hi,

I’m not sure what you expect to happen when it moves to two lines but either way remove the line-height as vertical-align will align the image anyway.

e.g.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Untitled Document</title>
<style type="text/css">
h1 {
	text-align:center;
	margin:0 0 1em;
}
h1 span {
	display:inline-block;
	width:25px; /* image width */
	height:25px; /* image-height */
	background:red url(images/h1Image.png) 50% 50% no-repeat;
}
h1 span, h1 b { vertical-align:middle; }
</style>
</head>

<body>
<h1><span></span> <b>This is the text This is the text This is the text This is the text </b> </h1>
</body>
</html>


You may need to show us a link to what you are working with so that we don;lt have to guess :slight_smile:

It’s part of a major site update and hence isn’t online and as it’s all intertwined with other cufon code, parent divs etc. I feel it quicker just to attach a screenshot along with the code below.

Code:
.header-image {
display: inline-block;
width: 55px; /* image width /
height: 48px; /
set to your line-height */
vertical-align: middle;
}

[COLOR=“#000080”].sale span {background: url(…/images/sale_offer.png) top left no-repeat;}

<h2 class=“sale”><span class=“header-image”></span><?php echo $offer_title ?></h2>[/COLOR]

Screenshot:
See attachment below.

Several observations:

  1. per your comments there is no line-height albeit a height but that has to be there from what I see as then the image won’t get displayed (or will but it won’t be visible)
  2. Notice in the screenshot how the second and each line is not indented about 60px from the left
  3. Also from the screenshot, the second line has a wide gap (this can somewhat we remedied but only to a degree but forcing in a line-height: 0px on the h2 class (not the span)
  4. Also from the screenshot, from the third and later lines of header text line-height automatically gets reduced to a give or take normal level (no more wide gap)
  5. From what I believe problems 2-4 can be solved by a display: block but as the image span needs a display: inline-block this isn’t possible. I tried placing it in a div (just the header text) but that’s also a no-go (at least for now).
  6. If the this header (class = “sale”) was only intended to be a text-align: left all its life then I could just do the below, except then there’s no way to center the whole thing without it getting messy.

h2.sale {padding: 0px 0px 5px 55px; background: url(…/images/sale_offer.png) top left no-repeat;}

Overall, perfect code so long as the header text is a one liner only.

Pardon me if I am missing the point ( at first i thought you were trying to essentially text-align;center with a bg image. If that not the case, and your text is aligned left, why not use text-indent: (width of image + some white space); This will indent only the FIRST line of text, assuming your line-height >= the height of your image, you can keep it as a bg ( no-repeat, of course) to your block element. It also wont cause that odd “leading” effect. if you are worried about vertical centering to the first line, you can ROUGHLY calculate an offset based on an em value , sort of like this:

background: url (myImage.gif) no-repeat left (~1/2 line-height-half image height);

the smaller the image, be better this will work , of course, since you CSS doesn’t perform math so chances are that the CSS is will effectively need to be something like this:

background: url (myImage.gif) no-repeat left .45em; /not the measurement in ems/

I hope this helps.

Do you mean something more like this?

The wrapping span method I mentioned would not have the word-wrap issue… If I have time later I’ll toss together a demo showing that.

Oh, and paul, what’s with the bold tag on your demo? Just pad the left of the span and put it in as the background-image on that… no need for three tags when two will do the job just fine.

– edit – sorry for no code tags, they screw up currently…

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN” “http://www.w3.org/TR/html4/strict.dtd”>
<html>
<head>
<meta http-equiv=“Content-Type” content=“text/html; charset=utf-8”>
<title>Untitled Document</title>
<style type=“text/css”>
h1 {
text-align:center;
font:bold 28px/32px arial,helvetica,sans-serif;
}
h1 span {
display:inline-block;
padding-left:55px;
background:url(images/sale.gif) top left no-repeat;
}
</style>
</head>

<body>
<h1><span>This is the text This is the text This is the text This is the text</span></h1>
</body>
</html>

Shorter, simpler… everything is tabbed in past the image when it word-wraps instead of dropping under it.

I tried that and it won’t won’t work unless you add line-height to match the height of the image which then affects all subsequent lines as pointed out by the OP. It would be ok for images that are always smaller than the line-height though. My method allows for the image to be any size and not interfere with the wrapping at all.

Yes the code tags seem to be corrupted at the moment but you can use html tags ok.

Yes, sorry. From my observations I found that:

a) The method(s) originally proposed by deathshadow60 above are excellent in cases where the header text (or it can be a, p etc.) fits within one line. If the latter isn’t true and thus the text spans over two or more lines you get the gaps and text kicking back left. I as far as I found there’s no way to either force in a display: block (due to the fact that this method require a display: inline-block) and remove line-height as the latter defines how tall the span image is and without it the image won’t be visible or only some of the image will be visible. An option is to use an image that’s only ever as wide as the subsequent text but then that’s a limitation. Overall this method is great and IMHO looks professional rather than doing <h1><img></>sample text</h1> even though this way might be a little more SEO friendly due to the img’s alt attribute so a little keyword stuffing. Personally I don’t think search engines bother all that much with img alt’s anymore, it’s so easy to cheat this way.

b) The method I wrote about in post #13 above (at the bottom) is great for multiple line texts but cannot be centered as then the padding has to be changed. It’s also fine with display: block so the second and further lines of text (should they exist) wrap round nicely and are vertically aligned. The size of the image doesn’t intefere with line gaps.

Overall I don’t see a perfect method. I brought it up only down to the fact that I like to keep my CSS consistent, not one method for this, another for that and so forth. Unless I missed something above then this isn’t possible in this case. As a sidenote I’m surprised that they came up with floats but only left and right.

Thanks to all who responded and shared their insight.

Did you not see my example which as far as I can tell does excactly what you want without any of the other problems you wre encountering?

Hi Paul,

Sorry, only just tested it out on this end. Seems promising, works in everything including IE7 but not IE8 or IE9. While it ‘works’ in the latter it seems to indent the second line so you lose that vertical alignment on the left, this happens regardless whether one does a h1 text-align:left; or h1 text-align:center;. Oddly though the 3rd and 4th line (as I’ve expanded the header text to see how it behaves with multiple lines) in IE8 and IE9 behave correctly like the first line.