David's Inline Box Model

This is a proposal for clearer explanation, clarification, and modification of the CSS2 inline box model. It is more than just an explanation of CSS2's model, since it incorporates a number of changes that I have proposed.

This document is badly in need of some illustrated examples. Hopefully I'll make some soon.

This document makes no attempt (yet) to cover the formatting of marker boxes or compact elements or deal with bidirectionality.


Inline elements

There are two types of inline elements: replaced inline elements and non-replaced inline elements. In general, non-replaced elements are those whose content is contained in the document, whereas replaced-elements are those whose content is outside of the document. For example, in the code:

Visit the <a href="http://www.w3.org/">World
Wide Web Consortium</a> to learn about...

the content of the a element is "World Wide Web Consortium". Replaced elements are those where the content comes from some external source, for example, an object or img element.

However, as far as the inline box model is concerned, the definitions are as described above except that elements with display types inline-table and inline-block (the latter is a proposed type for CSS3 to accommodate form elements) are considered replaced elements.

Box properties on inline elements

The box properties (margin, padding, and border properties) on inline replaced elements are handled just like box properties on block-level elements. The content height and width are determined by the height and width properties (which may be auto), and the padding, border, and margin are placed outside of the content. For inline replaced elements, the only edge relevant to the inline box model is the margin edge (the outside edge of the margin).

Inline non-replaced elements are treated quite differently. The content width of an inline non-replaced element is the width required by its content (which is affected by the font properties and also word-spacing, letter-spacing, which in turn are affected by whether text-align is justify or not). The width property has no effect. The horizontal margin, border, and padding are placed to the left of the beginning of the inline element and to the right of the end, but have no effect at line breaks.

Heights of inline elements are more complicated. For the purposes of the box properties, the font height is used. The vertical padding and border are placed outside the font edge. If the inline element is broken over lines, they occur on every line. However, they have no effect on the layout of the inline elements. (Since the vertical box properties have no effect on the layout of the elements, the vertical margin properties have no effect at all.) The logical height of an inline element will be discussed later.

Anonymous inline boxes

Block level elements (in which I include elements of display block, list-item, table-cell, inline-block (proposed), and some elements of display compact and run-in) can contain three types of content. They can contain other block-level elements, inline elements, or text. In terms of the DOM, the first two types of children are Element Nodes and the latter type are Text Nodes. Whenever a block-level element contains inline elements or text, all consecutive inline elements and text nodes not separated by block-level elements are enclosed within one anonymous inline box. (This box is later split during the construction of lines. [NOTE: Would it be clearer to call the two types of anonymous inline boxes two different things?]) In other words, anonymous inline boxes are created according to the following constraints:

Anonymous inline boxes are considered to have the font-size and line-height properties of their parent block-level element.

The line-height property

The line-height property specifies the logical height of an inline element. This is the height used in the vertical alignment of inline elements and the construction of line boxes. [NOTE: Should it instead be said that (line-height - font-size) is the leading? This makes a difference when multiple fonts are mixed because the first font does not have sufficient characters. I think it would be far superior since it would prevent potential overlap or narrowing of spaces between lines because of one or two glyphs. But is it too complex?]

The line-height property takes four types of values (other than inherit): normal, <length>, <number>, and <percentage>, as described in CSS2. <length> and <percentage> values have computed values that are lengths, and therefore the computed values inherit as lengths. Therefore, they are dangerous on any element whose descendants have a different font size. <number> and normal values have computed values that are scaling factors, and therefore inherit as scaling factors that are multiplied by the font-size when used in calculations. They are therefore safe. [NOTE: Should normal be considered to be a computed value so that it can vary by font, using information provided in the font metrics?] [NOTE: Should percentages and scaling factors be based on the computed font size, the actual font size (of the first font in the font set?), or the actual max-ascent to max-descent (of the first...?)?]

Computing the heights and edges of a non-replaced inline box

There are five important horizontal lines in the box generated by a non-replaced inline element: the logical top, the font top, the baseline, the font bottom, and the logical bottom. This section explains how to compute the relative positions of these five lines. The distance from the font top to the font bottom (i.e., between the font edges) is called the font height, and the distance from the logical top to the logical bottom (i.e., between the logical edges) is called the logical height. (The logical height is given by the line-height property.)

