Title: CSS Grid Layout Module Level 3
Shortname: css-grid
Level: 3
Status: ED
Work Status: Exploring
Group: csswg
ED: https://drafts.csswg.org/css-grid-3/
Editor: Tab Atkins Jr., Google, http://www.xanthir.com/contact/, w3cid 42199
Editor: Elika J. Etemad / fantasai, Apple, http://fantasai.inkedblade.net/contact, w3cid 35400
Editor: Mats Palmgren, Mozilla, mailto:mats@mozilla.com
Editor: Jen Simmons, Apple, http://jensimmons.com/
Editor: Brandon Stewart, Apple, https://brandonstewart.net
Abstract: This module introduces masonry layout as an additional layout mode for CSS Grid containers.
WPT Path Prefix: css/css-grid/masonry/tentative/
Markup Shorthands: css yes

Introduction

This section is not normative. Grid Layout is a layout model for CSS that has powerful abilities to control the sizing and positioning of boxes and their contents. Grid Layout is optimized for 2-dimensional layouts: those in which alignment of content is desired in both dimensions.
An example of grid layout:
		     two rows of items,
		     the first being four items — the last of which spans both rows,
		     and the second being two items —
		     the first of which spans the first two columns —
		     plus the spanned item from the first row.
Representative Grid layout example
Although many layouts can be expressed with regular Grid Layout, restricting items into a grid in both axes also makes it impossible to express some common layouts on the Web. This module defines a layout system that removes that restriction so that items can be placed into Grid-like tracks in just one of the axes, while stacking them one after another in the other axis. Items are placed into the column (or row) with the most remaining space based on the layout size of the items placed so far. This module also extends CSS Grid with this new grid item placement strategy and CSS Box Alignment with new alignment features.

Background and Motivation

Masonry layout is a common Web design pattern where a number of items-- commonly images or short article summaries-- are placed one by one into columns in a way that loosely resembles stone masonry. Unlike [[CSS-MULTICOL-1|multi-column layout]], where content is placed vertically in the first column until it must spills over to the second column, [=masonry layout=] selects a column for each new item such that it is generally closer to the top of the layout than items placed later.
The Pinterest search results page exemplifies this layout:
An example of masonry layout:
			          four columns of items,
			          each item is placed into the column with the smallest height so far.
Representative masonry layout example
Here, each item has a different height (depending on the content and the width of the column), and inspecting the DOM reveals (as the visual content itself gives no indication of ordering) that each item has been placed into the column with the smallest height so far.
This layout superficially looks similar to multi-column layout; but it has the advantage that scrolling down will naturally lead to "later" items in the layout (that is, those less relevant in the search results). It's not possible to achieve this layout using earlier CSS layout models, unless you know up-front how tall each item will be, or use JavaScript for content measurement or placement.

Value Definitions

This specification follows the CSS property definition conventions from [[!CSS2]] using the value definition syntax from [[!CSS-VALUES-3]]. Value types not defined in this specification are defined in CSS Values & Units [[!CSS-VALUES-3]]. Combination with other CSS modules may expand the definitions of these value types. In addition to the property-specific values listed in their definitions, all properties defined in this specification also accept the CSS-wide keywords as their property value. For readability they have not been repeated explicitly.

