Home  ·  Blog  ·  Projects  ·  Talks  ·  Recipes

A CSS-Only Overflow Indicator for Multiline Text

When building archive.observer, a page for browsing the AskHistorians subreddit archives, I wanted to preview the text for each post in a list:

Posts in that subreddit can be quite long, so I wanted to truncate the preview to a fixed number of lines. If that happened, I needed an indicator showing that there was more text available to read. I settled for a “fadeout” effect:

In this post, I’ll show you how to implement this indicator, along with a little trick that makes sure it works in all of these cases:

The Code

This is the code we’ll produce:

.hide-overflow {
    position: relative;
    max-height: 15.75rem;
    overflow: hidden;
}

.hide-overflow::after {
    position: absolute;
    height: 5.25rem;
    left: 0;
    right: 0;
    background: linear-gradient(transparent, white);
    content: " ";
    pointer-events: none;

    /* If text is shorter than max-height, push the 
    overflow indicator down so it's hidden by the overflow. */
    bottom: calc((100% - 15.75rem) * 20);
}

It assumes a HTML structure like this:

<div class="hide-overflow">
    <p>
        Lorem Ipsum dolor sit amet...
    </p>
</div>

Walkthrough

The CSS is mostly a standard affair. We give the container a class, set a max-height and hide the overflow:

.hide-overflow {
    position: relative;
    max-height: 15rem;
    overflow: hidden;
}

Using position: relative allows us to position the ::after pseudo-element relative to the bottom edges of our container:

.hide-overflow::after {
    position: absolute;
    height: 5rem;
    left: 0;
    right: 0;
    bottom: 0;
    /* ... */
}

So far, so good. Let’s make the indicator visible using a simple gradient:

.hide-overflow::after {
    /* ... */
    background: linear-gradient(transparent, white);
    content: " ";
    pointer-events: none;
    /* ... */
}

pointer-events: none ensures that users can still select text below the indicator.

Here’s the trick. We change the bottom style to actually hide this indicator when the text fits inside the containers max-height:

.hide-overflow::after {
    /* ... */

    bottom: calc((100% - 15rem) * 20);
}

100% - 15rem is the key: This expression is lower than zero if the actual height of the container (100%) is smaller than its maximum height (15rem), meaning the text didn’t overflow the container. If it is lower than zero, we multiply it by an arbitrarily large number (in this example it’s 20) to move the indicator below the bottom edge of the container, effectively hiding it. The whole thing is basically a roundabout way of using calc to “detect” if there’s any text overflow.

Here’s an example showing the position of the hidden indicator in green. In this case, the bottom property has a computed value of -160px:

Conclusion

It took me a little while to work out this trick and I found no alternatives with similar features on the web; Even though it’s a hackish workaround, I’m quite fond of its simplicity, and I think other websites might profit from it as well. Reddit, for example, seems to use JS to determine whether the overflow indicator should be visible or not.

I tried to implement the indicator using container queries, but couldn’t get around some of their limitations.