Why Is the Span's Line-Height Useless

Why is the span's line-height useless?

Block layouts, like div elements are by default, are made up of a vertical stack of line boxes, which never have space between them and never overlap. Each line box starts with a strut which is an imaginary inline box the height of the line-height specified for the block. The line box then continues with the inline boxes of the markup, according to their line heights.

The diagram below shows the layout for your first example. Note that because 1.7 times the font-height is much less than the height of the strut, the line height is determined by the height of the strut, since the line box must wholly contain its inline boxes. If you had set the line height on the span to be greater than 200px, the line boxes would be taller, and you would see the text move further apart.

Layout with span as inline

When you make the span inline-block, the relationship between the div and the span doesn't change, but the span gains it's own block layout structure with its own stack of line boxes. So the the text and the line breaks are laid out within this inner stack. The strut of the inner block is 1.7 times the font-height, i.e., the same as the text, and the layout now looks as below, so the text lines are positioned closer together, reflecting the line-height setting of the span.

(Note that the two diagrams are on different scales.)

Layout with span as inline-block

How do you decrease the line height when line-height does nothing?

I'm trying to change the line spacing to be single-spaced for the summary text in the span


The span has display: inline, line-height does not work for inline elements.

I think the idea is that if you had a inline element in a block of text the CSS spec assumes you wouldn't want to have one line with a different line-height than the rest, so it doesn't allow you to set line-height. For example, if you had

<p>lorem ipsum. 
Sed ut <em>perspiciatis unde</em>
ut aliquid ex
ea commodi consequatur? </p>

it would be weird if the second line had a different line-height than the rest.

Important note: line-height is not the only thing that doesn't work for inline elements. Read more at CSS display: inline vs inline-block

If you know the answer to the question I'd also be very interested in how you got that answer. Unlike regular code, when I have CSS problems I have no idea how to debug them, besides googling and flailing around trying random things until it works.

When a commenter tried to answer the question I googled around some more and eventually ran into the stackoverflow answer about inline elements. If I run into problems in the future I should add in the html element I'm having trouble with. For example, a "span line-height not working" google search query would have answered my question in the second result. So unfortunately the strategy basically remains "googling and trying random things" but to be fair you could say that's pretty much what debugging is. Or you could say "researching and experimenting with educated guesses" if you wanted to be fancy.

Line height is set to 0, why is there space between lines?

If you want to remove the vertical space, change the vertical-align property to something other than the default value which is baseline (vertical-align: baseline).

.foo2 {
vertical-align: top;
}

When the value is set to baseline this vertical space is reserved for letters such as j, y, p, q.