Many non-replaced inline elements contain text. In some cases that text is a child of the inline element itself, whereas in others, the text is contained within other descendant inline boxes. (In a DOM sense, the former case is when the text nodes are children of the inline box and the latter is when the text nodes are descendants but not children.) When the inline element has text nodes as children, the metrics of all the fonts used to set the child text nodes determine the font height and font edges of the inline box. (Note that multiple fonts are only used when the first font in the font set does not contain all the glyphs needed.) When the inline element does not have text nodes as children, the metrics of the first font in the font set (i.e., the first font that would be tested for the presence of a character) are used instead.

The metrics of every font specify a baseline, a max-ascent (largest distance a character extends above the baseline), and a max-descent (largest distance a character extends below the baseline). [NOTE: Are the latter two the correct terms?] The font bottom of an inline box is below the baseline, separated from the baseline by the largest max-descent of the relevant fonts. (The relevant fonts, as noted above, are either those actually used or the first font in the font set.) The font top of an inline box is above the baseline, separated from the baseline by the largest max-ascent of the relevant fonts. [NOTE: Note that some people (a minority), myself included, believe that the distance between the max-ascent and max-descent for any given font should be the font-size that font claims. However, many scalable fonts do not behave this way.]

The difference between the logical height and the font height is called the leading. (The leading is negative when the font height is larger than the logical height.) This leading is distributed equally on both sides of the font height. That is, the logical top and the logical bottom are placed so that the midpoint (midline?) between them is the same is the midpoint (midline?) between the font top and the font bottom. Half the leading is called the half-leading. The logical top is above the font top by a distance of the half-leading, and the logical bottom is below the font bottom by the same distance. (If the half-leading is negative, then the above/below relationships are reversed.)

Note that anonymous inline boxes are no exception to these rules. They have a baseline, and their font edges are determined by only the fonts used for the anonymous text within them (or the first font in the font set), not the text contained within inline elements that they contain. Note also that their logical height is determined by the line-height property (of the element corresponding to the parent block box) just as the logical height of inline boxes is determined by the line-height property (of the element corresponding to the box itself).

The vertical-align property

There are two types of values (excluding inherit) for the vertical-align property: anchored and loose. The anchored vertical alignment types specify a vertical-alignment for an inline box relative to its parent inline box. The loose vertical alignment types specify a vertical alignment within the line box. Note that the vertical alignment of anonymous inline boxes cannot be specified by the vertical-align property on their parent block, since a vertical-align on an anonymous inline box would be meaningless. [NOTE: It's actually not meaningless in all cases, but only top and bottom would be useful.]

The anchored vertical-alignment types are as follows:

baseline
Align the baseline (or bottom outer edge, if replaced) of the box with the baseline of the parent box.
middle
Align the midpoint of the box (which is the same for both the logical edges or font edges) with the point in the parent one-half the x-height of the parent's font above the baseline. [NOTE: Should the first font in the parent's font set always be used? Is that the definition of an ex? Should this be defined in terms of an ex?] [NOTE: Which edge is used for finding the midpoint of a replaced element?]
sub
Align the baseline (or bottom outer edge, if replaced) of the box with the proper subscript baseline of the parent box. This subscript baseline may be determined from the font metrics of the parent's font. [NOTE: Is the first font in the font set used?]
super
Align the baseline (or bottom outer edge, if replaced) of the box with the proper superscript baseline of the parent box. This superscript baseline may be determined from the font metrics of the parent's font. [NOTE: Is the first font in the font set used?]
text-top
Align the logical top of the box (or top outer edge, if replaced) with the font top of the parent box.
text-bottom
Align the logical bottom of the box (or bottom outer edge, if replaced) with the font bottom of the parent box.
<length>
Align the baseline (or bottom outer edge, if replaced) of the box with the given length above (or below, if negative) the baseline of the parent box.
<percentage>
Convert the percentage into a length by multiplying by the font-size, and then treat as a <length>. Negative percentages are allowed. [NOTE: Is the computed value or the actual value (which?) of the font-size used?]

