3

I expect that I could set the line-height of a block element to zero, and each line box inside that element would then be aligned based only on the line-heights of its content. However, when I try to use very small fonts on those inline elements, they seem to align with a lower baseline than necessary for the content in the line. My understanding of the CSS spec doesn't line up with what all browsers are rendering; what do I have wrong?

The code for a simple demo looks like this (and it's in a fiddle too):

body {
    font-size:60px;
}
div {
    height:3em;
    width:8em;
    border:1px solid black;
    line-height:0; /* minimum line height for contained elements */
}
span {
    line-height:normal; /* don't inherit from containing block */
    background-color: #cff; /* so we can see positioning */
}
<div><span>Big text works</span></div>
<div><span style="font-size:.5em">Half text size works fine too</span></div>
<div><span style="font-size:.2em">Very small text doesn't align with the top of the containing box. Why does this happen?</span></div>

According to the CSS spec:

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.

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

I know that for a simple example like there are a bunch of ways to hack the position of the small text and get it to end up at the top of the containing block, but I'd really like to understand the actual reason that the text is lining up as it does.

Community
  • 1
  • 1
sethobrien
  • 969
  • 7
  • 13

2 Answers2

6

That's because <spans> are display: inline; by default and inline elements cannot have a line-height that is smaller than its parent. You can change the display type to block and it will work. You can also use inline-block if you also set vertical-align: top;.

Demo: jsFiddle

Output:

enter image description here

HTML:

<div><span style="font-size:.2em">"display: inline;" doesn't work</span></div>
<div><span id="block" style="font-size:.2em">"display: block;" works</span></div>
<div><span id="inline-block" style="font-size:.2em">"display: inline-block;" also works if you set "vertical-align: top;"</span></div>

CSS:

body {
    font-size:60px;
}
div {
    height:50px;
    width:400px;
    border:1px solid black;
    line-height:0; /* minimum line height for contained elements */
}
span {
    line-height:normal; /* don't inherit from containing block */
    background-color: #cff; /* so we can see positioning */
}
#block {
    display: block;
}
#inline-block {
    display: inline-block;
    vertical-align: top;
}
ThinkingStiff
  • 64,767
  • 30
  • 146
  • 239
  • 1
    Is this minimum line-height documented anywhere? What is it based on? The font-size of the parent element? For the particular project I'm working on, I can't have display:block because I'm lining several spans up next to each other, and I can't use vertical-align:top because those several spans can sometimes have differing font sizes. – sethobrien Feb 18 '13 at 21:54
  • @sethobrien I go into more detail, with some other examples, in this post: http://stackoverflow.com/questions/8964281/input-has-mysterious-bottom-padding/8964378#8964378. It's a slightly different topic, but the end result is the same. It's based on the font of the span itself. I just tried `vertical-align: middle;` and it works too. You might be able to make it work with your layout. – ThinkingStiff Feb 18 '13 at 22:05
  • Thanks for the link. In your example from that answer, the minimum line-height on the span matches the line-height of the containing div, as the spec says. If you make the containing div's line-height smaller, then the span can render with a small line-height as we expect it to, as shown in this [forked fiddle](http://jsfiddle.net/hW4j5/). And it's very interesting that `vertical-align:middle` works, but I really am depending on baseline alignment of spans with different text sizes. – sethobrien Feb 18 '13 at 23:03
  • @sethobrien So if I'm understanding what you're saying here, the parent container's (if it's `block` and the child is `inline`) `line-height` overrides the child? – ThinkingStiff Feb 18 '13 at 23:26
  • @sethobrien Further testing shows that. If the child is `inline` its `line-height` cannot be smaller than the parents, or it will be ignored. It can be bigger, though, and then it's honored. That's a mystery solved that I've often wondered about. – ThinkingStiff Feb 18 '13 at 23:31
  • @ThinkingStiff - That's not quite right. The span's line height is honoured, but the line box's height is limited by the strut rather than the span. – Alohci Feb 19 '13 at 00:00
  • @Alohci Just found your great answer on the topic: http://stackoverflow.com/a/11841648/918414. Thanks! – ThinkingStiff Feb 19 '13 at 00:48
1

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.

sethobrien
  • 969
  • 7
  • 13