body {  margin: 0;  padding: 0;}.wrap1 {  font-size: 20pt;  line-height: 0;}.wrap2 {  font-size: 40pt;  line-height: 0;}.wrap3 {  font-size: 60pt;  line-height: 0;}.foo2 {  display: inline-block;  line-height: 0;  font-size: 0;  vertical-align: top;}.foo2 p {} .foo2 span {  font-size: 16pt;  line-height: 1.2;  display: block;  background: #ccc;}
<div class="wrap1">  <div class="foo2">    <p><span>text1</span>    </p>  </div>  <div class="foo2">    <p><span>2text</span>    </p>  </div>  <br>  <div class="foo2">    <p><span>3biggertext</span>    </p>  </div>  <div class="foo2">    <p><span>4text</span>    </p>  </div></div><div class="wrap2">  <div class="foo2">    <p><span>text1</span>    </p>  </div>  <div class="foo2">    <p><span>2text</span>    </p>  </div>  <br>  <div class="foo2">    <p><span>3biggertext</span>    </p>  </div>  <div class="foo2">    <p><span>4text</span>    </p>  </div></div><div class="wrap3">  <div class="foo2">    <p><span>text1</span>    </p>  </div>  <div class="foo2">    <p><span>2text</span>    </p>  </div>  <br>  <div class="foo2">    <p><span>3biggertext</span>    </p>  </div>  <div class="foo2">    <p><span>4text</span>    </p>  </div></div>

The browser seems to have a minimum line-height on this block that contains text. Why?

It turns out that the behavior I saw isn't anything to do with a minimum line-height as I assumed. The issue is around baseline alignment, because the span had vertical-align:baseline. If the span had a different vertical-align such as bottom that didn't depend on its parent's baseline, it would move up to the top of the containing div where I expected it to be.

The strut (that imaginary character that goes at the beginning of every line block and has the parent element's font-size and line-height) doesn't just specify a top and bottom edge for the line, it also has a baseline of its own. (In CSS3 it actually has a bunch of baselines, but for now we can just focus on the "alphabetic" one.) The strut's baseline is positioned so that characters rendered in the parent element's font would end up halfway between the top and bottom of the strut. In this particular case where the line-height is zero, the strut's top and bottom are the same line, but it has a baseline that is actually below its bottom edge. The strut's baseline, like its bottom edge, defines a minimum for the line: it can move down, but never up. This means that when we render a much smaller font in the span, it is using a baseline that would center the div's font on the top edge of the div.

Line height issue with inline-block elements

The line-height is applying but you need to understand how it's applying. If we refer to the specification:

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

By setting line-height:5 on the parent element you set a minimum height for the linebox.

On a non-replaced inline element, 'line-height' specifies the height that is used in the calculation of the line box height.

By setting line-height:1.5 you defined the height of your element inside the linebox.

To make it easier, you have an element with a height equal to 1.5 inside a linebox with a height equal to 51 but you cannot visually see this. If you inscrease the line-height of child and you reach 5 you will then reach the minimum height and you will start increasing the linebox previously defined by the parent element.

To see this you need to apply vertical-align. If the line height of child element is smaller than the line height of the parent (the height of the child smaller than the height of the linebox) you can align:

.container {
max-width: 200px;
border: 2px black solid;
line-height: 5;
}

.container>a {
line-height: 1.5;
}
<div class="container">
<a>First</a>
<a style="vertical-align:top;">Second</a>
<a>Third</a>
<a style="vertical-align:bottom;">Fourth</a>
</div>

Why does unitless line-height behave differently from percentage or em in this example?

Based on clues in the proposed answers, I think the rendering behavior seen in these examples is counterintuitive, but correct, and mandated by the interaction of several rules in the spec, and the overall CSS box model.

  1. CSS calculates the leading L needed for a box by the formula
    line-height = L + AD, where AD is "the distance from the top to
    the
    bottom"
    of the font. Then "half the leading is added above A and the other
    half below D." So text that has font-size:16px and
    line-height:24px will have 4px of leading above and below. Text
    that font-size:8px and line-height:24px will have 8px of leading
    above and below.

  2. By default, however, "user agent must align the glyphs ... by their
    relevant
    baselines.".
    This starts to explain what's happening here. When line-height is
    specified by percentage or em, a computed value is inherited by the
    child (here, the smaller span). Meaning, the smaller span gets
    the same line-height as the parent block. But because of the L +
    AD formula, the text of that span has more leading on the top and
    bottom, and thus the baseline sits higher in its box. The browser
    pushes down the smaller span vertically to match the baselines.

  3. But then the browser has a new problem — how to deal with the line
    spacing in the enclosing block, which has been disrupted by this
    baseline-adjusting process. The spec resolves this too: the
    line-height of a block-level element "specifies the minimal
    height of line boxes within the
    element".
    Meaning, CSS makes no promise that you'll get your exact
    line-height, just that you'll get at least that amount. So the
    browser pushes the lines apart in the enclosing block so that the
    realigned child box will fit.

The reason this is counterinitutive is that it's the opposite of how most word processors and page-layout programs work. In these programs, a smaller stretch of text within a paragraph is aligned by its baseline (like CSS) but line height is enforced as a distance between baselines, not as a box surrounding the smaller text. But that's not a bug — CSS is designed around a box model. So in the end, we could say that this spacing behavior is a consequence of that model.

That still leaves us to explain the behavior in the example with the unitless line-height:

  1. First, note that when no line-height is specified, the browser
    will apply a unitless line-height by default. This is required by
    the
    spec:
    the initial value of line-height is normal, which is defined to
    have "the same meaning as <number>", and the spec recommends a
    value "between 1.0 and 1.2". And that's consistent with what we see
    in the examples above, where the paragraphs with line-height: 1.5
    have the same behavior as the paragraphs with no line-height setting
    (i.e., they are impliedly getting line-height: normal)

  2. As others have pointed out, when the paragraph has
    line-height: 1.5, the calculated line-height of the paragraph is
    not inherited by the smaller span. Rather, the smaller span
    calculates its own line height based on its own font size. When the
    paragraph has line-height: 1.5; font-size: 14px, then its
    calculated line height is 14px * 1.5 = 21px. And if the smaller
    span only has the property font-size: 50%, then its font size is
    14px * 50% = 7px, and its line height is 7px * 1.5 = 10.5px (which
    will generally be rounded to a whole pixel). But overall, the
    smaller box is half the size of the surrounding text.

  3. As before, the browser will vertically align the smaller span to
    the adjacent baseline. But this time, because the box around
    smaller is shorter than the surrounding text, this realignment
    doesn't have any side effects in the enclosing block. It already
    fits, so there's no need to spread the lines of the parent paragraph, as
    there was before.

Both cases represent a consistent implementation of the spec. That's good news, because it means we can predict the line-spacing behavior.

That brings us back to the original reason for this question. While I now understand that the CSS box model requires this behavior, as a practicing typographer, this is rarely the behavior I want. What I want is for the lines within a paragraph to have consistent & exact line spacing, even if some spans of text within that paragraph are smaller.

Unfortunately, it seems there's no way to directly enforce exact line spacing in CSS as one can in a word processor or page-layout program. Again, this is because of the CSS box model: it doesn't use a baseline-to-baseline line-spacing model, and line-height is specified to be a minimum measurement, not maximum.

But we can at least say that unitless line-height values produce the best approximation of exact line spacing in CSS. Fussy typographers like myself should feel comfortable using them, because unitless values are endorsed by the spec, and they produce consistent results across browsers. They are not a hack, nor are they deprecated.

The caveat is that they're still only an approximation. Unitless line-height values don't change the underlying CSS box model, nor the CSS box-positioning rules. So it's possible that in some edge cases, they won't have the intended result. But eternal vigilance is the price of good typography. Be careful out there.

How do I make p and span have the same line-height in HTML 5?

Problem is that p and span have different display types, resulting in different behaviors due to hidden spaces between lines.

If you want both of them to have the same line-height, force them to be of the same type :

display:inline; // or 'block' or 'inline-block'

JSFiddle demo

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.



Related Topics



Leave a reply



Submit