When design moves online for content experiences, it demands mixing art with the science of dev and coding.
Text over an image looks really nice in a magazine, and it can look nice on the web too. Such design can be challenging to implement responsively so the experience works across platforms of different sizes.
For a prime example of what we’re working toward, check out the featured article teaser on the homepage of HowLifeUnfolds.com. In particular, note how it responds to different screen widths.
So what does it take to do that? We’re going to peel back the design to share a coding approach that can make such experiences possible. We’ll take a deep dive into the technical requirements for displaying text over an image and explore an awesomely responsive solution.
The Hurdles of Placing Text on an Image
The simplest method to get text over image (or an image under text) is to add a background image using cascading style sheets, or CSS. There are a few drawbacks to this simple technique:
- You can’t use an alt tag to describe the image, which hurts your site’s accessibility.
- A background image doesn’t take up space in the document object model, which makes your site less responsive. Background images can grow as more text is added, but you have to add an explicit minimum height if you want an image to have a minimum height. But it’s not really responsive if you’re setting a min-height.
- Background images can be clumsy to work with in the content management system. Drupal, for instance, is much better at producing <img> tags to insert images on a webpage than it is at setting background images.
Having struggled with this problem many times, I finally came up with a solution that is close to ideal. The solution leverages the object-fit CSS property along with CSS transforms to make an <img> tag behave a lot like a background image while still ensuring alt text, implicit min-height and CMS ease. The object-fit property is well supported for <img> tags, which is the only element on which we will use it.
The Essential Code
Let’s assume you’re working with a flexbox-based grid system such as Bootstrap 4. Start with markup for a row holding two half-width columns.
<div class="card">
<div class="row text-over-image">
<div class="col-6 image"><img src="some-image.jpg"/></div>
<div class="col-6 text">Some text over image</div>
</div>
</div>
Let’s walk through our code in English first.
1. Hide overflow on the card.
2. Make the row have a 200 percent width.
If you stopped there, the image would take up the whole card and the text would not be visible. (It would be to the right of the card, where overflow is hidden.) So obviously, you don’t stop there.
3. Translate the text column left by 100 percent.
4. Set the object-fit property of the image such that it can handle any amount of text.
Now, here’s the above in CSS.
<style>
.card { /* Step 1 */
overflow: hidden;
}
.row.text-over-image { /* Step 2 */
width: 200%;
}
.text { /* Step 3 */
transform: translateX(-100%);
}
img { /* Step 4 */
height: 100%;
width: 100%;
object-fit: cover;
}
</style>
That’s it!
In practice, you’ll probably need more specific selectors, but I want to keep this simple. You’ll have typography and padding and stuff like that to worry about, but these are the essential styles for awesomely responsive text over an image. You can see and edit a demo on CodePen.io.
Really Responsive Code
I’ve already discussed the merits of an <img> tag over a background image. You can do really cool stuff when you add media queries to your stylesheet. Let’s say you wanted the card to look like a “normal” card on mobile. You want the image above the text. You probably want the image linked too.
So let’s prepare some new markup.
<div class="card">
<div class="row text-over-image">
<div class="col-md-6 image">
<a href="/some-article">
<img src="some-image.jpg"/>
</a>
</div>
<div class="col-md-6 text">Some text</div>
</div>
</div>
The grid system will give you a “normal” card on mobile. The styles below will give you text over image on desktop.
<style>
@media (min-width: 768) {
.card {
overflow: hidden;
}
.row.text-over-image {
width: 200%;
}
.text {
transform: translateX(-100%);
}
.image a {
height: 100%;
}
img {
height: 100%;
width: 100%;
object-fit: cover;
}
}
</style>
Pretty easy, right? That’s also on CodePen.
But let’s say you instead wanted the text to come before the image. That’s easy too. (This is the case that is demonstrated on the How Life Unfolds website.) The only gotcha is z-index. Otherwise, everything is pretty much the same.
<div class="card">
<div class="row text-over-image">
<div class="col-md-6 text">Some text</div>
<div class="col-md-6 image">
<a href="/some-article">
<img src="some-image.jpg"/>
</a>
</div>
</div>
</div>
<style>
@media (min-width: 768) {
.card {
overflow: hidden;
}
.row.text-over-image {
width: 200%;
}
.image {
transform: translateX(-100%);
}
.text { /* elevate the text over the image */
z-index: 1;
}
.image a {
height: 100%;
}
img {
height: 100%;
width: 100%;
object-fit: cover;
}
}
</style>
Once again, you can see a demo on CodePen. If you’re working in Drupal, a case like this is a great situation for the Responsive Image module, which is leveraged effectively in the How Life Unfolds example.
Don’t Forget About Site Accessibility
Don’t forget that text over image can be hard to read if not designed properly. I like to add a semi-opaque :before pseudo-element over my image to increase contrast. (You can’t actually put a :before pseudo-element on an <img> tag; it has to be placed on a tag that wraps the <img> tag.)
And remember, another benefit of this development method is that it lets you add alt text, so make sure to take advantage of that to increase your site’s accessibility.