The loose vertical-alignment types are as follows (terms used will be defined later):

top
Place the loose subtree top (or top outer edge, if replaced) of the box at the top of the line box.
bottom
Place the loose subtree bottom (or bottom outer edge, if replaced) of the box at the bottom of the line box.

[NOTE: Why isn't there a loose type for the middle of the line box? Is that what the proprietary align=absmiddle does?]

The inline formatting context (horizontal)

The inline content (inline elements and text) within a block level element is broken into lines using a line-breaking algorithm. [NOTE: Should Unicode be cited?] When doing this, the sizes of words (affected by letter-spacing), whitespace (affected by word-spacing), replaced elements, and horizontal margins, border, and padding must be considered. Lines are typically broken at word-breaks, although breaks may be permitted within words, forced, or prohibited between words. Forced breaks include the beginning or end of a block-level element and any markup or characters that force a line-break.

The content within each line is placed within a line box. If an inline element is split between lines, then more than one inline box is used to represent that element. (Note that horizontal box properties still only apply at the beginning and end of the element.) Similarly, when an anonymous inline box is split between lines, it is split into one anonymous inline box per line. Therefore, in the "formatting tree," every line box has exactly one child, which is always an anonymous inline box. This child is called the root of the line. Note that the line box is often taller than this anonymous inline box (the root of the line).

The width of line boxes is the width of the containing block box minus any space occupied by floats. The line boxes are stacked vertically within the block box with no separation or overlap, beginning at the top inner edge of the block. If the block has a height of auto, the bottom of the last line box determines the bottom inner edge of the block box.

The horizontal alignment of the content within the line box is controlled by the text-align property of the parent block-level element, which can take the following values for block-level elements (excluding inherit):

left
The content of the line box is placed to the left edge of the line box and the space unable to be filled is left at the right.
center
The content of the line box is placed horizontally in the middle of the line box and the space unable to be filled is divided equally between left and right.
right
The content of the line box is placed to the right edge of the line box and the space unable to be filled is left at the left.
justify
For line boxes not terminated by a forced break, attempt to remove the unfilled horizontal space by modifying none, any, or all of the letter spacing, word spacing, font stretch, or character widths. If this is not possible or not done, or for lines terminated by a forced break, treat the line as if text align were left (if direction is ltr) or right (if direction is rtl).

The construction of line boxes (vertical)

This section is based on a post I made to www-style, but I have improved (I hope!) the terminology.

In this section I will refer to boxes generated by elements that have a loose vertical alignment as loose boxes and those generated by elements that have a fixed vertical alignment as anchored boxes. The root of the line is always a loose box. Each loose box in a line has a loose subtree of boxes. This includes the box itself and all of its descendant boxes except for those that are loose or are descendants of such a descendant loose box.

All the boxes within each loose subtree are related by fixed vertical alignments, and therefore their positions relative to each other are clearly specified. Every loose subtree has a loose subtree top, which is the highest of the logical tops of the boxes in that loose subtree, and a corresponding loose subtree bottom. The distance between them is the loose subtree height.

The height of the line box is the largest of the heights of the loose subtrees within the line box. This tallest loose subtree extends from the top of the line box to the bottom. If this tallest loose subtree is the one corresponding to the root of the line, then all the other loose subtrees are aligned according to the value of the vertical-align property on the element generating the box that is the root of the loose subtree. If this tallest loose subtree is not the one corresponding to the root of the line, then the position of the loose subtree corresponding to the root of the line is undefined, but all the other loose subtrees are aligned (as in the previous case) according to the relevant value of the vertical-align properly. However, it is suggested that the root of the line be aligned as if the relevant vertical-align value were that of the tallest loose subtree in the line.

Changes from the CSS2 inline box model

I do not guarantee that these lists are complete.

Changes

Potentially controversial clarifications


Valid HTML 4.0!

(Back to CSS, David Baron)

LDB, dbaron@dbaron.org, 2000-01-09