Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why baseline of `inline-block` element with `overflow:hidden` is set to its bottom margin?

After reading two great answers explaining the behaviour of inline-block elements (Why is this inline-block element pushed downward? and why the span's line-height is useless) I still have two unexplained questions.

1. What the reason to change baseline of inline-block element from baseline of its line box to bottom margin edge?

http://www.w3.org/TR/CSS2/visudet.html#leading

The baseline of an 'inline-block' is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its 'overflow' property has a computed value other than 'visible', in which case the baseline is the bottom margin edge.

2. How to calculate this shift?

enter image description here

Important: I don't try to find a solution how to fix it. I try to understand what was the reason to change positioning behaviour of inline-block element when it is applied overflow: hidden. So please, don't post answers for dummies.

UPDATE

Unfortunately I didn't get what I want although I accepted the answer. I think the problem in the questions itself. Regarding the first question: I wanted to understand why inline-block can't preserve baseline of its line box even if it has overflow:hidden (despite of W3C specification of course). I wanted to hear the design decisions - not just it must be set to something, because W3C it mandates. The second one: I want to get a formula where we can paste font-size and line-height of an element and get the correct result.

Anyway thanks to anybody :)

UPDATE 2

Fortunately and subjectively the answer is found! See the first re-accepted answer. Thank you, @pallxk!)

like image 762
Timur Fayzrakhmanov Avatar asked Aug 18 '15 17:08

Timur Fayzrakhmanov


People also ask

Does margin top and margin bottom affect inline elements?

Top and bottom margins do not affect inline elements because inline elements flow with content on the page. You can set left and right margins/padding on an inline element but not top or bottom because it would disrupt the flow of content.

Does setting padding top and padding bottom on an inline element add to its dimensions?

When it comes to margins and padding, browsers treat inline elements differently. You can add space to the left and right on an inline element, but you cannot add height to the top or bottom padding or margin of an inline element.

Why margin auto does not work in inline block?

Auto margins set the div horizontally in the center (horizontal placement). So, if you use inline-block and auto margins at the same time, you are trying to place the div horizontally in two different ways that clash with each other.

Do inline elements have margins?

The margin-inline CSS shorthand property is a shorthand property that defines both the logical inline start and end margins of an element, which maps to physical margins depending on the element's writing mode, directionality, and text orientation.


2 Answers

1. What the reason to change baseline of inline-block element from baseline of its line box to bottom margin edge?

The baseline of an 'inline-block' is changed to its bottom margin edge when its overflow property is set to hidden (full specification here).

As for the reason for this decision, I think since the overflown part is hidden, user agents (browsers) may choose to render that overflown part and not display it, or choose to not render it at all. And when the overflown part is not rendered, user agents have no way to tell the baseline of its last line box, as it is not rendered, where it goes is not known.

If the baseline of 'inline-block' whose overflow is set to hidden is still kept as the baseline of its last line box, user agents are forced to render what is hidden to user, which may hinder performance, or at least, put extra restrictions on user agents. What's more, in such case, other inline texts in the same line box are aligned to such a baseline where texts around the overflow-hidden inline-box is hidden, which is kind of stange and not intuitive.

I made a simple demo emulating that inline-block with overflow hidden still has its baseline set to the baseline of its last line box.

emultaing_imaginary_baseline_of_overflow_hidden_inline_block

var isOverflowHidden = false;
document.querySelector('button').onclick = function() {
  document.getElementById('inline-box').style.overflow = isOverflowHidden ? '' : 'hidden';
  isOverflowHidden = !isOverflowHidden;
}
html { background: white; }
#inline-box { display: inline-block; height: 18px; }
.overflown { color: white; }
<p><button id="toggle">Toggle 'overflow: hidden;' on 'inline-block'</button></p>

<span>
  texts sit
  <span id="inline-box">
    texts in inline-block <br>
    <span class="overflown">
      line 2 <br>
      line 3
    </span>
  </span>
  on baseline
</span>

Besides, you may also compare this behavior with display: none. When that's set, clientWidth and clientHeight both equates to 0.

2. How to calculate this shift?

This part is much easier, since it's documented in the link you gave in the question.

I'll start from the definition of 'line-height'.

The height of the inline box encloses all glyphs and their half-leading on each side and is thus exactly 'line-height'.

That is, line-height is composed of, from top to bottom, top half-leading + height(ascent) + depth(descent) + bottom half-leading.

Height of each component can be calculated for a given font at a given size.

Basically, every font has font metrics that specify a characteristic height above the baseline and a depth below it.

Take 'Times New Roman' as an example, using FontForge, we see that it has Em Size as 2048, HHead Ascent as 1825, and HHead Descent as -443. That is, 1825 / 2048 = 89.1% of font-size contributes to the ascent, and 443 / 2048 = 21.6% contributes to the descent.

FontForge

There are also metrics starting with 'Typo', that category will be used if 'Really use Typo metrics' is checked, and the spec recommends this:

Note. It is recommended that implementations that use OpenType or TrueType fonts use the metrics "sTypoAscender" and "sTypoDescender" from the font's OS/2 table for A and D (after scaling to the current element's font size). In the absence of these metrics, the "Ascent" and "Descent" metrics from the HHEA table should be used.

Line-height minus ascent and descent is the so-called leading.

Half the leading is added above A(ascent) and the other half below D(descent).

