Published: September 19, 2024
The CSS Working Group has combined the two CSS masonry proposals into one draft specification. The group hopes that this will make it easier to compare the two, and make a final decision. The Chrome team still believes that a separate masonry syntax would be the best way to proceed. While the biggest performance issue mentioned in our previous post is resolved, there are still concerns around syntax, initial values, and how easy a version combined with grid would be to learn.
To test our assumptions however, we've worked through some examples to show how masonry would work with each version. Take a look at the examples in this post and give us your feedback, so we can make a decision and proceed with this feature.
This post doesn't cover all possible use cases, however it's clear that
separating masonry from grid layout won't result in the feature lacking
functionality. In fact, the opposite may be true. As you'll see in this post,
the display: masonry
version creates new opportunities and a simpler syntax.
In addition, many developers have raised concerns about the potential of
re-ordering items with masonry causing accessibility issues. This is also being
addressed for both versions of the syntax, through the proposed reading-flow
property.
A basic masonry layout
This is the layout most people imagine when thinking about masonry. Items display in rows, and after the first row is placed, subsequent items move into space left by shorter items.
With display: masonry
To create a masonry layout use the value of masonry
for the display
property. This creates a masonry layout with column tracks that you define (or
are defined by the content) and masonry in the other axis. The first item is
displayed at block and inline start (therefore top left in English), and items
are laid out in the inline direction.
To define tracks use masonry-template-tracks
with track listing values as used
in CSS grid layout.
.masonry {
display: masonry;
masonry-template-tracks: repeat(3, 1fr);
gap: 10px;
}
With display: grid
To create a masonry layout, first create a grid layout using the value of grid
for the display
property. Define columns with the grid-template-columns
property, then give grid-template-rows
a value of masonry
.
This will create a layout as you'd expect with auto-placed grid items, however the items in each row use a masonry layout and will rearrange to take up space left by smaller items in the preceding row.
.masonry {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: masonry;
gap: 10px;
}
Points to consider between the two options
A notable difference between these methods is that with the display: masonry
version, you get a masonry layout even if you don't specify any tracks with
masonry-template-tracks
. Therefore display: masonry
might be all you need.
This is because the initial value of masonry-template-tracks
is
repeat(auto-areas, auto)
. The layout creates as many auto-sized tracks as will
fit the container.
Reversed flow with masonry
The specification includes ways to change the direction of the masonry flow. For example, you can change the flow to display from the block-end up.
With display: masonry
Create a masonry layout with display: masonry
, then use masonry-direction
with a value of column-reverse
.
.masonry {
display: masonry;
masonry-template-tracks: repeat(3, 1fr);
masonry-direction: column-reverse;
}
With display: grid
Create a masonry layout with display: grid
and grid-template-rows: masonry
.
Then use the grid-auto-flow
property with a new value of row-reverse
to
cause the items to layout from the block end of the grid container.
.masonry {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: masonry;
grid-auto-flow: row-reverse;
}
Points to consider between the two options
The display: masonry
version feels very similar to how flexbox works. Change
the direction that columns flow using the masonry-direction
property with a
value of column-reverse
.
The CSS grid version uses grid-auto-flow
. As currently defined
grid-auto-flow: row-reverse
and grid-auto-flow: column-reverse
would give
the same effect. This could be confusing, as you might expect them to do
something different.
Masonry for rows
You could also change direction to define rows.
With display: masonry
Create a masonry layout with display: masonry
, then set the value of
masonry-direction
to row
. Unless you want your rows to have a specific block
size, you don't need to specify any track sizing as the default is auto
,
therefore the tracks will size to the content they contain.
.masonry {
display: masonry;
masonry-direction: row;
}
With display: grid
.masonry {
display: grid;
grid-template-columns: masonry;
grid-template-rows: repeat(3, 1fr);
}
Points to consider between the two options
As with reversed flow, changing the display: masonry
version from columns to
rows, means changing the value of masonry-direction
. With the grid version,
you will need to switch the values of the grid-template-columns
and
grid-template-rows
properties. Or, if using the shorthand, change the order of
the syntax.
With both of these examples of switching flow, the display: masonry
version
feels more intuitive. There's a single property controlling flow
masonry-direction
, it takes one of the following values:
row
column
row-reverse
column-reverse
You then add any sizing information needed to masonry-template-tracks
,
assuming the default auto value is not what you need.
With grid, to do the reverse direction you need to use the grid-auto-flow
property, and to do the row masonry switch the value of the grid-template-*
properties. It's also not possible in current grid syntax to leave the value for
the grid axis undefined. You always need to specify grid-template-*
properties
on the axis that doesn't have a value of masonry
.
Position items
In both versions you can explicitly position items using the line-based placement you will be familiar with from grid layout. In both versions you can only position items in the grid axis, this is the axis with the predefined tracks, you can't position items on the axis that is doing the masonry layout.
With display: masonry
The following CSS defines a masonry layout with four columns. The grid axis is
therefore columns. The item with a class of a
is placed from the first column
line to the third column line, spanning two tracks with the new
masonry-track-*
properties. This can also be defined as a shorthand of
masonry-track: 1 / 3;
.
.masonry {
display: masonry;
masonry-template-tracks: repeat(4, 1fr);
}
.a {
masonry-track-start: 1;
masonry-track-end: 3;
}
With display: grid
The following CSS defines a masonry layout with four columns. The grid axis is
therefore columns. The item with a class of a
is placed from the first column
line to the third column line, spanning two tracks with the grid-column-*
properties. This can also be defined as a shorthand of grid-column: 1 / 3;
.
If the grid axis is columns then the grid-row-*
properties will be ignored,
and if the grid axis is rows, the grid-columns-*
properties will be ignored.
.masonry {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: masonry;
}
.a {
grid-column-start: 1;
grid-column-end: 3;
}
You can use named lines with both syntaxes. The following examples show a grid
with two column lines named a
.
With display: masonry
The named lines are defined in the track listing value of
masonry-template-tracks
. The item can be placed after any line named a
.
.masonry {
display: masonry;
masonry-template-tracks: 100px [a] auto [a] auto 100px;
}
.item {
masonry-track: a;
}
With display: grid
The named lines are defined in the track listing value of
grid-template-columns
. The item is placed after the first line named a
. If
the grid-row
property is defined it will be ignored.
.masonry {
display: grid;
grid-template-columns: 100px [a] auto [a] auto 100px;
grid-template-rows: masonry;
}
.item {
grid-column: a;
grid-row: a; /* ignored */
}
You can also use named areas in both syntaxes. The following examples show a grid with three tracks named "a", "b", and "c".
With display: masonry
The tracks are named as the value of masonry-template-areas
. Because no track
sizes are defined, all three default to auto
size. The item is placed in the
"a" track.
.masonry {
display: masonry;
masonry-template-areas: "a b c";
}
.item {
masonry-track: a;
}
This works the same whether you are defining rows or columns; the only
difference would be the masonry-direction
property.
With display: grid
For columns, the syntax is essentially identical. Similarly, because no track
sizes are defined, all three default to auto
size, but you do still need to
explicitly say that the other axis is masonry:
.masonry {
display: grid;
grid-template-areas: "a b c";
grid-template-rows: masonry;
}
.item {
grid-column: a;
}
For rows, however, the value has to be written differently, because
grid-template-areas
is actually defining a two-dimensional area, and each row
is written as a separate string:
.masonry {
display: grid;
grid-template-areas: "a" "b" "c"; /* Note the difference, each row is quoted. */
grid-template-columns: masonry;
}
.item {
grid-row: a;
}
Points to consider between the two options
With any positioning, the display: masonry
syntax works better when it comes
to switching direction. As the masonry-track-*
property works in whichever
direction is the grid axis, all you need to do to change direction is change the
value of masonry-direction
. With the grid version, at the least you'll need
redundant properties to enable the switching. However, see the previous examples
for other ways in which changing direction is more complicated with the grid
version.
Shorthands
In this post the longhands have been used to make it clearer which properties
are in use, however both the display: masonry
and display: grid
versions can
be defined using shorthands.
With display: masonry
The display: masonry
shorthand uses the masonry
keyword. To create the basic
masonry layout use the following CSS:
.masonry {
display: masonry;
masonry: repeat(3, 1fr);
}
To create a simple row-based masonry layout:
.masonry {
display: masonry;
masonry: row;
}
To define tracks and a row-based layout with the shorthand:
.masonry {
display: masonry;
masonry: repeat(3, 1fr) row;
}
With display: grid
To create the basic masonry layout using the grid
shorthand.
.masonry {
display: grid;
grid: masonry / repeat(3, 1fr);
}
To create a simple row-based masonry layout:
.masonry {
display: grid;
grid: repeat(3, auto) / masonry;
}
In more complex examples, because the overall display:masonry
syntax is
simpler, more properties can be packed together into the shorthand without it
becoming overly complex.
For example, imagine creating three columns, named "a", "b", and "c", filled from the bottom up.
With display:masonry
In display: masonry
, all three of these can be set together in the shorthand:
.masonry {
display: masonry;
masonry: column-reverse "a b c";
}
Because they're auto-sized, you don't need to specify the sizes, but if you
wanted a specific size instead, that could be added. For example:
masonry: column-reverse 50px 100px 200px "a b c";
.
Also, each component can be freely reordered; there is no specific order you
need to remember. And if you wanted to do rows instead, all you need to do is
swap column-reverse
with row
or row-reverse
; the rest of the syntax stays
the same.
With display: grid
In display: grid
, these three aspects have to be set separately:
.masonry {
display: grid;
grid-template-rows: masonry;
grid-template-areas: "a b c";
grid-auto-flow: wrap-reverse;
}
Like the masonry example, this makes all the columns auto
size, but if you
wanted to supply explicit sizes, you can do so:
.masonry {
display: grid;
grid: masonry / 50px 100px 200px;
grid-template-areas: "a b c";
grid-auto-flow: wrap-reverse;
}
Or if you wanted to use 'grid' to set sizes and area names all together:
.masonry {
display: grid;
grid: "a b c" masonry / 50px 100px 200px;
grid-auto-flow: wrap-reverse;
}
In both of these examples, the order is strictly required, and different if you wanted rows instead. For example, changing to rows looks like:
.masonry {
display: grid;
grid: 50px 100px 200px / masonry;
grid-template-areas: "a" "b" "c";
}
Or, to put them all into one shorthand:
.masonry {
display: grid;
grid: "a" 50px "b" 100px "c" 200px / masonry;
}
Points to consider between the two options
The display: masonry
shorthand is likely to be widely used, given that it's a
relatively straightforward shorthand. In many cases, for a "standard" masonry
layout, you'll just set the track definitions; all other values can assume the
default.
The display: grid
version uses the existing
grid
shorthand, which is a
fairly complex shorthand, and is in our experience less frequently used by
developers. As shown in the preceding examples, even when used for simple
masonry layouts it requires care when setting the order of values.
Other considerations
This post looks at some common use cases today, however we don't know what the
future might hold for grid or masonry. A big argument for using the separate
display: masonry
syntax is it allows the two to diverge in future. In
particular with initial values—such as those for masonry-template-tracks
—it
might be useful to do something different in masonry than grid does. We can't
change the grid defaults if we go with the display: grid
version, this could
limit things we might want to do in the future.
In these examples, you can see places where the browser has to ignore
properties that are valid in grid if using masonry.
For example grid-template-areas
, where most of the values get
through away as it defines a two dimensional grid layout, in masonry we are only
fully defining one direction.
Provide your feedback
Take a look at these examples, and also the draft specification which lays each version out alongside the other. Let us know what you think by commenting on Issue 9041 or, if you prefer to write a post on your own blog or on social media, be sure to let us know on X or LinkedIn.