#StackBounty: #html #css Keep hanging characters visible in spite of `line-height: 1` and `overflow: hidden`

Bounty: 50

Target

enter image description here

<div class="Card">
  <div class="Card-FullNameLabel">Gregg Sims</div>
  <div class="Card-OrganizationNameLabel">Compubotics</div>
</div>
  1. The .Card-FullNameLabel has font-size: 16px and line-height: 1.
  2. The .Card-OrganizationNameLabel has font-size: 12px and line-height: 1.
  3. The vertical space between .Card-FullNameLabel and .Card-OrganizationNameLabel must be exactly 6px.
  4. Below CSS rule must work and must NOT be changed.
.Card-FullNameLabel + .Card-OrganizationNameLabel {
  margin-top: 6px;
}
  1. Both .Card-FullNameLabel and .Card-OrganizationNameLabel must have overflow tolerance (e. g. if this content will be
    something like ÀÇĤjgpfhjklbĜiEstosTreMalfaci and so on it must not overhang from the parrent).
  2. All letters must be fully visible despite to line-height: 1.
  3. The mental arithmetic in CSS code is not allowed.

What is O’K to do: use the functionality of Pug pre-processor for markup and CSS pre-processors for styles.

🌎 Inital fiddle does not satisfied to the condition number 5: currently the card is not overflow-tolerant.

enter image description here

About line-height: 1, the bad practice

I has been repeatedly told about I must set line-height to value more than 1.

It becomes obvious that setting line-height: 1 is a bad practice. I
remind you that unitless values are font-size relative, not
content-area relative, and dealing with a virtual-area smaller than
the content-area is the origin of many of our problems.

Deep dive CSS: font metrics, line-height and vertical-align

Well, I don’t going to dispute about it. All I want is the working solution for the reaching of my target (descripted above).
The usage of it is my responsibility and I will not reсcommend this solution if you agree that line-height must be more than 1.

But why I don’t want increase the line-height so persistently?

Reason 1: The precise defining of the vertical space between two elements will become too complicated

The rule .Card-FullNameLabel + .Card-OrganizationNameLabel { margin-top: 6px; } is clear, intuitive and expresses the guidelines (represented in the picture above) by CSS. "The .Card-OrganizationNameLabel must retire from .Card-FullNameLabel by 6 pixels", and nothing more.

But what if we need to define the same vertical space between .Card-FullNameLabel and .Card-OrganizationNameLabel when line height is more than 1 (or they have the top and bottom paddings)? The value of the margin-top (visualized by non-overlayed pink area in the picture below) of .Card-FullNameLabel + .Card-OrganizationNameLabel rule now be the difference of:

  1. The desired range (6px)
  2. The extra vertical space below .Card-FullNameLabel (designated as l_b)
  3. The extra vertical space above .Card-OrganizationNameLabel (designated as l_a)

enter image description here

As I told above, the mental arithmetic is not allowed because it devalues the programming (CSS preprocessors capabilities in CSS case) and makes flexibility/maintainability impact (if we change the line-height or font-size or desired vertical space between labels, everything need to be mentally re-computed).

Although the preprocessor’s variables (today became available in native CSS) can solve this problem, it will be too complicated to maintain it. To compute the non-intersecting red pink in the image above, we need to:

  1. Variablelize the font-size of .Card-FullNameLabel
  2. Variablelize the line-height of .Card-FullNameLabel
  3. Compute the extra space below .Card-FullNameLabel.
  4. Variablelize the font-size of .Card-OrganizationNameLabel
  5. Variablelize the line-height of .Card-OrganizationNameLabel
  6. Compute the extra space below .Card-OrganizationNameLabel
  7. Variablelize the desired range between .Card-FullNameLabel and .Card-OrganizationNameLabel (6 pixels in this example).

After this, we can finally compute the margin-top for the rule .Card-FullNameLabel + .Card-OrganizationNameLabel. And same for each pair of elements like .Card-FullNameLabel and .Card-OrganizationNameLabel!! Too poor technology for the web development in 2020s.

Reason 2: It does not require for each language

In below example, the Japanese symbols are perfectly fits to line with line-height: 1 (16px):

enter image description here

I suppose same will be for the Chinese, Korean and many other languages with non-latin characters.

But: in the small percentage of cases, there the foreign symbols could be mixed:

enter image description here

If to talk about high quality, this case must be supported.

I don’t want increase the line height just for this exception. It’s OK that the vertical space between lines actually became not 6px: the tails of j or À has a small weight and it will not break the geometric aesthetics.

My efforts

Attempt 1: usage of :before and :after

The SASS-mixin TextTruncation accepts the parameter $extraSpace which adding top and bottom paddings. The :before and :after pseudo elements compensates this paddings by negative margins.

@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;
}

Unfortunately, It does not work: the effect is same as if no margins and no paddings has been defined:

enter image description here

🌎 CodePen

Attempt 2: usage of the wrapper

If the combination of overflow-x: hidden and overflow-y: visible works, it was the solution. But it does no work and this problem has been considered in the question CSS overflow-x: visible; and overflow-y: hidden; causing scrollbar issue.

I want to avid the wrappers as possible, but here it looks like the wrapper will be the last resort. To avoid of writing two tags each time, I created the Pug mixin:

mixin SingleLineLabel

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

Well, the SingleLineLabel now a component. Besides the Pug mixin it’s required to define the basic styles and SASS mixin allows to customize the label individually:

// 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;
    }
  }
}

Now we can apply it:

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

It seems like the target has been reached:

enter image description here

🌎 CodePen

Unfortunately, it has the bug which occurrence regularity is unclear.
Sometimes the small vertical scrollbar appearing.

enter image description here

I really don’t know how to reproduce it, but in the past experiment it has occurred, for example, if to switch the browser to device simulation mode by development tools and then exit from this mode. Most likely, you will not get the same effect if repeat same experiment in fiddle.

Finally

The solution based on your great answers will be included to growing @yamato-daiwa/frontend library.

If you have the full list of the problematic symbols like g, p, À, Ĥ and so on, please share it – I’ll use it for the tests and also add them to the future pug functionality for the overflow tolerance testing.


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.