Layouts in CSS: Display Types and Normal Flow

One of the most important points to understand about CSS is that everything is a box.

During the parsing and layout process, browsers generate one or more boxes for each element, based on its display type.

Display types are a newer CSS concept, introduced in the CSS Display Module Level 3 specification . There are two of them: inner and outer. The inner display type affects how the descendants of an element—what’s inside the box—are arranged within it. Outer display types affect how the element’s box behaves in flow layout or normal flow. Display type is determined by the computed value of an element’s display property.

In practical terms, this means that there are two display box types that participate in normal flow:

  • block-level boxes that participate in a block formatting context
  • inline-level boxes that participate in an inline formatting context

Formatting context is a fancy way of saying that an element behaves according to the rules for boxes of this type.

Both block and inline are outer display values. The block value triggers a block formatting context for an element’s principal box, or its outermost, containing box. Using inline triggers an inline formatting context.

Inner display types include the flex / inline-flex , grid / inline-grid , and table values for the display property. These properties tell the browser how to lay out contents inside the principal box. They also provide a shorthand way to tell the browser to treat the outer box as a block-level (or inline-level) box, but arrange the stuff inside it according to the rules of its formatting context.

1. Block Formatting versus Inline Formatting

Block-level boxes are stacked in the order in which they appear in the source document. In a horizontal writing mode , they stack vertically from the top to the bottom of the screen.

In vertical modes, they sit horizontally—side by side and across the screen. With the exception of display: table and its related properties, block-level boxes also expand to fill the available width of their containing element.

Browsers generate a block-level box when the computed value of the display property is one of the following:

  • block
  • List-item
  • table or any of the table-* values such as tabLe-ceLL
  • fLex
  • grid
  • fLow-root

Other property-value combinations can also trigger block-level box behavior and a block formatting context. Multicolumn containers, for example, trigger a block formatting context when the value of column-count or column-width is something other than auto . Using column-span: aLL also triggers a block formatting context. We’ll discuss multicolumn layout later in this chapter.

Floating or positioning an element (with position: absolute or position: fixed ) also triggers a block formatting context. So does the contain property when its value is Layout , content , or strict .

Inline-level boxes, by contrast, don’t form new blocks of content. Instead, these boxes make up the lines inside a block-level box. They’re displayed horizontally and fill the width of the containing box, wrapping across lines if necessary, as shown in the image below, which shows an inline box with margin: 1em and padding: 5px applied.

Inline-level boxes have a display value of inline , inline-block , inline-table , or ruby .

Just about every browser ships with a user agent stylesheet that sets default rules for element selectors. These stylesheets typically add a display: block rule for elements such as <section> , <div> , <p> , and <uL> . Most phrasing content elements—such as <a> , <span> , and <canvas> —use the initial value of display , which is inline . When you view a document without any developer-authored CSS, you’re really seeing the computed values from the browser’s own stylesheet.

User agent stylesheets also set default styles for the root SVG element, particularly when SVG documents are combined with HTML. However, SVG documents rely on a coordinate system for layout instead of the box model. SVG elements do create a bounding box, but elements within the bounding box don’t participate in the box model or normal flow, and don’t affect the position of other elements in the document. As a result, most layout-related CSS properties don’t work with SVG elements. We’ll discuss that in greater depth in Chapter 12, “Using CSS with SVG”.

2. Logical Properties

Logical properties are closely related to block formatting. Defined by the Logical Properties and Values Level 1 specification, they affect the dimensions and position of elements.

Properties such as margin-left and width use directional or physical features of the viewport. Logical properties, on the other hand, are flow-relative. They’re affected by the value of the direction and writing-mode properties, and fall into two broad categories:

  • properties that affect the block direction
  • properties that affect the inline direction

For example, when the writing mode is horizontal, inset-bLock-start and inset-bLock-end are the top and bottom of the container respectively, as pictured below.

For vertical writing modes, however, inset-bLock-start and inset-bLock-end are the physical left and right of the container, as shown below.

The block-size property determines the vertical dimension of block-level elements when the writing mode is horizontal, and inline-size determines its horizontal dimension. They’re the equivalent of height and width respectively. When the writing mode is vertical, the inverse is true: block-size is the equivalent of width , and inline-size is the equivalent of height .

The Logical Properties specification also adds longhand properties for margins, padding, and borders. For example, the flow-relative alternative to margin-top is margin-block-start .

Be aware that browsers map the margin , padding , and border-width shorthand properties to top, right, bottom, and left. For a declaration such as border-width: 1rem , this is fine. But a declaration such as border-width: 10rem 1rem 1rem 1rem creates a physical, 10rem top border instead of a flow-relative one. You must use the longhand border-block-* , margin- block-* and padding-block-* properties if you want flow-relative borders, margins, or padding.

You’ll see flow-relative properties sprinkled throughout this chapter.

