David's Inline Box Model

This is an explanation and a proposal for 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.

Status of this document:

This document is needs some more 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:

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

Replaced 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).

Non-replaced inline elements

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.

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? It also makes a difference
when the font height is not the font-size.]

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 a combination?), or the
actual max-ascent to max-descent (of the first...?), i.e., the font
height?]

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.)

An inline box.

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, as described below. (Note that multiple fonts are used
only 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? Note also that I'm basing this section on my
(possibly incorrect) assumption that if a font that claims to
be 12pt has a max-ascent to max-descent of 14pt, the font
metrics say where the glyphs go in a 14pt box but not in a 12pt
box.] 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. However, many interfaces for using
scalable fonts do not behave this way.]

Construction of the font box when two fonts are present.

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 (i.e., a root 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? I would think the margin edge.]

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? What if that font isn't used for the subscript?
...for the surrounding text?]

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? What if that font isn't used for the superscript?
...for the surrounding text?]

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
line-height, and then treat as a <length>.
Negative percentages are allowed.
[NOTE: If the meaning of
line-height is changed as I suggest in a
NOTE above, should this be based on the
line-height or the height of the line
box?]

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?]

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.

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. Empty line boxes should not be created. [NOTE: Empty needs to be defined. If a box has
text or replaced elements, it is not empty, but what about
border and padding?] The only child of each line box is
an anonymous inline box called the root inline box
(of the line). All of the inline boxes and text are placed
within this root inline box. Note that the line box is often
taller than the root inline 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.)

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

When a block-level element or elements are contained within
inline elements, a forced break occurs at the beginning of the
block-level element(s). A block formatting context begins at
the end of the line box preceding the block-level elements, if
such a line box exists, and the outer edge of the block-level
element is aligned with the bottom of the previous line-box.
Otherwise the block-level element is placed according to the
rules for block level formatting. At the end of the block
level element(s), the top of the following line box (if it
exists) is placed at the bottom outer edge of the last block
box. If no such line box exists, the rules for block-level
formatting are followed. [NOTE: Should the
inline boxes containing the block continue through the line
boxes of the block? This has some significant advantages for
text-decoration, but it has some more serious disadvantages
relating to sizing.]

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.

Line boxes within a block box (with auto height).

The inline boxes within a given line box are unlikely to perfectly
fill that line box. In most cases, there will be some extra
horizontal space that is unfilled. In a few cases (where line
breaking is impossible), the content may be wider than the line
box. Therefore, the inline content must be horizontally aligned
within the line 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

Place the left edge of the content of the line box at the left edge of the
line box and the space unable to be filled (or the overflow)
at the right.

center

Place the center of content of the line box horizontally in
the middle of the line box and divide the space unable to be
filled (or the overflow) equally between left and right.
[NOTE: Is this how overflow should work
for centering?]

right

Place the right edge of the content of the line box at the right edge of the
line box and the space unable to be filled (or the overflow)
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, for lines terminated by a forced break,
or for lines with overflow,
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 anchored vertical
alignment as anchored boxes. The root inline box
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 anchored
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 inline
box, 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 inline box, then the position of
the loose subtree corresponding to the root inline box 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 inline box be aligned as if the relevant
vertical-align value were that of the tallest
loose subtree in the line.

Examples

The effects of large text

As a simple example, I will show two slightly different cases side
by side in a number of states. The only difference between the
two cases is the value of the line-height property.
Furthermore, the computed value of line-height
is the same in both examples on the element on which it is
specified, but it differs on the big element (30px
and 90px, respectively).

Changes from the CSS2 inline box model

I do not guarantee that these lists are complete.

Changes

I have introduced the twelve terms matched by
"(logical|font|loose subtree)
(top|bottom|height|edge)", and also the terms
loose, anchored, and root inline box (which I have previously
called the root of the line).

Corrects the error in CSS2 that padding and border were
described as going vertically around the logical height rather
than the font height. This was stated correctly in CSS1.

Anonymous inline boxes are completely different from the spec.
However, they are mainly theoretical, and the resulting effect
is perhaps even just a clarification of the spec.

Because of the changes to anonymous inline boxes, the
line-height property no longer has any
direct effect on block-level elements. However, the original
intent (I think) of the spec is incorporated into the new idea of
anonymous inlines.

The computed value of scaling factor line-heights is described
as the scaling factor itself.

The vertical-align property on a block-level element
no longer affects anonymous inline boxes within it.

Changed the meaning of top and bottom
values for vertical align as I
described before, and made suggestion on handling
ambiguous cases.

Potentially controversial clarifications

Elements with display type of inline-table
and inline-block should be treated as
replaced elements within the inline box model (although
they have their own inline box models within them).

Explicitly described how to handle fonts that claim to
be one size but have a max-ascent to max-descent of another
size. I don't like having to do this.

Explicitly stated how to handle having multiple fonts in
one inline element. This may disagree with the relevant
statement in CSS2, which I can't understand.

Explicitly stated how to handle an inline box containing no
text by using the first font in the font set.

Corrected the error in CSS2 (restoring the correct CSS1
statement) that the padding, border, and margins of non-replaced
inline elements go around the font box and not the logical box.

Clearly stated the meaning of the values of text-align.

Explained in more detail how to handle blocks within inlines.

Acknowledgments

Thanks to Ian Hickson for comments on the draft, and to Eric
Meyer for asking the question
on www-style that prompted me to write this.
However, the opinions expressed represent my views only.