CSS layout has a lot of legacy complexity but has mostly been retroactively explained as a consistent cross-browser model. This document lists the concepts we care about today, and the way they're used in widget design.
There are three general-purpose algorithms used by an element to lay out its children: Flow, Flex and Grid. Flow is the original HTML layout model, intended for text-based documents; the others are newer and often more suitable for application layouts. Grid is also useful for large-scale layout of document-like pages, and Flex is generally the most suitable for composable widgets, but all three are relevant.
For historical reasons, the detail of Flow layouts is controlled by the layout of their child elements - each either a 'block' or an 'inline' - as well as the float
and clear
properties on those children. These properties are difficult to use correctly and should be avoided in favour of flex/grid.
Flex and Grid layouts are configured using the box alignment properties (see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Alignment) as well as various flex-[..] and grid-[..] properties. Confusingly, the grid
shorthand property combines the layout properties of the outer element, but the flex
shorthand property is for flex items to configure their role within a flex layout. This is essentially the difference between the two modern layouts: a Grid specifies templates and regions within which its children just select an slot (using grid-column, grid-row or grid-area). A Flow sets alignment and wrapping rules (as does Grid) but the formation of 'rows' and 'columns' is up to its children.
DISPLAY INSIDE/OUTSIDE (see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Display)
The display
property is effectively shorthand for display-inside
and display-outside
, as well as several other more obscure properties. display-inside
controls the layout of an element's children, and display-outside controls the layout of the element itself if it is part of a flow layout. Generally we will only use the higher level display:
, which is idiomatic and has better browser support (since the other two are retcons). We're mostly interested in the following values:
display: flow
- this is the default and is not usually explicitly set as it doesn't specify display-outside. Instead, use one of the next two values.display: block
- Flow layout inside; box outside.display: inline
- Flow layout inside; inline outside.display: flex
- Flex layout inside; box outside.display: grid
- Grid layout inside; box outside.
For building inlines, the following shorthand values may also be useful:
display: inline-block
- New flow layout inside, not part of the existing formatting tree; inline outside.display: inline-flex
- Flex layout inside; inline outside.display: inline-grid
- Grid layout inside; inline outside. They're being obsoleted in favour of the explicit outside/inside settingsinline
/flow-root
,inline
/flex
andinline
/grid
, but the new version is not well supported.
There are also some special display
values which are retconned shorthand for display-box
:
display: none
- Nothing is rendered at all. This is the most complete way to 'toggle off' a subtree of DOM elements.display: content
- The element is replaced by its children. Used for semantic 'markers', but it has a number of special cases and bugs.
By default, all components in the framework are built as 'boxes' which are suitable for use in any of the major layout models, except for components specifically intended to be used as 'inlines' in a Flow layout. These unusual components will generally be wrappers around some sort of primitive (NavLink, TextInput) or or will be part of a family like FooField
.
What this means in practice is that the top level of most components should be a block-level element (div
, p
, article
etc) and/or should have a display
value like block
, flex
or grid
. The top level element of inline components should be an inline element (span
, a
, em
etc) or have a display
value such as inline-flex
or inline-grid
. It's preferable to use an element of the right type, making the markup comprehensible and easier to style, but this isn't a hard rule - if you want display: inline-block
you aren't going to find some sort of built in <divspan>
.