CSS text on images. The issues.

2015-10-13

There are many good reasons for taking text out of images and instead using html/css to overlay the text on top of the image instead.

  • The text can remain readable even if the image resizes.
  • Accessibility. Especially important if using a background-image.
  • Search engines can find and prioritize what is likely key words for your page.
  • Making text edits later doesn't require image re-work.
  • The text can be copy/pasted from the browser

Approach #1: CSS background-image

Using a css background image on a div is perhaps the most common approach.

  • + easy to overlay content
  • + easy to resize responsively
  • + easy to swap in different images at different widths
  • + easier to control focus point of image during resizing
  • - doesn't print by default
  • - harder to swap images by pixel density (retina)

A basic starting point for this method might look like this:

<div class="backgroundImageContainer">
    <div class="overlayTextContainer">
        <p>This is my overlay text!</p>
    </div>
</div>

This is my overlay text!

It's important to recognize that, thus-far, our image is completely controlled by our overlay text. The background image is only visible because we have overlay text occupying vertical space. This becomes important because:

  • If your overlap text wraps, your background image grows taller too.
  • If we decide to get fancy on how we position our text, we may need to find another way to manage the height of our image.

When I work with text overlays, it's the image that needs to be the boss. The text needs to move around where it will be readable but doesn't cover important parts of the image.

So let's make some minor changes. Let's add two things to our background container.

  • A specific height.
  • background-size: cover to make the image scale to our container width
.backgroundImageContainer{
    background-image: url('img/Unbalance1.jpg');
    height: 200px;
    background-size: cover;
}

This is my overlay text!

By giving the background container a height we have made the image container the boss - in a way that you might not have expected. (I know it surprised me)

Now when the browser resizes to a smaller width, if the overlay text needs to wrap - it will overlap the content underneath instead of pushing it down!

Here is an example to illustrate. Resize the browser to less wide and watch what happens to the text.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

So we have to be mindful of how our overlay text will wrap. The overflow problem is easy enough to fix by using overflow: hidden on our background container. But we still need to be aware that if we don't properly adjust our overlay text at smaller sizes - it will have the the bottom cropped off.

Below is the same example, but with overflow: hidden applied

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Before we can decide where and how to place that overlay text, we have to keep an eye on something else. Using background-size: cover will introduce cropping at the bottom of the image

As the image gets smaller and background-size: cover maintains the aspect ratio of the image, it has the effect of moving more of the image into view from the bottom. That can have the undesired result of overlapping your text on part of the image that you don't intend.

You could avoid that by not using background-size: cover but then we'd have to address keeping the aspect ratio of our image preserved.

I think it's worth dealing with this 'image creep' in order to have all the entire width of our image viewable in our container. But we need to keep this in mind, because we may need to move or resize our text at different sizes.

A valuable mothod for dealing with this is to use background-position. This allows you to define what part of your image to focus on.

This is done with background-position: X Y where X is the horizontal starting point and Y is the vertical starting point.

For our needs, it's really the vertical part that we care about. background-size: cover is taking care of the horizonal.

Let's see what it looks like when we use background-position: 0 50%

This is my overlay text!

Notice:

  • The image begins focused on the vertical middle
  • As we resize, the top and bottom adjust to keep the vertical middle in the middle

Now that we have a grip on our background image positioning, it's time to think about how to move our overlay text around.

I'm going to just address a headline. A common positioning technique would be to use css position.

We'll apply position: relative to our background image container in order to be able to use position: absolute to position our text anywhere inside it.

This is my overlay text!

This is centered text

This is vert and horiz centered

Here is the new css

.backgroundImageContainer6{
	background-image: url('img/Unbalance1.jpg');
	height: 200px;
	background-size: cover;
	overflow:hidden;
	background-position: 0 50%;
	position: relative;
}
.overlayTextContainer6{
	position: absolute;
	bottom: 0%;
}
.overlayTextContainer6 > p{
	color: white;
	padding:15px;
	font-size:30px;
}

