Using the multi-keyword syntax with CSS display
TheCSS display module defines a multi-keyword syntax for the CSSdisplay
property. This guide explains the multi-keyword syntax.
Note:Multi-keyword syntax is also referred to as "two-value syntax" or "multi-value syntax."
In this article
What happens when we change the value of the display property?
One of the first things we learn about CSS is that some elements are block-level and some are inline-level. These are theirouter display types. For example, an<h1>
or a<p>
are block-level by default, and a<span>
is inline-level. Using thedisplay
property we can switch between block and inline. For example to make a heading inline we would use the following CSS:
h1 { display: inline;}
Thedisplay
property also lets us useCSS grid layout andFlexbox whendisplay: grid
ordisplay: flex
is set. The important concept to understand is that changing an element'sdisplay
value can change the formatting context of its direct children. When you usedisplay: flex
ordisplay: grid
, the element's children become flex or grid items and respond to the properties in grid and flexbox specifications.
What grid and flexbox demonstrate, however, is that an element has both anouter and aninner display type. The outer display type describes whether the element is block-level or inline-level. The inner display type describes how the children of that box behave.
As an example, when we usedisplay: flex
we create a block-level container, with flex children. The children are described as participating in a flex formatting context. You can see this if you take a<span>
— normally an inline-level element — and applydisplay: flex
to it. The<span>
becomes a block-level element. It behaves as block-level things do in relationship to other boxes in the layout. It's as if you had applieddisplay: block
to the span, however we also get the changed behavior of the children.
The live example below has a<span>
withdisplay: flex
applied. It has become a block-level box taking up all available space in the inline direction. You can now usejustify-content: space-between
to put this space between the two flex items.
<span> Some text <em>emphasized text</em> </span>
body { font: 1.2em / 1.5 sans-serif;}.flex { border: 5px solid #cccccc; display: flex; justify-content: space-between;}
It's also possible to create inline flex containers. If you use the single valueinline-flex
you will have an inline-level box with flex children. The children behave in the same way as the flex children of a block-level container. The only thing that has changed is that the parent is now an inline-level box. It therefore behaves like other inline-level things, and doesn't take up the full width (or size in the inline dimension) that a block-level box does. This means that some following text could come up alongside the flex container.
<div> <div>One</div> <div>Two</div></div>Text following the flex container.
body { font: 1.2em / 1.5 sans-serif;}.flex > div { border: 2px solid rgb(96 139 168); border-radius: 5px; background-color: rgb(96 139 168 / 0.2);}.flex { border: 5px solid #cccccc; display: inline-flex;}
The same is true when working with grid layout. Usingdisplay: grid
will give you a block-level box, which creates a grid formatting context for the direct children. Usingdisplay: inline-grid
will create an inline-level box, which creates a grid formatting context for the children.
Using the multi-keyword syntax
As you can see from the above explanation, thedisplay
property has considerable powers. In addition to indicating whether something is block-level or inline-level in relationship to other boxes on the page, it also indicates the formatting context inside the box it is applied to. To better describe this behavior, thedisplay
property allows for two values — an outer and inner value — to be set on it. The original single-value syntax is also valid.
This means that instead of settingdisplay: flex
to create a block-level box with flex children, we usedisplay: block flex
. Instead ofdisplay: inline-flex
to create an inline-level box with flex children, we usedisplay: inline flex
. The example below demonstrates these values.
<h1>Multiple values for display</h1><div> <div>Item One</div> <div>Item Two</div> <div>Item Three</div></div><p>The first example is a block element with flex children.</p><div> <div>Item One</div> <div>Item Two</div> <div>Item Three</div></div>The second example is an inline element with flex children.
body { font: 1.2em / 1.5 sans-serif;}.flex { border: 5px solid #cccccc; gap: 10px;}.flex > * { border: 2px solid rgb(96 139 168); border-radius: 5px; background-color: rgb(96 139 168 / 0.2);}.flex1 { display: block flex;}.flex2 { display: inline flex;}
There are mappings for all of the existing values ofdisplay
; the most common ones are listed in the table below. To see a full list take a look at the table found in thedisplay
property specification.
Single value | Multi value |
---|---|
block | block flow |
flow-root | block flow-root |
inline | inline flow |
inline-block | inline flow-root |
flex | block flex |
inline-flex | inline flex |
grid | block grid |
inline-grid | inline grid |
display: block flow-root and display: inline flow-root
Regarding how this multi-value syntax helps clarify CSS layout, we can look at some values in the table above that might be less familiar to you. The multi-keyworddisplay: block flow-root
maps to a single value;display: flow-root
. This value's only purpose is to create a newBlock Formatting Context (BFC). A BFC ensures that everything inside your box stays inside, and things outside the box cannot intrude into it.
In the example below, two<p>
elements, one inside a<div>
demonstrate how display values affect formatting contexts.The first<div>
element with the demo controls is hidden so we can focus on the elements that follow instead.The elements that we should focus on are the "parent", "child", and "sibling"<div>
and<p>
elements which you can differentiate by their IDs.
What's notable about this layout is that there is no content between the parent and child elements, and the child element has a top margin applied.You might expect the top margin to effectively push the child element down within the parent element, but what happens instead is something calledmargin collapse.In this case, the margin of the child element extends well above the parent's bounding box and pushes the parent element further down the page.This is easier to see if you inspect the box model of the child elementin your browser's developer tools.
Change the selected option in the<select>
element to see the effect of differentdisplay
values.You can use any value withflow-root
to create a new formatting context for the parent, making the child element margin relative to its parent's outer edge and avoiding the margin collapse.Changing betweendisplay: flow-root
anddisplay: block flow-root
will achieve the same effect as the single-valueflow-root
keyword.
const parentDiv = document.getElementById("parent");const siblingDiv = document.getElementById("sibling");const displayTypeSelect = document.getElementById("displayType");const displayType = displayTypeSelect.value;function changeDisplayType() { parentDiv.style.display = displayType; siblingDiv.style.display = displayType;}displayTypeSelect.addEventListener("change", changeDisplayType);
#controls { padding: 1rem; outline: 2px dashed black;}body { margin: 10px; font-family: sans-serif;}
div,p { outline: 2px solid black; background-color: cornflowerblue; display: block; margin-bottom: 2rem;}#parent { background-color: oldlace; min-height: 2rem;}#child { margin-top: 4rem; outline: 2px dashed red;}#sibling { background-color: lavender;}
<div> <label for="displayType">display:</label> <select> <option value="block">block</option> <option value="flow-root">flow-root</option> <option value="block flow-root">block flow-root</option> <option value="inline">inline</option> <option value="inline flow-root">inline flow-root</option> </select></div>
<div> <p>The #child paragraph (nested in #parent).</p></div><p>The #sibling paragraph (sibling of #parent).</p>
Theflow-root
value makes sense if you think about block and inline layout, which is sometimes callednormal flow. Our HTML page creates a new formatting context (floats and margins cannot extend out from the boundaries) and our content lays out in normal flow, using block and inline layout, unless we change the value ofdisplay
to use some other formatting context. Creating a grid or flex container also creates a new formatting context (a grid or flex formatting context, respectively.) These also contain everything inside them. However, if you want to contain floats and margins but continue using block and inline layout, you can create a new flow root, and start over with block and inline layout. From that point downwards everything is contained inside the new flow root.
This is whydisplay: flow-root
can be written using the multi-keyword syntaxdisplay: block flow-root
. You are creating a block formatting context, with a block-level box and children participating in normal flow. What about the matched pairdisplay: inline flow-root
? This is the current way of describingdisplay: inline-block
.
The valuedisplay: inline-block
has been around since the early days of CSS. The reason we tend to use it is to allow padding to push inline items away from an element, when creating navigation items for example, or when wanting to add a background with padding to an inline element as in the example below.
<p> This paragraph has a span <span>with padding</span> it is an inline-block so the padding is contained and pushes the other line boxes away.</p>
body { font: 1.2em / 1.5 sans-serif;}p { border: 2px dashed; width: 300px;}.inline-block { background-color: rgb(0 0 0 / 0.4); color: white; padding: 10px; display: inline-block;}
An element withdisplay: inline-block
however, will also contain floats. It contains everything inside the inline-level box. Thereforedisplay: inline-block
does exactly whatdisplay: flow-root
does, but with an inline-level, rather than a block-level box. The two-value syntax accurately describes what is happening with this value. In the example above, you can changedisplay: inline-block
todisplay: inline flow-root
and get the same result.
What about the old values of display?
The single values ofdisplay
are described in the specification as legacy values, and currently you gain no benefit from using the multi-keyword versions, as there is a direct mapping for each multi-keyword version to a legacy version, as demonstrated in the table above.
To deal with single values ofdisplay
the specification explains what to do if only the outer value ofblock
orinline
is used:
"If a
<display-outside>
value is specified but<display-inside>
is omitted, the element's inner display type defaults to flow."
This means that the behavior is exactly as it is in a single value world. If you specifydisplay: block
ordisplay: inline
, that changes the outer display value of the box but any children continue in normal flow.If only an inner value offlex
,grid
, orflow-root
is specified thenthe specification explains that the outer value should be set toblock
:
"If a
<display-inside>
value is specified but<display-outside>
is omitted, the element's outer display type defaults to block—except for ruby, which defaults to inline."
Finally, we have some legacypre-composed inline-level values of:
inline-block
inline-table
inline-flex
inline-grid
If a supporting browser comes across these as single values then it treats them the same as the multi-keyword versions:
inline flow-root
inline table
inline flex
inline grid
So all of the current situations are neatly covered, meaning that we maintain compatibility of existing and new sites that use the single values, while allowing the spec to evolve.