Line Clampin’ (Truncating Multiple Line Text)

Avatar of Chris Coyier
Chris Coyier on (Updated on )

Take your JavaScript to the next level at Frontend Masters.

You want X lines of text. Anything after that: gracefully cut off. That’s “line clamping” and it is a perfectly legit desire. When you can count on the text being a certain number of lines, you can create stronger and more reliable grids from the elements that contain that text, as well as achieve some symmetric aesthetic harmony.

There are a couple of ways to get it done, none of them spectacular.

In case that explanation wasn’t clean, imagine you have some HTML like this:

<div class="module">
  <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
</div>

And you want to limit it to exactly three lines in a container. Like this:

In all these examples, let’s assume we have a “module”.

.module {
  width: 250px;
  overflow: hidden;
}

The Standardized Way

I used to call this “the weird WebKit flexbox way”, but in an extra weird twist, the spec now includes this as part of the overflow module, old flexbox and all. And, Firefox implemented it just like that. And with Edge-gone-Chromium, this weird technique has gotten a lot more useful instead of less.

.line-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;  
}

Despite the weird syntax, this is awesome and exactly what we need. Here’s a playground for playing with it:

To be fair, it really is weird. Why does it need to be a flexbox thing (the old version at that)? It doesn’t work without that. And it’s extremely fragile. Let’s say you want the module (or the paragraph) to have some padding. You can’t because the padding will expose extra lines. That’s what we get with sorta half-baked originally-non-standardized properties.

The Fade Out Way

The root of this technique is just setting the height of the module in a predictable way. Let’s say you set the line-height to 1.2em. If we want to expose three lines of text, we can just make the height of the container 3.6em (1.2em × 3). The hidden overflow will hide the rest.

But it can be a bit awkward to just cut the text off like that. Ideally, we would add ellipsis, but we can’t reliably position them. So instead, we’ll fade out the text achieving the same kind of communication (“there is more…”).

To fade out the last line, we make a box (a pseudo-element will work great) and overlay a transparent-to-background-color gradient over the top. Making it nearly as wide as the container is best in case the last line is short. Because we know the line-height, we can make the pseudo-element box exactly one line tall.

.fade {
  position: relative;
  height: 3.6em; /* exactly three lines */
}
.fade::after {
  content: "";
  text-align: right;
  position: absolute;
  bottom: 0;
  right: 0;
  width: 70%;
  height: 1.2em;
  background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1) 50%);
}

The Opera Overflow Way

Opera as its own rendering engine, is long defunct. Just leaving this in for historical reasons.

Like WebKit, Opera has its own way to handle this. They apply ellipsis on the line you wish to. Of course, the clock is ticking for Presto (Opera’s rendering engine pre-Blink) so this isn’t particularly useful. Perhaps it can still inform a future implementation though.

.last-line {
  height: 3.6em; /* exactly three lines */
  text-overflow: -o-ellipsis-lastline;
}

The Clamp.js Way

Where there is a will there is a way (with JavaScript). I think that’s a saying. Joseph J. Schmitt has an excellent library-free JavaScript thing called Clamp.js for making this happen in a cross-browser way.

var module = document.getElementById("clamp-this-module");

$clamp(module, {clamp: 3});

Make sure you target the element with the text directly inside of it. Originally I tried putting the ID on the module, which worked in Chrome/Safari, but failed in Firefox. Putting it on the <p> made it work in both (Thx Merri).

The Hide Overflow & Place Ellipsis Pure CSS Way

The trick is to set a max-height equal to the maximum number of lines multiplied by the line-height.

html {
  --lh: 1.4rem;
  line-height: var(--lh);
}

.truncate-overflow {
  --max-lines: 3;
  position: relative;
  max-height: calc(var(--lh) * var(--max-lines));
  overflow: hidden;
  padding-right: 1rem; /* space for ellipsis */
}
.truncate-overflow::before {
  position: absolute;
  content: "...";
  inset-block-end: 0; /* "bottom" */
  inset-inline-end: 0; /* "right" */
}
.truncate-overflow::after {
  content: "";
  position: absolute;
  inset-inline-end: 0; /* "right" */
  width: 1rem;
  height: 1rem;
  background: white;
}

The rest of that is to place an “…” ellipsis at the end of the lines but only if the text exceeds the maximum lines.

The Demos

Update: More Good Ways!

  • There is an exceptionally clever all-CSS way to do this posted on the Mobify blog Update: removed link, dead blog, added in the technique here.
  • Vesa Piittinen created an alternative method to Clamp.js.
    “Unlike Clamp.js it retains all the text within the clamped element and uses text-overflow to do the magic.”
  • FT Labs also has created a JavaScript plugin to do the job. It’s nice in that you don’t have to specify the number of lines, it just happens when the text overflows the container, so the design decision stay in the CSS.
  • Succinct: “A tiny jQuery plugin for truncating multiple lines of text.”

These examples have been added to the main Pen.

There is also Shave from DollarShaveClub: