重现 MDN 的截断文本效果

Avatar of Geoff Graham
Geoff Graham

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

众所周知,MDN 于 3 月份 推出了新的设计。它非常漂亮!并且其中包含一些有趣的 CSS 宝石,值得一看。其中一个宝石是卡片组件如何处理截断文本。

很酷,对吧?我很快就会将其拆解,但有几件事让我对这种方法产生了兴趣。

  • 这是一个有意截断内容的例子。我们在其他地方将其称为 CSS 数据丢失。虽然数据丢失通常是一件坏事,但我喜欢它在这里的用法,因为摘录旨在作为完整内容的预告。
  • 这与使用 text-overflow: ellipsis 截断文本不同,这个话题最近在 Eric Eggert 分享 他对此的担忧时被提及。反对它的主要论点是,无法恢复在截断中被截断的文本——辅助技术会宣布它,但有视力障碍的用户无法恢复它。MDN 的方法在这方面提供了更多控制,因为截断仅仅是视觉上的。

那么,MDN 是如何做到的呢?就 HTML 而言,这里没有什么太花哨的,只是一个包含段落的容器。

<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>

我们可以添加一些基本样式来巩固内容。

同样,没有什么太花哨的。我们的目标是在第三行之后截断内容。我们可以为段落设置一个 max-height 并隐藏该溢出内容。

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

等等,calc() 是怎么回事?请注意,我在前面设置了一个 --base 变量,它可以用作通用乘数。我用它来计算 font-sizeline-height、卡片的 padding,以及现在段落的 max-height。我发现使用常量值更容易,尤其是在我需要的尺寸像这样真正基于比例时。我注意到 MDN 使用了类似的 --base-line-height 变量,可能是出于同样的目的。

让第三行文本淡出?这是段落 :after 伪元素上的经典 linear-gradient(),它固定在卡片的右下角。因此,我们可以设置它。

.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` */
}

请注意,我调用了一个 --background 变量,该变量设置为与 .card 本身使用的相同背景颜色值。这样,文本看起来像是淡入背景。我发现我需要调整渐变中的第二个颜色停止点,因为当渐变完全混合到 100% 时,文本不会完全隐藏。我发现 80% 对我的眼睛来说是一个最佳点。

是的,:after 需要 heightwidthheight--base 变量再次发挥作用的地方,因为我们希望将其按比例缩放至段落的 line-height,以便使用 :after 的高度覆盖文本。

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

添加一个额外的像素高度似乎可以解决问题,但是当我查看 DevTools 时,MDN 能够在没有它的情况下实现它。再说一遍,我也不会使用 top(或 inset-block-start)来沿该方向偏移渐变。🤷‍♂️

现在 p:after 是绝对定位的,我们需要在段落上显式声明相对定位以保持 :after 在其流中。否则,:after 将完全从文档流中移除,并最终出现在卡片之外。这将成为 .card 段落的完整 CSS。

.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 */
}

我们完成了,对吧?不!该渐变似乎没有处于正确的位置。

我承认我在这一点上犯了一个错误,并在 MDN 上启动了 DevTools 以查看我错过了什么。哦,是的,:after 需要显示为块级元素。当为它添加红色边框时,这一点一目了然。🤦‍♂️

.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%;
}

现在都放在一起了!

并且,是的,VoiceOver 看起来听起来尊重完整文本。不过,我还没有测试任何其他屏幕阅读器。

我还注意到,MDN 的实现从 p:after 中删除了 pointer-events。这可能是一种很好的防御策略,可以防止在选择文本时出现奇怪的行为。我添加了它,并且在 Safari、Firefox 和 Chrome 中,选择文本确实感觉更流畅了一些。