首页 > 解决方案 > 尽管有 `line-height: 1` 和 `overflow: hidden`,仍保持悬挂字符可见

问题描述

目标

在此处输入图像描述

<div class="Card">
  <div class="Card-FullNameLabel">Gregg Sims</div>
  <div class="Card-OrganizationNameLabel">Compubotics</div>
</div>
  1. .Card-FullNameLabelfont-size: 16pxline-height: 1。_
  2. .Card-OrganizationNameLabelfont-size: 12pxline-height: 1。_
  3. .Card-FullNameLabel和之间的垂直空间.Card-OrganizationNameLabel必须恰好是6px
  4. 以下 CSS 规则必须有效且不得更改。
.Card-FullNameLabel + .Card-OrganizationNameLabel {
  margin-top: 6px;
}
  1. 两者.Card-FullNameLabel和都.Card-OrganizationNameLabel必须具有溢出容限(例如,如果此内容类似于等ÀÇĤjgpfhjklbĜiEstosTreMalfaci,则它不能从父对象悬垂)。
  2. 尽管有 ,所有字母都必须完全可见line-height: 1
  3. 不允许在 CSS 代码中使用心算(幻数和/或硬编码偏移量和其他必须预先计算的值)。

可以做什么:使用 Pug 预处理器的功能进行标记和 CSS 预处理器的样式。

初始小提琴不满足条件编号5:当前卡不具有溢出容错性。

在此处输入图像描述

关于line-height: 1,不好的做法

我一再被告知我必须将line-height值设置为超过1.

很明显,设置 line-height: 1 是一种不好的做法。我提醒你,无单位值是相对于字体大小的,而不是相对于内容区域的,处理小于内容区域的虚拟区域是我们许多问题的根源。

深入研究 CSS:字体度量、行高和垂直对齐

好吧,我不会为此争论。我想要的只是实现我的目标的工作解决方案(如上所述)。使用它是我的责任,如果您同意line-height必须超过1.

但是为什么我不想line-height那么执着地增加呢?

原因一:精确定义两个元素之间的垂直空间会变得过于复杂

该规则.Card-FullNameLabel + .Card-OrganizationNameLabel { margin-top: 6px; }清晰、直观,并通过 CSS 表达了准则(如上图所示)。“.Card-OrganizationNameLabel必须退出.Card-FullNameLabel6 个像素”,仅此而已。