3. Box Dimensions and the Box Model

How does the browser calculate the dimensions of a block? Box dimensions are the sum of the box’s content area, plus its padding size and border size, as defined by the CSS Level 2 specification. The margin size creates a margin box for the element.

Margin boxes affect the placement of other elements in the document, but the size of the margin has no effect on the dimensions of the box itself.

Adjacent margin boxes also collapse. If two paragraph elements have top and bottom margins of 20 pixels, the margin space between them will be 20 pixels—not 40 pixels.

For instance, a <p> element with width: 300px , padding: 20px , and border: 10px , has a calculated width of 360 pixels. That’s the sum of its width, left and right padding, and left and right border-width properties. To create an element that’s 300 pixels wide with 20 pixels of padding and a ten-pixel border, the width needs to be 240px .

Let’s take a brief detour to add some historical context. Although today’s browsers calculate the width as I’ve just described, Internet Explorer 5.5 didn’t. Instead, IE5.5 used the width property as the final arbiter of box dimensions, with padding and border drawn inside the box, as shown in the image below, which compares the CSS 2.1 box with the old Internet Explorer 5.5 “quirks mode” box model.

In IE5.5, both padding and border values were, in effect, subtracted from width , decreasing the size of the content area. Though this was the exact opposite of the behavior defined in early CSS specifications, many web developers thought it was the more sensible approach.

As a way to resolve these competing models, the CSS Working Group introduced the box-sizing property. It lets us choose how the browser should calculate box dimensions.

4. Managing Box Dimensions with box-sizing

The box-sizing property is defined in the CSS Basic User Interface Module Level 3 specification. It has two possible values: content-box and border-box .

Initially, the value of box-sizing is content-box . With this value, setting the width and height (or inline-size and block-size ) properties of an element affects the size of its content area. This matches the behavior defined by the CSS 2.1 specification. It’s also the default behavior in browsers (as illustrated in the image above).

Setting the value of box-sizing to border-box creates a little bit of magic. Now the values of width and height are applied to the outer border edge instead of the content area. Borders and padding are drawn inside the element box. Let’s look at an example that mixes percentage widths and px units for padding and borders:

<div class=”wrapper”>


<h2>This is a headline</h2>

<p>Lorem ipsum dolor sit amet, consectetur adipisicing … </p>



<h2>This is a secondary headline</h2>

<p>Lorem ipsum dolor sit amet, consectetur adipisicing … </p>



Both our <article> and <aside> elements have the following CSS applied. Our first element has a width of 60%, while the second has a width of 40%:

article, aside {

background: #FFEB3B;

border: 10px solid #9C27B0;

float: left;

padding: 10px;


article {

width: 60%;


aside {

width: 40%;


The image below shows how this code renders in the browser.

By default, both <aside> and <articLe> have a box-sizing value of content-box . The border-width and padding values add 40 pixels to the width of each element, which throws off the 60%/40% split. Now let’s add box-sizing: border-box to the <article> and <aside> elements:

article, aside {

box-sizing: border-box;


You can see the change below.

The elements have the same width, but the box-sizing: border-box means that the width includes the border and padding. Because the width property applies to the border edge instead of the content area, our elements now fit side by side.

I recommend using box-sizing: border-box in your projects. It makes life easier, as there’s no need to calculate the width value to account for the values of padding and border . Boxes behave more predictably.

The best way to apply box-sizing: border-box is with reset rules. The following example is from Chris Coyier’s CSS-Tricks post, “Inheriting box-sizing Probably Slightly Better Best- Practice”:

html {

box-sizing: border-box;


*, *:before, *:after {

box-sizing: inherit;


This applies border-box sizing to every element by default, without affecting the box-sizing behavior of existing parts of your project. If you know that there’ll be no third-party or legacy components that rely on content-box behavior, you can simplify these rules:

*, *:before, *:after {

box-sizing: border-box;


In some cases, you may not want an element to generate a box at all, but still keep its contents visible to the user and retain its semantics. That’s when you’ll want to use display: contents .

5. Preventing Box Generation with display: contents

Using the contents value for the display property prevents the browser from generating an element box, without removing its semantics. Applying display: contents to an unordered list, for example, removes its default margin and padding.

In visual terms, it’s as if the <uL> element isn’t there. This is particularly useful when working with Grid and Flexbox. In grid and flexible box layout, the direct children of the grid or flex container participate in the formatting context. Adding display: contents to the child of a grid or flex container means that the container’s “grandchild” elements participate in that formatting context instead. We’ll come back to this point later in the chapter.

Chrome and Edge fixed a similar bug in their implementations of display: contents as of version 89 (released March 2021) and later. Firefox resolved its version of this bug with Firefox 62 (released in 2018).

Source: Brown Tiffany B (2021), CSS , SitePoint; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *