Skip to content

Commit 452e673

Browse files
committed
[css-grid-2] Add masonry layout explainer.
1 parent bd5124b commit 452e673

8 files changed

Lines changed: 243 additions & 0 deletions

File tree

css-grid-2/MASONRY-EXPLAINER.md

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
# Masonry Layout
2+
3+
## Problem description
4+
5+
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, so-called because it resembles stone masonry. Unlike regular column layout, where items are placed vertically in the first column until they must spill 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.
6+
7+
The Pinterest search results page exemplifies this layout:
8+
9+
![Pinterest search results screenshot](masonry-explainer-images/pinterest.png)
10+
11+
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.
12+
13+
An advantage to using this layout over regular column layout is that the masonry layout container height grows as more items are placed into it, as a balancing multi-column layout would, but the effect for the reader is that scrolling down will naturally lead to "later" items in the layout (that is, those less relevant in the search results).
14+
15+
Achieving this layout without knowing upfront how tall each item will be is not possible without using script.
16+
17+
## Examples demonstrating proposed solution
18+
19+
Our proposed solution is an extension to the CSS Grid model to support the automatic placement of grid items using masonry rules instead of the existing auto-placement algorithm. Below are some examples to demonstrate.
20+
21+
### Example 1 – Simple masonry layout
22+
23+
This shows how to achieve the Pinterest search results page layout:
24+
25+
```html
26+
<style>
27+
/* masonry layout with items automatically placed in as many 200px columns as will fit */
28+
.results {
29+
display: grid;
30+
grid-template-rows: masonry;
31+
grid-template-columns: repeat(auto-fill, 200px);
32+
gap: 40px;
33+
justify-content: center; /* center columns in the grid container */
34+
}
35+
36+
/* size images to take up their grid column width */
37+
img { width: 100%; }
38+
</style>
39+
<div class="results">
40+
<div><img src="R1.jpg"><br>❤️😃😮 42</div>
41+
<div><img src="R2.jpg"></div>
42+
<div><img src="R3.jpg"></div>
43+
<div><img src="R4.jpg"></div>
44+
...
45+
</div>
46+
```
47+
48+
![Simple example screen shot](masonry-explainer-images/simple-screenshot.png)
49+
50+
By specifying `grid-template-rows: masonry`, we cause items to be placed in the column that has the least content so far. Columns tracks are created as usual (due to the `repeat(auto-fill, 200px)` value for `grid-template-columns`), but row tracks are not used for the final layout of the grid items; instead each item assigned to a column is positioned just below (including any `gap`) the previous item.
51+
52+
### Example 2 – Spanning columns
53+
54+
Since this is a type of grid layout, we can make items span multiple columns. Taking the same Pinterest example and making all portrait orientation images span two columns:
55+
56+
```html
57+
...
58+
<div class="results">
59+
<div><img src="R1.jpg"><br>❤️😃😮 42</div>
60+
<div style="grid-column: auto / span 2;"><img src="R2.jpg"></div>
61+
<div><img src="R3.jpg"></div>
62+
<div><img src="R4.jpg"></div>
63+
...
64+
</div>
65+
```
66+
67+
![Spanning item screen shot](masonry-explainer-images/span-screenshot.png)
68+
69+
Here, the `grid-column: auto / span 2` property causes the second item to span two column tracks. `auto` is used for the starting grid line, allowing the automatic placement by the masonry layout algorithm into the next appropriate column to work.
70+
71+
### Example 3 — Non-uniform column widths
72+
73+
As with regular CSS Grid layout, columns can be assigned different widths:
74+
75+
```html
76+
<style>
77+
.results {
78+
display: grid;
79+
grid-template-rows: masonry;
80+
grid-template-columns: 100px repeat(auto-fill, 200px) 100px;
81+
gap: 40px;
82+
justify-content: center;
83+
}
84+
img { width: 100%; }
85+
</style>
86+
<div class="results">
87+
...
88+
</div>
89+
```
90+
91+
![Different column widths screen shot](masonry-explainer-images/widths-screenshot.png)
92+
93+
### Example 4 – Definite placement of items
94+
95+
If we want to ensure an item is placed in a specific column, we can use the regular grid placement properties. For example, to place the item with the reaction emoji in the final column:
96+
97+
```html
98+
...
99+
<div class="results">
100+
<div style="grid-column: -2;"><img src="R1.jpg"><br>❤️😃😮 42</div>
101+
<div><img src="R2.jpg"></div>
102+
<div><img src="R3.jpg"></div>
103+
<div><img src="R4.jpg"></div>
104+
...
105+
</div>
106+
```
107+
108+
![Definite item placement screen shot](masonry-explainer-images/placement-screenshot.png)
109+
110+
### Example 5 – Intrinsic size based column widths
111+
112+
Tracks can be sized based on item intrinsic sizes, with some limitations due to masonry automatic placement occurring after track sizing:
113+
114+
```html
115+
<style>
116+
body { font: 16px sans-serif; }
117+
h1 { font-size: 24px; text-transform: uppercase; margin-top: 0; }
118+
img { width: 100%; }
119+
120+
.container {
121+
display: grid;
122+
/* auto size the first column; only the first automatically placed item in this
123+
column's intrinsic size influences the track width */
124+
grid-template-rows: masonry;
125+
grid-template-columns: auto repeat(3, 150px);
126+
gap: 20px;
127+
justify-content: center;
128+
}
129+
130+
.feature {
131+
background-color: #ddd;
132+
border-radius: 8px;
133+
padding: 8px;
134+
width: min-content;
135+
}
136+
</style>
137+
<div class="container">
138+
<div class="feature">
139+
<h1>Eyjafjallajökull</h1>
140+
<div>Icelandic volcano name causes havoc for page layouts across the Web!</div>
141+
</div>
142+
<div><img src="R1.jpg"></div>
143+
<div><img src="R2.jpg"></div>
144+
...
145+
</div>
146+
```
147+
148+
![Track sizing screen shot](masonry-explainer-images/sizing-screenshot.png)
149+
150+
### Example 6 – Aligning items in a column
151+
152+
Since items within each column are not aligned with items in adjacent columns, there is an opportunity to align all of the items vertically in each column independently. (As opposed to regular grid layout, where only either tracks as a whole, or items within a single grid area, can be aligned.)
153+
154+
```html
155+
<style>
156+
...
157+
.container {
158+
/* auto size the first column; only the first automatically placed item in this
159+
column's intrinsic size influences the track width */
160+
grid-template-rows: masonry;
161+
grid-template-columns: auto repeat(3, 150px);
162+
gap: 20px;
163+
justify-content: center;
164+
165+
/* align items in each column to the bottom of the container */
166+
align-tracks: bottom;
167+
}
168+
...
169+
</style>
170+
...
171+
```
172+
173+
![Track alignment screen shot](masonry-explainer-images/align-screenshot.png)
174+
175+
176+
## Summary of proposed solution
177+
178+
In the common case (and as shown in all the examples above), items are stacked in the grid columns, without the vertical alignment that comes from items being placed into rows as in regular CSS Grid layout. The horizontal axis (along which the vertical grid lines are placed) is termed the "grid axis", and the vertical axis (in which items are stacked) is termed the "masonry axis". At most one axis can use masonry layout.
179+
180+
Syntax:
181+
182+
* a new value `masonry` for `grid-template-columns` and `grid-template-rows` properties, indicating that masonry layout is to be used for the grid container in the specified axis
183+
* new properties `justify-tracks` and `align-tracks` (taking the same values as their `justify-content`/`align-content` cousins), which can be used to align items within a grid axis track
184+
* a new property `masonry-auto-flow`, to control aspects of the masonry layout algorithm
185+
186+
**[For ease of explanation, we assume below that masonry layout is specified for `grid-template-rows`.]**
187+
188+
Conceptually, a grid container using masonry layout only generates a one dimensional grid. No grid rows are generated.
189+
190+
Grid columns are generated as a result of the usual `grid-template-columns`, `grid-auto-columns`, and item placement properties.
191+
192+
The grid columns are sized by running the standard [grid item placement algorithm](https://drafts.csswg.org/css-grid/#auto-placement-algo) to generate a hypothetical grid, find a placement for each item in this hypothetical grid, and then running the [grid sizing algorithm](https://drafts.csswg.org/css-grid/#algo-overview) on it to compute the column sizes. Only the intrinsic sizes of the following items are taken into account when sizing the columns:
193+
194+
* items that have a definite placement in the grid axis (e.g. with `grid-column: 3 / span 2`)
195+
* items that have an automatic placement that puts them in the first row
196+
* items that span all columns of the grid
197+
198+
(Since masonry layout places items into columns depending on the filled height of the columns so far, the only items that we know will be in specific columns regardless of layout, and thus can be taken into account when sizing the columns, are these ones.)
199+
200+
Once the grid columns have been created and sized, masonry layout is performed to place each item. Each grid column keeps track of a running position, initialized to zero, which is used to determine which column (or span of columns) to assign the next item to. As each subsequent item is placed, the running position for the column (or span of columns) the item is placed in is updated. `gap` values and margins between items are applied.
201+
202+
The masonry grid item placement is done in `order`-modified document order.
203+
204+
The `masonry-auto-flow` property allows control of aspects of the masonry layout algorithm. Its initial value is `definite-first pack`, which causes definitely placed items to be handled before automatically placed items (since that is how the regular grid layout placement algorithm works), and for the columns chosen for each item to be based on the smallest running position so far. Keywords to override these defaults:
205+
206+
* an `ordered` value in place of `definite-first`, which causes items to be placed in their pure `order`-modified document order, rather than selecting definitely placed items first
207+
* a `next` value in place of `pack`, which causes each column in term to be selected, rather than using the running position to choose the shortest one
208+
209+
Finally, we use the value of `align-tracks` to align, distribute, and stretch the items in each column. This supports a comma separated list of keywords to apply to each grid column, with the last value applying to any remaining columns. Alignment in the grid axis behaves as in regular grid layout.
210+
211+
## Details
212+
213+
More details, including on sizing the masonry grid container, performing baseline alignment of items, fragmentation, and interaction with subgrid, are in [#4650](https://github.com/w3c/csswg-drafts/issues/4650).
214+
215+
## Prototype
216+
217+
A prototype of this proposal is available in Firefox Nightly, behind a pref. See [this comment](https://github.com/w3c/csswg-drafts/issues/4650#issuecomment-620864503) for how to enable it.
218+
219+
## Issues
220+
221+
* How do we define a static position for absolutely positioned children of a masonry grid container, and what containing block do we use to align them?
222+
223+
## Why extend CSS Grid?
224+
225+
The reasons we think extending CSS Grid is a reasonable solution, as opposed to creating a wholly new layout model with a new `display` type:
226+
227+
* It suits requirements from existing masonry layouts found on the Web, such as having items that span multiple columns and using gaps between items.
228+
* It avoids needing to duplicate concepts from CSS Grid, which means that it should not only be easier to specify, but it will also be easier for authors to learn if they are already familiar with Grid and Alignment.
229+
* It supports more control than a solution written from scratch likely would, e.g. by getting CSS Grid line name resolution, item placement, complex track sizing functions, and subgrid support for free.
230+
* Supporting masonry layout in developer tools will require much less work, since it's easy to augment existing Grid tools.
231+
* It allows for graceful degradation to a regular grid layout, if written with for example:
232+
233+
```css
234+
grid-template-rows: masonry; /* ignored if masonry not supported */
235+
grid-template-columns: repeat(4, 100px); /* applied even if masonry not supported */
236+
```
237+
238+
## Alternative solutions
239+
240+
If the script overhead is acceptable, then there are a number of script libraries and framework plugins that implement masonry layout, the first of which was [Masonry by David DeSandro](https://masonry.desandro.com/). As with all widely deployed scripted layout solutions, they can only participate in the surrounding page layout in limited, less efficient ways than built-in engine support for a layout model. Although often this is sufficient.
241+
242+
Houdini Layout provides a script API for authors to correctly hook into the browser's layout engine, so this provides a way to correctly participate in the surrounding page layout. This comes at the cost of being more complex to write than a simple, resize event or `ResizeObserver` using script.
243+
54.3 KB
Loading
1.27 MB
Loading
29.2 KB
Loading
39.4 KB
Loading
50.2 KB
Loading
35.8 KB
Loading
31.2 KB
Loading

0 commit comments

Comments
 (0)