但是,如果我们需要在行高大于 1(或者它们具有顶部和底部填充).Card-FullNameLabel之间定义相同的垂直空间怎么办?.Card-OrganizationNameLabel规则的值margin-top(由下图中未覆盖的粉红色区域可视化).Card-FullNameLabel + .Card-OrganizationNameLabel现在是:

  1. 所需范围(6px)
  2. 下面的额外垂直空间.Card-FullNameLabel(指定为l_b
  3. 上面的额外垂直空间.Card-OrganizationNameLabel(指定为l_a

在此处输入图像描述

正如我上面所说,心算是不允许的,因为它会贬低编程(CSS 案例中的 CSS 预处理器功能)并产生灵活性/可维护性影响(如果我们更改标签之间的line-heightfont-size或所需的垂直空间,一切都需要在心理上重新 -计算)。

虽然预处理器的变量(今天在原生 CSS 中可用)可以解决这个问题,但维护起来太复杂了。要计算上图中不相交的红色粉红色,我们需要:

  1. 变量font-size.Card-FullNameLabel
  2. 变量line-height.Card-FullNameLabel
  3. 计算下面的额外空间.Card-FullNameLabel
  4. 变量font-size.Card-OrganizationNameLabel
  5. 变量line-height.Card-OrganizationNameLabel
  6. 计算下面的额外空间.Card-OrganizationNameLabel
  7. 将所需范围可变为.Card-FullNameLabel.Card-OrganizationNameLabel(在本例中为 6 个像素)。

在此之后,我们最终可以计算margin-top规则的.Card-FullNameLabel + .Card-OrganizationNameLabel。每对元素都一样,比如.Card-FullNameLabeland .Card-OrganizationNameLabel!! 2020 年代 Web 开发的技术太差了。

原因2:不需要每种语言

在下面的示例中,日文符号完全符合line-height: 1(16px):

在此处输入图像描述

我想中文、韩文和许多其他非拉丁字符的语言也是如此。

但是:在少数情况下,可能会混合使用外国符号:

在此处输入图像描述

如果要谈高品质,这个案子是必须要支持的。

我不想仅仅因为这个例外而增加行高。线与线之间的垂直空间实际上变成了没关系:或6px的尾巴重量很小,不会破坏几何美学。jÀ

我的努力

尝试1:使用:beforeand:after

SASS-mixinTextTruncation接受$extraSpace添加顶部和底部填充的参数。和伪元素通过:before:after边距补偿此填充。

@mixin TextTruncation($extraSpace, $displayEllipsis: false) {
  
  overflow: hidden;
  white-space: nowrap;
  
  @if ($displayEllipsis) {
    text-overflow: ellipsis;
  } @else {
    text-overflow: clip;
  }
  
  padding-top: $extraSpace;
  padding-bottom: $extraSpace;
  
  &:before,
  &:after {
    content: "";
    display: block;
  }
  
  &:before {
    margin-top: -$extraSpace;
  }
  
  &:after {
    margin-bottom: -$extraSpace;
  }
}

body {
  padding: 12px;
}

* {
  line-height: 1;
  font-family: Arial, sans-serif;
}

.Card {
  
  display: flex;
  flex-direction: column;
  align-items: center;
  
  width: 240px;
  height: 320px;
  padding: 6px 12px 12px;
  
  background: white;
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
}

.Card-FullNameLabel {
  
  max-width: 100%; /* Required when the flex parent has `align-items: center` */
  @include TextTruncation($extraSpace: 2px, $displayEllipsis: true);
  
  font-size: 16px;
  color: #707070;
}

.Card-OrganizationNameLabel {
  
  max-width: 100%; /* Required when the flex parent has `align-items: center` */
  @include TextTruncation($extraSpace: 2px, $displayEllipsis: true);
  
  font-size: 12px;
  color: #A2A2A2;
}

.Card-FullNameLabel + .Card-OrganizationNameLabel {
  margin-top: 6px;
}

不幸的是,它不起作用:效果就像没有定义边距和填充一样:

在此处输入图像描述

代码笔

尝试2:使用包装器

如果 和 的组合overflow-x: hidden有效overflow-y: visible,那就是解决方案。但它不起作用,这个问题已经在问题CSS overflow-x: visible;中考虑过。和溢出-y:隐藏;导致滚动条问题

我想尽可能地使用包装器,但在这里看起来包装器将是最后的手段。为了避免每次都写两个标签,我创建了 Pug mixin:

mixin SingleLineLabel

  span.SingleLineLabel&attributes(attributes)
    span.SingleLineLabel-Text
      block

好吧,SingleLineLabel现在是一个组件。除了 Pug mixin 之外,还需要定义基本样式,SASS mixin 允许单独自定义标签:

// Constant styles
.SingleLineLabel {

  overflow-y: visible;

  &:before,
  &:after {
    content: "";
    display: block;
  }


  &-Text {
    display: block;
    overflow-x: hidden;
    white-space: nowrap;
  }
}

// Variable styles
@mixin SingleLineLabel($truncatedVerticalSpaceCompensation, $displayEllipsis: false) {
  
  &:before {
    margin-top: -$truncatedVerticalSpaceCompensation
  }

  &:after {
    margin-bottom: -$truncatedVerticalSpaceCompensation
  }
  

  .SingleLineLabel-Text {
    
    padding-top: $truncatedVerticalSpaceCompensation;
    padding-bottom: $truncatedVerticalSpaceCompensation;
    
    @if ($displayEllipsis) {
      text-overflow: ellipsis;
    } @else {
      text-overflow: clip;
    }
  }
}

现在我们可以应用它了:

.Card-FullNameLabel {
  
  max-width: 100%; /* Required when the flex parent has `align-items: center` */
  @include SingleLineLabel($truncatedVerticalSpaceCompensation: 1px, $displayEllipsis: true);
  
  font-size: 16px;
  color: #707070;
}

.Card-OrganizationNameLabel {
  
  max-width: 100%; /* Required when the flex parent has `align-items: center` */
  @include SingleLineLabel($truncatedVerticalSpaceCompensation: 2px, $displayEllipsis: true);
  
  font-size: 12px;
  color: #A2A2A2;
}

.Card-FullNameLabel + .Card-OrganizationNameLabel {
  margin-top: 6px;
}

似乎已经达到了目标:

在此处输入图像描述

代码笔

不幸的是,它存在发生规律不清楚的错误。有时会出现小的垂直滚动条。

在此处输入图像描述

我真的不知道怎么重现,但是在过去的实验中出现过,例如,如果通过开发工具将浏览器切换到设备模拟模式然后退出该模式。如果在小提琴中重复相同的实验,很可能不会得到相同的效果。

最后

基于您出色答案的解决方案将包含在不断增长的@yamato-daiwa/frontend库中。

如果您有问题符号的完整列表,例如gpÀĤ,请分享它 - 我将使用它进行测试,并将它们添加到未来的 pug 功能中以进行溢出容限测试。

标签: htmlcss

解决方案


我知道您明确表示您需要保留line-height: 1and margin-top: 6px,但是正如您在记录的溢出 CSS 问题中确定的那样,您有点受制于当前的限制。

如果完全有可能对这些限制保持灵活,我有一个在视觉上与您最初追求的解决方案相同的解决方案。

原始状态

我从您的Initial Fiddle开始,并在 html 中添加了省略号截断 CSS 和有问题的文本。

.Card-FullNameLabel,
.Card-OrganizationNameLabel {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

结果就是这种状态,我称之为“原始”,因为它使您的line-heightmargin保持不变。请注意,我添加了一条overflow: hidden规则来代替有问题的overflow-xoverflow-y规则混合。

建议修复

我建议进行以下 CSS 更改。这增加到line-height允许1.5所有字体的上升和下降都可见。然后我添加了负偏移边距来补偿:

.Card-FullNameLabel,
.Card-OrganizationNameLabel {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;

  /* Shows all ascenders and descenders */
  line-height: 1.5;
}

.Card-FullNameLabel {
  font-size: 16px;
  color: #707070;

  /* Compensates for line-height */
  margin: -4px 0;
}

.Card-OrganizationNameLabel {
  font-size: 12px;
  color: #A2A2A2;

  /* Compensates for line-height */
  margin: -3px 0;
}

.Card-FullNameLabel + .Card-OrganizationNameLabel {
  /* 6px visually (minus 3px) */
  margin-top: 3px;
}

结果可以在这里看到,我将其称为“建议的修复”。我已经确认结果在 MacOS 上的最新桌面 Chrome、Firefox 和 Safari 以及 iOS 上的 Mobile Safari 中是一致的。

比较

我从“之前”和“之后”屏幕截图中制作了一个简单的动画,展示了输出在视觉上是相同的,只是建议的修复不会切断字体的上升和下降。

请注意,您可以单击动画来查看全尺寸、1:1 像素精度的版本。

在此处输入图像描述

我用我称之为“中间元素”的东西做了一些额外的测试,以证明即使中间有元素,建议的修复也会与原始修复相同。

在此处输入图像描述


更新:自动化

正如评论中明确指出的,要求之一是 CSS 中没有“硬编码”或“魔术”数字。因此,虽然上述解决方案有效,但它需要提前手动算术。

这是一个更新的Codepen,它可以通过使用一些 SCSS 逻辑自动生成与上面显示的相似的 CSS,该逻辑将根据这些输入值计算偏移量:

多变的 当前值
$globalLineHeight 1
$minLineHeight 1.5
$fullNameFontSize 16px
$fullNameLineHeight $globalLineHeight
$orgNameFontSize 12px
$orgNameLineHeight $globalLineHeight
$orgNameMarginTop 6px

出于演示目的,我添加了一些额外的代码来显示“之前”和“之后”悬停效果,以便您可以看到与原始 CSS 相比 SCSS 逻辑的行为方式。正如 HTML 和 CSS 中所指出的,您可以删除以 . 开头的行以下的任何内容#DELETE-ME


推荐阅读