DEV Community

Cover image for Mapping HTML to CSS boxes.
Nicholas Stimpson
Nicholas Stimpson

Posted on • Updated on

Mapping HTML to CSS boxes.

Let’s start with a quick quiz. You know everything in CSS is a box, right? A box being one of those things with a content area, and padding, borders and margins around it. Of course you do. So in this simple HTML example below, starting from the outer div and including its descendants, how many block and inline boxes are used to render it?

Two? Well, there’s two elements, the inner div and the outer div. An HTML element becomes a box in CSS right? So just the two block boxes then. Sorry, no.

Five? We’ve got some runs of text too. Starting with “The Vogons...”, “The dolphins ...” and “So long...”. These text runs are contained within anonymous inline boxes. If we add one inline box for each of those to the two block boxes of the divs, that makes five. Better, but still no.

Six? The observant might notice that the first run of text is rendered over two lines. An inline box sits within one line, so if we’ve got two lines, the text must be split across two inline boxes. So four inline boxes plus two block boxes equals six. Getting closer, but we’re still not there.

Eight? All block boxes are block container boxes. Block container boxes contain as their children either (a) only block-level boxes or (b) only inline-level boxes. The inner div is fine. It only has a single inline-level box. But the outer div appears to be containing three inline-level boxes and one block box. When this happens, CSS automatically adds anonymous block boxes around the groups of inline-level boxes. That way, the outer div contains only three block boxes and no inline-level boxes, and the first and second anonymous block boxes only contain only two inline-boxes and one inline box respectively, with no block boxes. So we have two block boxes for the divs, two anonymous block boxes, and four anonymous inline boxes making a grand total of eight. Hurrah! we’re there.

Well, sort of. In addition the anonymous inline box in each line, each line has a “strut” which is used to give the line a minimum height. The CSS specification says that this strut is as if each line starts with a zero-width inline box. So if we ignore the “as if” we should count an additional four boxes, one for each line, to make a total of Twelve.

Including the struts as purple rectangles, the actual arrangement looks like this…

Figure 1. The nested arrangement of the twelve boxes

So what’s the point of this? It’s just a reminder that the relationship between HTML elements and CSS boxes is rather more subtle than it may seem at first glance.

More Boxes than Elements

The display property has a profound effect on both the type and number of boxes to which an element maps. Here’s some more examples of situations where elements generate either more than a single box.

  • The generation of anonymous block boxes described above applies for all block containers. So computed display values of inline-block, table-cell and list-item can all generate those boxes when necessary.

  • display:list-item and the proposed but only currently implemented in Firefox display:inline list-item also generate at least two boxes, a principal box and a marker box.
    (The marker box is always a child of the principal box, even when it’s positioned "outside".)

  • display:table and display:inline-table elements also generate at least two boxes. A table-wrapper box and inside that, a table box. When CSS properties are assigned to an element with display:table, some of the properties are assigned to the table-wrapper box and some to the table box, but never both. For all the properties not assigned, the values used are the default ones for the property.

    For example, any specified margin is applied to the table-wrapper box, so the margin of the table box is 0px all round. But any specified value of overflow is applied to the table box, so the overflow value for the table-wrapper box is always “visible”. As well as the table box, the table-wrapper box contains any boxes generated by the caption element if one exists.

  • Table layouts can also result in anonymous table objects. The structure for HTML tables is tabletbodytrtd, which maps to the CSS display values table - table-row-group - table-row - table-cell. How each of these works within the layout depends on both their context and content.

    So what happens if a div element is assigned display:table-row on its own in the CSS? It generates three boxes: an anonymous table or inline-table box, which surrounds the table-row box of the element, which in turn contains an anonymous table-cell box, which contains the div’s contents. So if the HTML is:
    <div style="display:table-row">Lorem ipsum dolor sit amet</div>

    the box structure is

    Figure 2. named and anonymous table boxes

    Similarly, if a lone display:table-cell element is present, it is wrapped in anonymous table-row and table boxes. Sufficient anonymous inside or outside boxes will always be generated to meet either the pattern table - table-row - table-cell or table - table-row-group - table-row - table-cell.

    So for another example, suppose we had:
    <div class="outer" style="display:table">
    <span class="inner" style="display:table-row-group">
    lorem ipsum dolor sit amet

    that would form:
    Figure 3. table boxes include table-row-group

Fewer Boxes than Elements

On the other hand, there are a couple of display values that cause no box to be generated at all. display:none and display:contents do that. display:none affects descendant elements as well so they do not generate boxes either, while the descendants of display:contents elements generate boxes normally.

Why Boxes Matter

So can we make use of knowing this? It can help us understand why CSS doesn't always do what we expect it to, and it can help us spot solutions to particular layout problems.

For example, let’s suppose we have

we might have expected that the .cell element would be 200px tall and the text "Hello World" vertically centered in it, since that's how table cell vertical alignment normally works. But it’s at the top. That's because the table-cell’s height isn’t 100% of the .outer element, but 100% of the anonymous table box. And because it’s anonymous, it can’t be styled to be anything other than the default height:auto, and auto means that it's the height of its content. Which is just the height of the text.

As a second example, suppose we have some nicely semantic HTML like this.

The default styling for displaying on a desktop might be just what we want. But on a mobile, we might want to compress it down a bit and for good responsiveness lay the title and list items using a flexbox layout. Now if we just give the div element display:flex, it'll have two flex items, the first created by the h1 element and the second created by the ul element. Evening up the fonts and spacing, we get something like this:

That's not compressed it much. If we wanted the list items to follow on from the heading horizontally we could try making the ul element display:flex as well. However, doing that the items won't wrap under the heading, so we still get this:

Which hasn't helped. If we want the items to wrap under the heading, we need the list items themselves to participate in the same flexbox as the heading. Fortunately, if we realise that the flex items are the boxes created by the elements rather than the elements themselves, then we can simply remove the box created by the ul element, and the boxes created by the li elements will become the flex items. The div flex container will then have six items instead of two. All we have to do to remove the box created by the ul element id to apply display:contents to it. And the result is this:

Comments, corrections and follow-up questions are most welcome. Even tl;dr.

Discussion (4)

afif profile image
Temani Afif • Edited on

Alohci, right ??! :)

update: yes it's you, nice seeing you here ;)

alohci profile image
Nicholas Stimpson Author

Yes, and you too. I need to correct this sometime. I'm aware that CSS Inline level 3 spec defines a root inline box that I've omitted, and that the fragmentation rules there also affect things a bit, as Boltclock pointed out on an SO comment recently.

mrjjwright profile image
John Wright • Edited on

The div flex container will then have four items instead of two.

Why not 6? The h1 plus each of the 5 li?

alohci profile image
Nicholas Stimpson Author

You're absolutely right. Thanks, corrected.