Extra padding in a container when using HTML5 doctype

advertisements

There is extra top padding in the following div:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Extra Top Padding</title>
    <style>
        div {
            background: tan;
        }
        div * {
            vertical-align: middle;
        }
        span {
            font-size: 16px;
        }
    </style>
</head>

<body>
    <div id="container">
        <img src="https://ssl.gstatic.com/images/icons/gplus-16.png"><span>Some text</span>
    </div>
    <p id="indicator"></p>
    <script>
        document.getElementById('indicator').textContent = document.getElementById('container').clientHeight;
    </script>
</body>

</html>

DEMO

It shows correctly if you use HTML 4.01 Transitional doctype:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

Is it a bug in HTML5?


tl;dr

It's not bug. It follows as a consequence of using almost standards mode or standards mode for your document. And it's not padding, it's line-height space.


Personally, I wouldn't be satisfied with the above as an explanation. But to fully explain requires substantial detail. So here goes:

Calculations are for Times New Roman typeface. Other typefaces may give different values though the principles remain the same.

The height difference is the same with or without the img element so let's discard that.

When you use the HTML5 doctype (or the HTML 4.01 Strict doctype) you get Standards mode. When you use the HTML 4.01 Transitional doctype you get Almost Standards mode. The difference between the two modes is primarily about which inline boxes affect the line height. In particular, the strut has no effect in Almost Standards mode.

So in Almost Standards mode, the only inline box is the text. The total height of the div is simply the line height of that text. Which is 16px * 1.25 (the div's line-height setting) = 20px.

Standards mode is more complicated. Now we have both the text and the strut. the strut is aligned to the baseline. The default line height of 1.25 gives us 2px upper half leading + 13px ascender above the baseline and 3px descender + 2px lower half leading. Total 20px.

The text is vertical-align:middle. The CSS 2.1 spec for that says

Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent.

The baseline of the parent box is the baseline of the strut which we know from the above is 5px above the bottom (before adding the text).

The x height for the typeface at a font-size of 16px is around 7px. Halve that and truncate to a whole number gives 3px. Add that to the 5px and we get an alignment line for the text of 8px above the bottom.

The vertical midpoint of the box has 10px above the alignment line and 10px below the alignment line.

The line height must be large enough to contain all its inline boxes, so that means that it must be the maximum of the boxes above the alignment line + the maximum of the boxes below the alignment line. That's max(12px[strut], 10px[text]) above the line and max(8px[strut], 10px[text]) below the line.

Which equals (12px + 10px) or 22px.