Smooth text overlays with CSS transforms

— Updated

Images with text overlays are one of only a few design patterns to have successfully transferred from print and bread and butter for most front-end developers, but sometimes they’re not so simple given the limitations of CSS. Stylesheets can now be written with clear separation between layout and visual effects and this “image with text overlay” or “caption” component demonstrates a few performant and neat techniques.

The markup

<article class="caption">
    <img class="caption__media" src="/path/to/image.jpg" />
    <div class="caption__overlay">
        <h1 class="caption__overlay__title">Caption title</h1>
        <p class="caption__overlay__content">Lorem ipsum doler sit dui amet.</p>
    </div>
</article>

I’m using the BEM class naming convention to de-couple my HTML from my layout but still maintain structural sense for a developer. An <article> element with a heading such as the example above could work well on a site homepage to highlight further content but it may be more suitable to use the <figure> and <figcaption> elements if the image is part of a larger piece of content.

The CSS

.caption {
    position: relative;
    overflow: hidden;
}
.caption__media {
    display: block;
    min-width: 100%;
    max-width: 100%;
    height: auto;
}
.caption__overlay {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    padding: 10px;
    color: white;
    transform: translateY(100%);
    transition: transform .35s ease-out;
}
.caption:hover .caption__overlay {
    transform: translateY(0);
}

The setup of the container and overlay is business as usual, the overlay is positioned over the image, sized to fit its container and moved below the visible area, ready to roll up on mouse over. Rather than adjust the absolute positioning of the overlay I’ve opted to use a 2D transform which has the advantage of not requiring the browser re-calculate any layout when visually moving the element around.

The next part is tricky, the overlay title must always be visible and aligned to the bottom of the container even if the text wraps onto multiple lines, but to do so would require knowing the vertical height of the element which is a property that’s not easily exploitable with only CSS.

CSS layout percentage values are calculated based on the width of an element’s containing block so setting the top margin of the title to -100% or using relative positioning would not work. Fortunately CSS transform functions are relative to the dimensions of the element they’re acting upon so the title can be moved by exactly the right amount using translateY(). Any padding applied to the overlay can be accounted for by adjusting the value with the calc() function. On mouse over the overlay title moves back into its original layout position which gives a nice elastic effect. Note: An elements margins are not calculated as part of its dimensions, so I’m applying padding to the bottom of the heading.

.caption__overlay__title {
    margin: 0;
    padding: 0 0 12px;
    transform: translateY( calc(-100% - 10px) ); /* +10px overlay padding */
    transition: transform .35s ease-out;
}
.caption:hover .caption__overlay__title {
    transform: translateY(0);
}

There is no guarantee that the contrast between the overlay text and the image will be great enough to ensure the text is always readable, so adding a background or shadow to the text is sensible. I’ve decided to impose a dark colour over the image when the text overlay is active using pseudo-content which avoids adding another element to the page when it’s only for display purposes.

.caption::before {
    content: ' ';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: transparent;
    transition: background .35s ease-out;
}
.caption:hover::before {
    background: rgba(0, 0, 0, .5);
}

Lastly, I’ve promoted the entire component to a hardware-accelerated layer by using a 3D transform because pushing the rendering of the elements onto a device’s graphics hardware will make the transitions much smoother.

.caption {
    ...
    transform: translateZ(0);
}

Separation of concerns

Sometimes even simple layout problems can be tricky with CSS but transforms have introduced a powerful layer of presentational properties that combine with existing layout techniques to make some very elegant solutions. The separation of concerns is common to all programming and now within CSS there is the separation between layout and visual effects which is very good news indeed.

View the source on JSFiddle

comments powered by Disqus

A photo of Matt Hinchliffe

About Me

I'm a 29 year old web developer building new stuff at the Financial Times based in London. I specialise in crafting scalable, performance-driven code, tackle accessibility issues and keep an opinionated interest in the latest hotness. I like my tea robustly brewed, white and with no sugar, thanks!