Masonry Layout

		Name: grid-template-columns, grid-template-rows
		New values: masonry
		Initial: none
		Applies to: [=grid containers=]
		Inherited: no
		Percentages: refer to corresponding dimension of the content area
		Computed value: the keyword ''grid-template-columns/none'' or the keyword ''grid-template-columns/masonry'' or a [[computed track list]]
		Animation type: see [[css-grid-2#track-sizing|CSS Grid Level 2]]
	
[=Masonry layout=] can be applied to [=grid containers=] by specifying the value ''grid-template-columns/masonry'' for one of its axes. This axis is called the masonry axis, and the other axis is called the grid axis. The full power of [=grid layout=] is available in the [=grid axis=]. Line names and track sizes can be specified on the [=grid container=], and [=grid items=] can be placed into the tracks and span them using 'grid-column' / 'grid-row' as usual. The [=box alignment properties=] work the same as in a regular [=grid container=] in the [=grid axis=]. In the [=masonry axis=] however, items are laid out one after another using the [[#masonry-layout-algorithm]].
Here's a masonry layout example demonstrating placed and spanning items:
Rendering of the example above.
Subgrid [=grid items=] are supported, but subgridding only occurs in the [=grid container's=] [=grid axis=]; see [[#subgrids]] for details. If ''grid-template-columns/masonry'' is specified for both ''grid-template-columns'' and ''grid-template-rows'', then the [=used value=] for ''grid-template-columns'' is ''grid-template-columns/none'', and thus the [=inline axis=] will be the [=grid axis=]. [=Grid items=] are formed and [=blockified=] exactly the same as in a regular [=grid container=]. All CSS properties work the same as in a regular [=grid container=] unless otherwise specified by this specification. For example, ''order'' can be used to specify a different layout order for the items.

Line Name Resolution

[=Grid item=] line name resolution works the same as if ''grid-template-columns/masonry'' were replaced with ''grid-template-columns/none'', i.e. line names are resolved in both axes. The [[css-grid-2#line-placement|line name resolution]] works exactly is in CSS Grid.

Grid Axis Track Sizing

Track sizing works the same as in [[css-grid-2#algo-track-sizing|CSS Grid]], except that when considering which items contribute to intrinsic sizes: * All items explicitly placed in that track contribute, and * All items without an explicit placement contribute (regardless of whether they are ultimately placed in that track).
For example, suppose there are two columns in the [=grid axis=] and that * Items A, B, and C have no explicit placement. * Item D is explicitly placed into the first column. In this case, items A, B, C, and D all contribute to sizing the first column, while only A, B, and C (and not D) contribute to the second column.
In the case of spanning items with no explicit placement, they are assumed to be placed at every possible start position, and contribute accordingly.
For example, suppose there are 5 columns in the [=grid axis=], with the middle having a fixed size of ''100px'' and the other two being ''auto''-sized. For the purpose of track sizing, an item that spans 2 tracks and has an intrinsic contribution of 220px is essentially copied and assumed to exist: * At grid line 1, contributing 110px to each of the first two tracks. * At grid line 2, contributing 120px to the second track. * At grid line 3, contributing 120px to the fourth track. * At grid line 4, contributing 110px to the fourth and fifth tracks.
Note: This algorithm ensures that each track is at least big enough to accommodate every item that is ultimately placed in it, and does not create dependency cycles between placement and track sizing. However, depending on the variation in sizes, tracks could be larger than necessary: an exact fit is only guaranteed if all items are explicitly placed in the [=grid axis=] or all items are the same size (or matching multiples of that size, in the case of spanning items).

repeat(auto-fit)

''grid-template-rows/repeat(auto-fit)'' behaves as ''grid-template-rows/repeat(auto-fill)'' when the other axis is a [=masonry axis=]. The reason for this is that ''grid-row/auto''-placed items depend on the layout size of their siblings. Removing empty tracks after layout wouldn't be possible in most cases since it might affect any intrinsic track sizes. Even if all track sizes are definite, the containing block size could change for grid-aligned absolutely-positioned descendants. This makes ''grid-template-rows/repeat(auto-fit)'' impossible to support in a [=grid container=] with [=masonry layout=].

Masonry Layout Algorithm

Items are placed in [=order-modified document order=], but items with a definite placement are placed before items with an indefinite position (as in regular grid layout). For each of the tracks in the [=grid axis=], keep a running position initialized to zero. First for each item with a definite placement in the [=grid axis=], then for each item with an indefinite placement:
  1. If the item has an definite placement in the [=grid axis=], use that placement. Otherwise, resolve its [=grid axis=] placement using these substeps:
    1. Starting at the first [=grid axis=] line in the [=implicit grid=].
    2. Find the largest [=running position=] of the [=grid axis=] tracks that the item would span if it were placed at this line, and call this position max_pos.
    3. Increment the line number and repeat step 2 until the item would no longer fit inside the grid.
    4. Pick the line that resulted in the smallest max_pos as the item's definite placement in the [=grid axis=].
  2. Place the item in its [=grid axis=] tracks at the maximum of the [=running position=]s of the tracks it spans.
  3. Calculate the size of the item's containing block and then layout the item. Then calculate its resulting [=margin box=] in the [=masonry axis=]. Set the [=running position=] of the spanned [=grid axis=] tracks to max_pos + margin-box-end + grid-gap.

Containing Block

The [=containing block=] for a [=grid item=] participating in [=masonry layout=] is formed by its [=grid area=] in the [=grid axis=] and the [=grid container=]'s [=content box=] in the [=masonry axis=].

The Implicit Grid

The [=implicit grid=] is formed in the same way as for a regular [=grid container=]. However, it's only used in the [=grid axis=]. The flow axis specified by ''grid-auto-flow'' is ignored: items are always placed by filling the [=grid axis=]. ''direction:rtl'' reverses the grid if the [=inline axis=] is the [=grid axis=] (as usual for a regular [=grid container=]) and it makes items flow from right to left if the [=inline axis=] is the [=masonry axis=].
Here's a simple example using ''direction: rtl'' in the [=grid axis=]: ```css ``` ```html
1 2 3 4
```
Rendering of the ''direction: rtl'' example above.
Here's a simple example using ''direction: rtl'' in the [=masonry axis=]: ```css ``` ```html
1 2 3 4
```
Rendering of the ''direction: rtl'' example above.

Sizing Grid Containers

[[css-grid-2#intrinsic-sizes|Sizing Grid Containers]] works the same as for regular [=grid containers=] but with the following addendum for the [=masonry axis=]: The max-content size (min-content size) of a [=grid container=] in the [=masonry axis=] is the largest distance between the [=grid container's=] [=content-box=] [=start=] edge and the maximum [=margin-box=] end of all the items, when sized under a max-content constraint (min-content constraint).
Here's a simple example: ```css ``` ```html
1 2 3 4 5 6
```
Rendering of the [=grid container=] intrinsic sizing example above.

Alignment and Spacing

[[css-grid-2#gutters|Gutters]] are supported in both axes. In the [=masonry axis=], the gap is applied between the margin boxes of each pair of adjacent items. Margins do not collapse in either axis. In the [=grid axis=], [[css-grid-2#alignment|alignment]] works the same as in a regular [=grid container=]. In the [=masonry axis=], [[css-align-3#content-distribution|content-distribution]] is applied to the content as a whole, similarly to how it behaves in block containers. More specifically, the alignment subject is the masonry box, which is the smallest rectangle bounding the [=margin boxes=] of all the [=grid items=].
The extent of the [=masonry box=] is indicated by the dashed border. (Note that item 1 has a 5px bottom margin here.)
Note: There is only ever one alignment subject for these properties in the [=masonry axis=], so the unique 'align-content' / 'justify-content' values boil down to ''align-content/start'', ''align-content/center'', ''align-content/end'', and [=baseline alignment=]. (The behavior of ''align-content/normal'' and ''align-content/stretch'' is identical to ''align-content/start'', and the [=distributed alignment=] values behave as their [=fallback alignments=].) If the [=grid items=] overflow the [=grid container=]'s [=content box=] in the [=masonry axis=], then the [=masonry box=] will be larger than the [=grid container=]'s [=content box=].

Baseline Alignment in the Masonry Axis

Item [=baseline alignment=] inside the [=grid axis=] tracks works as usual for a regular [=grid container=], and the [=grid container=]'s baseline is determined the same as for a regular [=grid container=] in that axis. [=Baseline alignment=] is not supported in the [=masonry axis=]. The first baseline set of the [=grid container=] in this axis is generated from the [=alignment baseline=] of the first [=grid item=] in the first occupied track, and the last baseline set from the last [=grid item=] placed. ISSUE: We could support baseline alignment in the first row. Do we want to? ISSUE: Should the last baseline come from the last lowest item placed instead?

Fragmentation

Fragmentation in the Masonry Axis

Each [=grid axis=] track is fragmented independently in the [=masonry axis=]. If a [=grid item=] is fragmented, or has a [=forced break=] before/after it, then the [=running position=] for the tracks that it spans in the [=grid axis=] are set to the size of the [=fragmentainer=] so that no further items will be placed in those tracks. An item that is split into multiple fragments retains its placement in the [=grid axis=] for all its fragments. A grid item that is pushed, however, is placed again by the next [=grid container=] fragment. Placement continues until all items are placed or pushed to a new fragment.
Here's an example illustrating fragmentation of a grid with masonry layout in its [=block axis=]. It renders like this:
Visualization of fragmentation in a [=block-axis=] [=masonry layout=].

Fragmentation in the Grid Axis

Fragmentation in the [=grid axis=] with [=masonry layout=] in the other axis is also supported. In this case the fragmentation behaves more like in a regular [=grid container=]; however, there's a separate step to determine which [=grid-axis=] track each item is placed into, before fragmentation occurs.
Here's an example illustrating fragmentation of a grid with [=masonry layout=] in its [=inline axis=]. In this case the breaks occurs between the [=grid-axis=] rows. It renders like this:
Visualization of fragmentation in the [=block axis=] with [=inline-axis=] [=masonry layout=].

Subgrids

[=Masonry layout=] is supported in [=subgrids=] (e.g. ''grid: subgrid / masonry''), and grids that use [=masonry layout|masonry=] can have subgrids as children. However, only a parent grid axis can be subgridded in the normal sense. A [=subgrid axis=] with a parent [=masonry axis=] will behave as ''grid/masonry'', unless the subgrid's other axis is also ''grid/masonry'' in which case it behaves as ''grid/none'' (because a [=grid container=] can only have one [=masonry axis=]). ''grid-column/auto''-placed subgrids don't inherit any line names from their parent grid, because that would make the placement of the [=subgrid=]'s [=grid items=] dependent on layout results; but the subgrid's tracks are still aligned to the parent's tracks as usual. Here's a subgrid example:
```css ``` ```html
1 2 3 subgrid.1 sub.2 s.3 4 5 6 7
```
The rendering of the subgrid example above.
Note how the subgrid's first item ("subgrid.1") contributes to the intrinsic size of the 2nd row in the parent grid. This is possible since the subgrid specified a definite placement so we know which tracks it will occupy. Note also that trying to subgrid the parent's [=masonry axis=] results in the subgrid getting [=masonry layout=] in its [=inline axis=].

Absolute Positioning

[[css-grid-1#abspos-items|Grid-aligned absolute-positioned descendants]] are supported. In the [=masonry axis=], all [=grid positions=] except line 1 are treated as ''grid-area/auto''. Line 1 in the [=masonry axis=] corresponds to the start of the [=masonry box=] (which is usually also the [=start=] [=content edge=]) and ''grid-area/auto'' uses the [=grid container=] [=padding edge=] as usual. The [=containing block=] is the extent of the tracks the item spans in the [=grid axis=] and the position of line 1 and the [=padding edge=] in the [=masonry axis=]. ISSUE: It might be useful to define a static position in the [=masonry axis=]. Maybe it could defined as the max (or min?) current [=running position=] of the [=grid-axis=] tracks at that point? Or the end of the item before it? ISSUE: It would also be useful to be able to align the [=masonry box=] [=end=] edge somehow, but for that we need a way to address the end line in an implicit grid, or could we just use any non-auto line number other than 1 to indicate the end line given that we don't really have any lines in this axis other than line 1?

Performance Notes

In general, masonry layout should have significantly better performance than the equivalent regular (2-axis) grid layout, particularly when the [=masonry axis=] is the [=block axis=] since the intrinsic sizing of grid rows is typically quite expensive. Any intrinsic track sizing in the [=grid axis=] should be cheaper too, because, typically, only a subset of items contribute to the intrinsic sizing in a masonry layout, contrary to a 2-axis grid where all items spanning an intrinsically-sized track contribute. Stretched items do a second layout with the new size (when it actually changed) so this can be costly if there are a huge amount of stretched items that each contains a lot of content. Especially nested stretched masonry layouts should be avoided unless they are small/trivial. Advisement: This can be ameliorated by the author by opting out from the stretching on most items though, e.g. specifying ''justify/align-items:start'' and then opting in for just a few items with ''justify/align-self:stretch'' to let those items fill the [=masonry axis=]. (This performance analysis is from a Gecko perspective, but I suspect there's some truth to it for other layout engines as well.)

Graceful Degradation

Typically, a masonry design can be expected to degrade quite nicely in a UA that supports Grid layout but not masonry layout if the 'grid'/'grid-template' shorthands are avoided and the longhands are used instead. e.g. ```css grid-template-rows: masonry; /* ignored by UAs that don't support it */ grid-template-columns: 150px 100px 50px; ```
Here's an example to illustrate this. It's a layout with three columns, but will have "more gaps" in the [=block axis=] if the UA doesn't support masonry layout. Here's what it looks like with Masonry support for comparison:
Rendering of the example in a UA with Masonry support.

Acknowledgements

Thanks goes to Cameron McCormack who wrote a masonry layout explainer document (from which I lifted the Background chapter) and presented it to the CSSWG. Thanks also to everyone who provided feedback on the initial proposal for this feature.