Web Platform · CSS Layout

Line Box Construction and the Inline Formatting Context

How inline-level boxes are packed into line boxes, what the strut contributes to line height, and why baseline alignment produces gaps that padding cannot remove.

Text inside a paragraph does not have a “height” in the sense a block box does. The block’s content height is the sum of the heights of the line boxes the text produced, and each line box’s height is determined by the tallest inline-level content on the line after vertical alignment, not by the font size alone. A line of 16-pixel text in a container with line-height: 1.5 occupies 24 pixels, and whatever the font’s own metrics do not account for is distributed above and below the text as leading.

The model explains a class of stubborn layout problems: an image inside a line sits on the baseline but the line reserves room for descenders below it, so a few pixels of space appear underneath; a span with a background colour and padding shows that background broken at a line break; and inline-block elements align by their last line’s baseline rather than by their box.

Inline-level boxes and the anonymous line box

A block container that has inline-level children establishes an inline formatting context, and its inline content is wrapped in anonymous block boxes where necessary. Inside that context, line boxes are generated: each line box is a rectangle whose inline size is the containing block’s content inline size and whose block size is determined by the content it holds.

Three kinds of box participate. Non-replaced inline boxes contribute their text and their own font metrics. Atomic inline-level boxes, which include replaced elements such as img and video and elements with display: inline-block or inline-flex, participate as a single unbreakable unit. Inline-block boxes contain their own formatting context internally and present a single box to the outside line.

Line breaking is decided by the Unicode line breaking algorithm constrained by white-space processing. Spaces and other soft wrap opportunities are the break points; white-space: nowrap removes them, white-space: pre preserves them and also preserves newlines, and overflow-wrap: break-word or word-break allow breaks inside words when a single unbreakable run would otherwise overflow.

Line boxes that contain no content and no preserved whitespace are not generated. Preserved whitespace alone can generate an empty line box with the height of the strut, which is why white-space: pre content can show blank lines and why trailing whitespace inside an inline-block can contribute width.

The strut and half-leading

Every line box begins with a zero-width inline box called the strut, which carries the block container’s font and line-height. Even a line containing only a 10-pixel icon has a strut of the container’s font size and line height, and the line box is at least as tall as the strut.

The height of an inline box’s content area in the block direction is not its font-size. CSS 2.1 §10.6.1 leaves the construction to the font and notes two possible choices, the em box or the font’s maximum ascender plus descender; implementations take the second, so the content area is the font’s ascent plus its descent, a sum that exceeds the em size for many faces. Leading is line-height minus that content-area height, split evenly above and below as half-leading. Where the ascent and descent sum to the font size exactly, 16-pixel text with line-height: 24px leaves 8 pixels of leading and puts 4 on each side; a face whose metrics sum to 19 pixels leaves 5 and puts 2.5 on each side. The em box can also be taller or shorter than the glyphs’ ink extent, because ascenders and descenders may exceed the em square.

line-height: normal uses a font-dependent value, typically close to 1.2 times the font size for common text faces but not fixed by any specification; the font’s own metrics decide. This is why a design that relies on normal produces slightly different line heights across font families and platforms, and why explicit numeric values are used when vertical rhythm matters. A unitless line-height: 1.5 multiplies the element’s own font size and inherits as a factor, whereas line-height: 150% computes to a length at the element and inherits that fixed length, so a child with a different font size does not scale it.

At the block level, the content height is the distance from the top of the first line box’s top edge to the bottom of the last line box’s bottom edge. The line-height therefore controls block height for single-font text, and the block’s own padding and border are added outside that.

Vertical alignment against the baseline

Inline-level boxes are aligned in the block direction by vertical-align, whose initial value is baseline. The parent’s baseline is established by its own font metrics; each child inline box is shifted so that its baseline coincides with the parent’s, and the line box grows to accommodate whichever box extends furthest above or below.

The keyword values map to specific reference points:

  • baseline aligns the box’s baseline with the parent’s baseline. For an inline-block with in-flow content, that is the baseline of its last line box; if it has no in-flow line boxes, or its overflow is not visible, the baseline is its bottom margin edge.
  • middle aligns the box’s vertical midpoint with the parent’s baseline plus half the parent’s x-height.
  • text-top and text-bottom align with the top and bottom of the parent’s content area, the font-metric box before any leading is added.
  • top and bottom align with the top and bottom of the line box itself, not the parent, and are only meaningful when the line box has already been sized by other content.
  • A length raises or lowers the box by that distance; a percentage resolves against the element’s own line-height.

Alignment can change the line box height, and changing the line box height can change the position of top and bottom aligned siblings: the boxes are aligned to the baseline, the extremes are found, and only then are the line-box-relative boxes placed. When a top or bottom box is tall enough that several placements give the same line box height, CSS 2.1 leaves the position of the line box’s baseline undefined.

The classic symptom is a line containing an image. An img with display: inline (the initial value for replaced elements) sits with its bottom edge on the baseline. The line box also reserves the strut’s descender space below the baseline, so empty space appears beneath the image. Setting display: block on the image removes it from the line, and vertical-align: bottom or vertical-align: middle changes the alignment without removing the strut’s contribution to line height.

Where the inline box paints

An inline box’s background and border are painted over the content area of the fragments on each line, and its padding and margin apply at the start and end of each fragment. The default box-decoration-break: slice means the box is treated as if it were cut at each line break: a left border appears at the start of the first fragment, a right border at the end of the last, and no border appears at the break itself. Backgrounds and border images are drawn as if applied to the fragments reassembled in visual order, so a gradient runs continuously across a break instead of restarting on each line.

Setting box-decoration-break: clone paints a complete box on each fragment, including borders at every break. The property has effects on layout as well as painting: cloned decoration adds the inline-start and inline-end padding and border to each fragment, which changes how many words fit on a line and can change the number of lines.

Padding on inline boxes does not affect line height. A span with padding: 20px 0 and a background will paint 20 pixels above and below the text without pushing adjacent lines apart, so the painted area overlaps the neighbouring line boxes. Only line-height and the vertical alignment of the boxes on the line control spacing between lines. This asymmetry is the reason vertical padding on inline elements is usually replaced with display: inline-block when space is actually wanted.

/* Painted padding does not expand the line box. */
.highlight {
  padding-block: 0.5rem;
  background: gold;
}

/* An inline-block box reserves the padding in layout. */
.chip {
  display: inline-block;
  padding-block: 0.25rem;
  line-height: 1.5;
}

Interaction with the block model

An inline formatting context is created by a block container whose children are inline-level. A block-level element among inline content splits the anonymous inline content into anonymous block boxes, and the block-level element’s vertical margins then participate in the block formatting described in Adjoining Margins and the Block Formatting Context.

Inline boxes ignore width and height. Their inline size is the text or atomic content they contain, and their block size is derived from the font and line height. A declaration of height on a span has no effect unless the display type is changed to something that generates a block container. The same constraint applies to margin-top and margin-bottom: they are computed but do not affect the layout of the line, which is why vertical margins on inline elements appear inert.

When the content of a line exceeds the containing block’s inline size and cannot break, the line box overflows. The block’s content box does not grow in the inline direction; overflow is painted outside it, and the containing block’s overflow property decides whether it is clipped or scrollable. The measurement of the resulting overflow, and the interaction with scroll containers, is treated in Alignment, Overflow, and Scroll Containers.

References

  1. CSS Inline Layout Module Level 3 — W3C
  2. CSS 2.1, Section 9.4.2: Inline formatting contexts — W3C
  3. CSS 2.1, Section 10.8: Line height calculations — W3C