.overlayTextContainerCENTERED{
	position: absolute;
	width: 100%;
	left: 0;
}
.overlayTextContainerCENTERED > p{
	color: red;
	padding:15px;
	font-size:30px;
	text-align:center;
}

.overlayTextContainerVERTandHORIZcenter{
	position: absolute;
	margin: auto;
  	top: 0; left: 0; bottom: 0; right: 0;
	width: 100%;
	height: 42px; /* need value a bit larger than font size for vert centering */
}
.overlayTextContainerVERTandHORIZcenter > p{
	color: blue;
	padding:0;
	font-size:30px;
	text-align:center;
	margin: 0;
}

In my example, I'm positioning my overlayTextContainer div. I'm getting extra padding and margin from my p tag. I won't go into the details of using left, right, top, or bottom with position to place your text. Just be sure to adjust as needed in your css breakpoints for possible text wrapping.

Although some hints on using position elements are in order. Once you start using position: absolute, you can't center text like usual. You could try something like left: 50% but you'd quickly realize that only puts the left edge at 50%, you'd have to guess what percent (36% maybe) to shift your text container over in order for it to look centered. That becomes a bigger problem as widths change.

Instead, to achieve horizontal centering, give your text container width: 100% and left: 0. Now you are able to use text-align: center on your p tag inside your text container.

Planning for different sizes

When you are overlaying text on top of images, you have to be able to resize your text as your image changes size.

In addition to swapping out different images (maybe a portrait version at the phone size and a landscape version on tablet/desktop) you should also look for good widths to adjust the size/positioning of your overlay stuff in your media queries.

IE8 doesn't support background-size: cover !

With IE8, filter sizingMethod='scale' can work to help solve the problem. But you have to be careful because it doesn't preserve the aspect ratio like cover does. It forces your image to the dimensions of your container. That will skew your image.

So you need to combine it with another trick. Instead of setting the height to a set value, you use height: 0 and padding-top: X% to simulate height. I find it easiest to manually play with the padding-top percent until you find the right height which matches your aspect ratio. It's amazing that it works to preserve the aspect ratio during resizing...allowing the filter sizingMethod='scale' combo to work like cover!

This is my overlay text!

Here is the css for that

.backgroundImageContainer{
    background-image: url('img/Unbalance1.jpg');
    height: 0; /* height: 513px; */
    padding-top: 28%; /* play with this until you find the perfect height. */
    background-size: cover;
        filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(
        src='img/Unbalance1.jpg',
        sizingMethod='scale');
        
        -ms-filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(
        src='img/Unbalance1.jpg',
        sizingMethod='scale');   
    overflow:hidden;
    background-position: 0 50%; /* doesn't work if using filter above in ie8 */
    	-ms-background-position-y: 50%; /* doesn't work if using filter above in ie8 */
    position: relative;
}
.overlayTextContainer{
	position: absolute;
	bottom: 0%;
}
.overlayTextContainer > p{
	color: white;
	padding:15px;
	font-size:30px;
}

It still has some trade offs. If you use filter sizingMethod to scale, you lose the background-position ability to keep the focus on the middle or wherever. If you went the other way, you could use -ms-background-position-y: 50%; after background-position: 0 50%; as an ie8 fallback, but you'd have to forgo using the filter sizingMethod.



Approach #2: Using an IMG tag

Well, it's not quite as simple as that. But that is an easy way to contrast/compare to the css background-image method.

This has an easy starting point, img tag in a div. With the img having a width:100% and height: auto style applied.

<div class="heroImgContainer">
  <img src="img/Unbalance1.jpg" alt="">
</div>

with this css:

.heroImgContainer > img{
    max-width:100%;
    height: auto;
}

Here is where we run into the biggest problem. There isn't a good way to set the focus point during resize. We'd could swap out different images but on a particular image, we're stuck with upward image creep. :(