How to Determine Height of Content-Box of a Block and Inline Element

How to determine height of content-box of a block and inline element

For block elements its quite easy but you need to distinguish between two different cases. When we have an IFC (inline formatting context) or BFC (block formatting context). From the specification you can read

If 'height' is 'auto', the height depends on whether the element has any block-level children and whether it has padding or borders:

The element's height is the distance from its top content edge to the first applicable of the following:

  1. the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
  2. the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child's bottom margin does not collapse with the element's bottom margin
  3. the bottom border edge of the last in-flow child whose top margin doesn't collapse with the element's bottom margin
  4. zero, otherwise

Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored, and relatively positioned boxes are considered without their offset)

If we consider an IFC then the line boxes will define our height exactly like your examples where in the div and p you have one line box defined by the line-height.

If we consider a BFC then the (2) and (3) apply. Here, you can see it as a recursive definition because having a BFC means we have other blocks inside and those blocks will follow the same rules (either BFC or IFC and so on). In addition, we consider the margin collapsing rules to get the final height.

If the block is empty then it's zero.

Of course, this consider the case of height:auto. If you explicitely set a height then it's trivial.


For inline elements you can refer to this part of the specification:

The 'height' property does not apply. The height of the content area should be based on the font, but this specification does not specify how. A UA may, e.g., use the em-box or the maximum ascender and descender of the font. (The latter would ensure that glyphs with parts above or below the em-box still fall within the content area, but leads to differently sized boxes for different fonts; the former would ensure authors can control background styling relative to the 'line-height', but leads to glyphs painting outside their content area.)

Here is a bit tricky but the rule is simple: you cannot control or set the height of the content area. Only the font properties you will be using will define the final height.

You should also note that the content area is different from the line box.

The vertical padding, border and margin of an inline, non-replaced box start at the top and bottom of the content area, and has nothing to do with the 'line-height'. But only the 'line-height' is used when calculating the height of the line box.

<span style="background-color: aquamarine; font-size: 16px; line-height: 2em;">Is line-height same as height of span tag?</span>
<br>
<span style="background-color: aquamarine; font-size: 16px; line-height: 4em;">Is line-height same as height of span tag?</span>
<br>
<span style="background-color: aquamarine; font-size: 16px; line-height: 8em;">Is line-height same as height of span tag?</span>

Why inline and inline-block have different height with the same text content

This is related to how line-height works and how the height of each element is calculated.

Let's start with a trivial example to highlight the effect of line-height

span {
border:1px solid red;
padding:5px;
line-height:50px;
}
<span>some text</span> <span style="display:inline-block">some text</span>

How to calculate the height of an inline element

The CSS 2.1 spec says:

10.6.1 Inline, non-replaced elements

The 'height' property does not apply. The height of the content area should be based on the font, but this specification does not specify how.

As it happens the height, as opposed to the line-height, of a non-replaced inline element has very little effect on anything else so browsers are pretty free to report whatever number they like here.

However, a little reverse engineering can be instructive.

If we look at the font metrics for Times New Roman, we see EM size of 2048, WinAscent of 1825, and WinDescent of 443. Sum the ascent and descent, divide by the EM size, multiply by the font size (20px) and round to the integer and we get 22px.

Taking Arial as another font, we have EM size of 2048, WinAscent of 1854, and WinDescent of 434. Do the calculation again and we again get 22px.

What about a different font? Tahoma has EM size of 2048, WinAscent of 2049, and WinDescent of 423. Do the calculation again and this time we get 24px. And hey presto, if you try your JsBin with the Tahoma font, Firefox does indeed show a height of 24px.

The font metrics above were obtained from loading the fonts into Type Light 3.2.

Not conclusive, but a reasonable suggestion of how it all works.

Is it possible to make it tall 20px without using inline-block ?

Assuming the above is correct, you should be able to achieve it by using a custom font and modifying the font metrics of that font to suit. I wouldn't like to predict the knock-on effects of doing that though.