Assuming a font-family: Times New Roman; font-size: 100px; line-height: 200px;, we get

ascent = 100px * (1825 / 2048) = 89px
descent = 100px * (443 / 2048) = 22px
top half-leading = bottom half-leading = (200px - 89px - 22px) / 2 = 44.5px

So we see that this can be calculated. And this can also be measured on pages.

Here is another demo for you to fiddle with.

If you are asking for the shift of bottom half-leading, it's shown as space between green line and blue line in the code snippet. If you are asking for the shift of descent and bottom half-leading, it's shown as space between red line and blue line in the code snippet.

var $ = document.querySelector.bind(document);

var fontFamily = window.getComputedStyle($('#examinee'))['font-family']
  , fontSize = +window.getComputedStyle($('#examinee'))['font-size'].replace('px', '')
  , containerLineHeight = +window.getComputedStyle($('#examinee'))['line-height'].replace('px', '')
  , textLineHeight = $('.target').offsetHeight
  , ascent = $('#examinee .baseline').offsetTop + $('#examinee .baseline').offsetHeight - $('#examinee .text-top').offsetTop
  , descent = $('#examinee .text-bottom').offsetTop - $('#examinee .baseline').offsetTop
  , topHalfLeading = $('#examinee .text-top').offsetTop
  , bottomHalfLeading = $('#examinee').offsetHeight - 2/* borders of the container */ - $('#examinee .text-bottom').offsetTop - $('#examinee .text-bottom').offsetHeight;

$('#font-family').innerText = fontFamily;
$('#font-size').innerText = fontSize + 'px';
$('#container-line-height').innerText = containerLineHeight + 'px';
$('#text-line-height').innerText = textLineHeight + 'px';
$('#ascent').innerText = ascent + 'px';
$('#descent').innerText = descent + 'px';
$('#top-half-leading').innerText = topHalfLeading + 'px';
$('#bottom-half-leading').innerText = bottomHalfLeading + 'px';
div {
  font-size: 20px;
  line-height: 2;
  width: 650px;
  
  border: 1px dashed gray;
  border-top: 1px solid blue;
  border-bottom: 1px solid blue;
  margin: 1rem 0;
  overflow: hidden;
  white-space: nowrap;
}

span:not([class]) {
  display: inline-block;
  border: 1px dashed gray;
}

.baseline,
.text-bottom,
.text-top {
  display: inline-block;
  width: 200%;
  margin: 0 -100%;
}

.baseline {
  border-bottom: 1px solid red;
  vertical-align: baseline;  /* the default */
}

.text-bottom {
  border-bottom: 1px solid green;
  vertical-align: text-bottom;
}

.text-top {
  border-bottom: 1px solid green;
  vertical-align: text-top;
}

#examinee {
  position: relative;
  font-size: 100px;
  line-height: 200px;
}
<p>
  Demonstrates that "overflow: hidden;" sets baseline of an inline-block element to its bottom margin.
</p>
<div>
  <span class="baseline"></span>
  <span class="text-top"></span>
  <span class="text-bottom"></span>
  &lt;div&gt;
  <span>
    &lt;span style=""&gt;&lt;/span&gt;
  </span>
  &lt;/div&gt;
</div>
<div>
  <span class="baseline"></span>
  <span class="text-top"></span>
  <span class="text-bottom"></span>
  &lt;div&gt;
  <span style="overflow: hidden;">
    &lt;span style="overflow: hidden;"&gt;&lt;/span&gt;
  </span>
  &lt;/div&gt;
</div>

<p>
  Demonstrates the position of baseline, text-top and text-bottom. <br>
  Demonstrates how "line-height" affects box sizing.
</p>

<ul>
  <li>Blue lines: top and bottom borders of line boxes
  <li>Red lines: baseline of texts
  <li>Green lines: text-top or text-bottom of texts
</ul>

<ul>
  <li>Between blue lines: the line-height
  <li>Between red line and green line: ascent or descent
</ul>

<div id="examinee">
  <span class="target">GgJjPpQqYy</span>
  <span class="baseline"></span>
  <span class="text-top"></span>
  <span class="text-bottom"></span>
</div>


Measured metrics:
<ul>
  <li>font-family: <span id="font-family"></span></li>
  <li>font-size: <span id="font-size"></span></li>
  <li>container line-height: <span id="container-line-height"></span></li>
  <li>text line-height: <span id="text-line-height"></span></li>
  <li>ascent: <span id="ascent"></span></li>
  <li>descent: <span id="descent"></span></li>
  <li>top half-leading: <span id="top-half-leading"></span></li>
  <li>bottom half-leading: <span id="bottom-half-leading"></span></li>
</ul>
line-height measuring
like image 85
pallxk Avatar answered Oct 16 '22 15:10

pallxk


This might not be the answer. but it might help to resolve this issue by removing the extra space in between inline-block elements.

<style>
.main_div {
    display:table;
    border-collapse:collapse;
    width:100%;
    border:1px solid red;
}
.main_div span {
    display:table-cell;
    border:1px solid black;
    height:20px;
    border:1px solid green;
}

</style>
<div class="main_div">
    <span class="one">one</span>
    <span class="two">two</span>
    <span class="three">three</span>
</div>
like image 34
aimme Avatar answered Oct 16 '22 15:10

aimme