Recreating MDN’s Truncated Text Effect

It’s no secret that MDN rolled out a new design back in March. It’s gorgeous! And there are some sweet CSS-y gems in it that are fun to look at. One of those gems is how card components handle truncated text.

Pretty cool, yeah? I wanna tear that apart in just a bit, but a couple of things really draw me into this approach:

  • It’s an example of intentionally cutting off content. We’ve referred to that as CSS data loss in other places. And while data loss is generally a bad thing, I like how it’s being used here since excerpts are meant to be a teaser for the full content.
  • This is different than truncating text with text-overflow: ellipsis, a topic that came up rather recently when Eric Eggert shared his concerns with it. The main argument against it is that there is no way to recover the text that gets cut off in the truncation — assistive tech will announce it, but sighted users have no way to recover it. MDNs approach provides a bit more control in that department since the truncation is merely visual.

So, how did MDN do it? Nothing too fancy here as far the HTML goes, just a container with a paragraph.

<div class="card">
  <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore consectetur temporibus quae aliquam nobis nam accusantium, minima quam iste magnam autem neque laborum nulla esse cupiditate modi impedit sapiente vero?</p>
</div>

We can drop in a few baseline styles to shore things up.

Again, nothing too fancy. Our goal is cut the content off after, say, the third line. We can set a max-height on the paragraph and hide the overflow for that:

.card p {
  max-height: calc(4rem * var(--base)); /* Set a cut-off point for the content */
  overflow: hidden; /* Cut off the content */
}

Whoa whoa, what’s up with that calc() stuff? Notice that I set up a --base variable up front that can be used as a common multiplier. I’m using it to compute the font-size, line-height, padding for the card, and now the max-height of the paragraph. I find it easier to work with a constant values especially when the sizing I need is really based on scale like this. I noticed MDN uses a similar --base-line-height variable, probably for the same purpose.

Getting that third line of text to fade out? It’s a classic linear-gradient() on the pargraph’s :after pseudo-element, which is pinned to the bottom-right corner of the card. So, we can set that up:

.card p:after {
  content: ""; /* Needed to render the pseudo */
  background-image: linear-gradient(to right, transparent, var(--background) 80%);
  position: absolute;
  inset-inline-end: 0; /* Logical property equivalent to `right: 0` */
}

Notice I’m calling a --background variable that’s set to the same background color value that’s used on the .card itself. That way, the text appears to fade into the background. And I found that I needed to tweak the second color stop in the gradient because the text isn’t completely hidden when the gradient blends all the way to 100%. I found 80% to be a sweet spot for my eyes.

And, yes, :after needs a height and width. The height is where that --base variables comes back into play because we want that scaled to the paragraph’s line-height in order to cover the text with the height of :after.

.card p:after {
  /* same as before */
  height: calc(1rem * var(--base) + 1px);
  width: 100%; /* relative to the .card container */
}

Adding one extra pixel of height seemed to do the trick, but MDN was able to pull it off without it when I peeked at DevTools. Then again, I’m not using top (or inset-block-start) to offset the gradient in that direction either. 🤷‍♂️

Now that p:after is absolutely positioned, we need to explicitly declare relative positioning on the paragraph to keep :after in its flow. Otherwise, :after would be completely yanked from the document flow and wind up outside of the card. This becomes the full CSS for the .card paragraph:

.card p {
  max-height: calc(4rem * var(--base)); /* Set a cut-off point for the content */
  overflow: hidden; /* Cut off the content */
  position: relative; /* needed for :after */
}

We’re done, right? Nope! The dang gradient just doesn’t seem to be in the right position.

I’ll admit I brain-farted on this one and fired up DevTools on MDN to see what the heck I was missing. Oh yeah, :after needs to be displayed as a block element. It’s clear as day when adding a red border to it.🤦‍♂️

.card p:after {
  content: "";
  background: linear-gradient(to right, transparent, var(--background) 80%);
  display: block;
  height: calc(1rem * var(--base) + 1px);
  inset-block-end: 0;
  position: absolute;
  width: 100%;
}

All together now!

And, yep, looks sounds like VoiceOver respects the full text. I haven’t tested any other screen readers though.

I also noticed that MDN’s implementation removes pointer-events from p:after. Probably a good defensive tactic to prevent odd behaviors when selecting text. I added it in and selecting text does feel a little smoother, at least in Safari, Firefox, and Chrome.


Recreating MDN’s Truncated Text Effect originally published on CSS-Tricks. You should get the newsletter.

Text-overflow: ellipsis considered harmful

Eric Eggert:

There are a few legitimate use cases for this technique. For example, you might have a table with titles and descriptions. To preserve more space for the title, you constrain the description to one line on small viewports to the one-line and you repeat the description on the detail page for this item.

However, I often see it used on items like buttons or even form labels to make them look nicer(?) or when aligning them vertically. But once you change the viewport or resize the text, the end of the text disappears.

I think “… if used in certain situations” belongs there, but it certainly makes for a better blog post title without it. As Eric says, there are legitimate use cases for truncating text. Maybe only a few, but legitimate nonetheless.

The ultimate goal is to prevent “losing” data, something that can certainly happen in CSS. Text that inadvertently overflows a container is lost in the sense that it’s simply not there. And if that text is simply not there, users will miss it, even if it is the best and most well-crafted call to action ever published to the web.

Eric points out that there is no way to make the text truncated by text-overflow: ellipsis visible. Once it’s gone, it’s gone (although screen readers seem to announce it). It’s practically lost data. You might be OK with that. That’s cool as long as you know what’s happening and it’s intended.

But here’s what Eric says that made me want to share this:

Don’t constrain the content to fit your design, make your CSS flexible to handle longer words gracefully.

Again, you might want to conform content to the design. But I’d probably argue, like Eric, that the design should adapt to the content rather than the other way around. I have a hard time recalling any situation where the text on a page is unimportant or without purpose to the extent that I’d be cool cutting if off at any arbitrary point determined by a CSS property. Maybe an archive of blog posts where each post shows an excerpt of the post content before truncating, but that’s not exactly a use case for text-overflow: ellipse.

CSS has the tools to make a flexible design that accounts for varying lengths of text. So maybe err on the side of writing defensive CSS… CSS that anticipates issues and knows how to gracefully handle different content scenarios. text-overflow: ellipsis might be part of your CSS arsenal for that. But it might also be throwing the baby out with the bath water. Worth asking whether losing that data is worth the cost of what that content is supposed to do before giving giving it a haircut.

While we’re talking about truncating text…

To Shared LinkPermalink on CSS-Tricks


Text-overflow: ellipsis considered harmful originally published on CSS-Tricks. You should get the newsletter.

Clipping Scrollable Areas On The inline-start Side

On a default left-to-right web page, “hanging” an element off the right side of the page (e.g. position: absolute; right: -100px;) triggers a horizontal scrollbar that scrolls as far as needed to make that whole element visible. But if you hang an element of the left side of the page, it’s just hidden (no scrollbar is triggered). That’s called “data loss” in CSS terms, if you’re fancy. The same is true for the top edge of the page (hidden) and bottom of the page (scrolling happens).

Ahmad puts a point on this. It’s just one of those CSS things that you just need to know. I love how Ahmad uses logical directions like inline-start direction (and block-start direction) to describe the issue because those directions change when the direction or writing mode of the page changes. If the page is right-to-left (RTL) like <html dir="rtl">, then horizontal edges where the data loss occurs are flipped (except in Firefox 🤷‍♀️).

Direct Link to ArticlePermalink


The post Clipping Scrollable Areas On The inline-start Side appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.