Why Empty Display Inline Block Element Create Height But Display Inline and Display Block not?

To understand this you need to know the difference between a BFC (block formatting context) and IFC (inline formating context.

When having only block elements inside your container this one will create a BFC:

In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. The vertical distance between two sibling boxes is determined by the 'margin' properties. Vertical margins between adjacent block-level boxes in a block formatting context collapse.

In this case, only the height and margin of the block element inside are considered to calculate the height of your container and you only have one empty element so the height is 0.

When having an inline-block/inline elements you will trigger the creation of an IFC and the story is different:

In an inline formatting context, boxes are laid out horizontally, one after the other, beginning at the top of a containing block. Horizontal margins, borders, and padding are respected between these boxes. The boxes may be aligned vertically in different ways: their bottoms or tops may be aligned, or the baselines of text within them may be aligned. The rectangular area that contains the boxes that form a line is called a line box.

The height of a line box is determined by the rules given in the section on line height calculations.

As you discovered, we will be dealing with line boxes and line-height will be considered here to define the height of each line box (in your case you have only one)


Now, the difference between the inline and inline-block is about white space. In the case of inline element you will end having an empty container because all the white space will collapse. More details here: https://www.w3.org/TR/2011/REC-CSS2-20110607/text.html#white-space-prop.

If you change the white space algorithm (and you add a space) you will get the same height as with the inline-block element.

.container {
background-color: red;
margin:5px;
white-space:pre;
}
<div class="container"><div style="display: inline-block;"></div></div>

<div class="container"><div style="display: inline;"> </div></div>

Inline elements and line-height

This might be confusing because in the inline formatting model there are different heights.

Height of an inline box

An element with display: inline generates an inline box:

An inline box is one that is both inline-level and whose contents
participate in its containing inline formatting context. A
non-replaced element with a display value of inline generates an
inline box.

And line-height determines the height of that box:

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

Therefore, your box is, in fact, 15px tall.

Height of a line box

There are also line boxes:

In an inline formatting context, boxes are laid out horizontally, one
after the other, beginning at the top of a containing block.
Horizontal margins, borders, and padding are respected between these
boxes. The boxes may be aligned vertically in different ways: their
bottoms or tops may be aligned, or the baselines of text within them
may be aligned. The rectangular area that contains the boxes that form
a line is called a line box.

The height of a line box is determined by the rules given in the
section on line height calculations.

In case a line box only contains non-replaced inline boxes with the same line-height and vertical-align, those rules say that the height of the line box will be given by line-height.

So in your case, this is also 15px.

Height of the content area of an inline box

However, the developer tools of your browser said 18px. That's because those 18px are the height of the content area. It's also this content area (together with paddings) which is painted by the green background.

Note those 18px might vary because CSS 2.1 doesn't specify an algorithm:

The height of the content area should be based on the font, but this
specification does not specify how. A UA may, e.g., use the em-box or
the maximum ascender and descender of the font. (The latter would
ensure that glyphs with parts above or below the em-box still fall
within the content area, but leads to differently sized boxes for
different fonts; the former would ensure authors can control
background styling relative to the 'line-height', but leads to glyphs
painting outside their content area.)

If an UA implements the first suggestion, the content height will be given by font-size, which determines the em-box. This would what you expected, with the green box being 15px tall.

However, most UAs don't seem to do that. That means that, probably, the height will be the height of the tallest glyph in the font-family and font-size used.

But using a font-size value of 15px doesn't mean that the tallest glyph will be 15px tall too. That depends on the font. This is somewhat analogous to normal, the initial value of line-height, which is defined as

Tells user agents to set the used value to a "reasonable" value based
on the font of the element[...]. We recommend a used value for
'normal' between 1.0 to 1.2.

That means that, if you use font-size: 15px, a "reasonable" line-height would be between 15px and 18px. In the "Verdana" font, Firefox thinks the best is 18px; in the "sans-serif", it uses 17px.

Box Model for Inline Elements

For the second question you may refer to this part of the specification:

The 'height' property does not apply. The height of the content area
should be based on the font, but this specification does not specify
how. A UA may, e.g., use the em-box or the maximum ascender and
descender of the font. (The latter would ensure that glyphs with parts
above or below the em-box still fall within the content area, but
leads to differently sized boxes for different fonts; the former would
ensure authors can control background styling relative to the
'line-height', but leads to glyphs painting outside their content
area.)

The vertical padding, border and margin of an inline, non-replaced box
start at the top and bottom of the content area, and has nothing to do
with the 'line-height'. But only the 'line-height' is used when
calculating the height of the line box.

.test {  margin: 0 10px;  padding: 20px;  border: 5px solid blue;}
div { border:1px solid red; margin:50px 0;}
<div><span class="test">test2test2test2test2test2test2test2 test</span></div>
<div><span class="test">test2test2test2test2test2test2test2 test st2test2test2test2 test st2test2test2test2 test st2test2test2test2 test</span></div>


<div><span class="test" style="line-height:50px;">test2test2test2test2test2test2test2 test2test2</span></div>
<div><span class="test" style="line-height:50px;">test2test2test2test2test2test2test2 test2test2 test2test2test2test2test2test2test2 test2test2</span></div>

Auto' heights for block formatting context roots (inline-block)

Thanks @TemaniAfif

He was right the reason of 18px for height was in line-height of div element. div's line-height specify minimal height of line boxes

On a block container element whose content is composed of inline-level
elements, 'line-height' specifies the minimal height of line boxes
within the element.

In my case div doesn't specify line-height property so it has default value equals normal.

Name: line-height

Value: normal | <number> | <length> | <percentage> |

inherit Initial: normal

https://www.w3.org/TR/CSS22/visudet.html#leading

If line-height value is normal then computed value depends from user agent.
But spec recommends to use value 1.0 to 1.2 of element's font-size. Looks like usually this value are equals to font-size * 1.2 but it's also depends from font's metrics like ascent, descent and em-box (so result would depends from font-family).

normal

Tells user agents to set the used value to a "reasonable" value
based on the font of the element. The value has the same meaning as
. We recommend a used value for 'normal' between 1.0 to 1.2.
The computed value is 'normal'.

If you know font's metrics like ascender and descender and font-size which you would use on the web you can calculate line-heigh by (ascender + Math.abs(descender)) * font-size / (units_per_em)

function lh(ascender, descender, unitsPerEm, fontSize) {
return (ascender + Math.abs(descender)) * fontSize / unitsPerEm;
}

Sample Image
Sample Image

In the pictures above ascender=5000, descender=-200, units_per_em=1000 and font-size=48px so line-height=(5000 + 200) * 48 / 1000 = 249.6 (lh(5000, 200, 1000, 48))
Sample Image

ascent, descent font's properties are shipped with font and defined by font's creator.
You can use FontDrop! service to get information about font

Some implementations details from Chromium(Webkit) relates to line-height

LayoutUnit ComputedStyle::ComputedLineHeightAsFixed() const {
const Length& lh = LineHeight();
// Negative value means the line height is not set. Use the font's built-in
// spacing, if avalible.
if (lh.IsNegative() && GetFont().PrimaryFont())
return GetFont().PrimaryFont()->GetFontMetrics().FixedLineSpacing();

https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/core/style/computed_style.cc#1835

FixedLineSpacing

 float line_gap = SkScalarToFloat(metrics.fLeading);
font_metrics_.SetLineGap(line_gap);
font_metrics_.SetLineSpacing(lroundf(ascent) + lroundf(descent) +
lroundf(line_gap));

https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/platform/fonts/simple_font_data.cc#123

Text overflow - why is block element smaller than inline element

overflow apply to only block level element ref and the content of inline element cannot overflow since you cannot have it's height smaller than its content due to the fact that you cannot define height on inline element and the height is automatically defined by the content ref

Adding border will clearly show this: