Skip to content

Commit f44c246

Browse files
committed
docs: add article about css-only approach
1 parent 0c0afd4 commit f44c246

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
---
2+
title: "Masonry Grid Goes CSS-Only: An Experimental Approach"
3+
description: A new experimental approach to masonry layouts using CSS Grid's grid-row span property. Pure CSS, perfect for SSR, but with trade-offs.
4+
sidebar:
5+
hidden: true
6+
head:
7+
- tag: meta
8+
attrs:
9+
property: og:image
10+
content: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/frc1qxx2g8gzaaw3iv5m.png
11+
- tag: meta
12+
attrs:
13+
name: twitter:image:src
14+
content: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/frc1qxx2g8gzaaw3iv5m.png
15+
---
16+
17+
I recently published an [article](https://masonry-grid.js.org/articles/masonry-grid-a-14-kb-library-that-actually-works/) about Masonry Grid, and here I am again with another post. What happened?
18+
19+
Well, something interesting popped up in the Reddit comments.
20+
21+
## A CSS Trick from the Comments
22+
23+
After sharing the initial release, [Zardoz84](https://www.reddit.com/user/Zardoz84/) in the comments linked to [a clever CSS-only masonry example](https://codepen.io/zardoz89/pen/KKVEGbw). The example used a neat trick with `grid-row: span X` to create a masonry effect without any JavaScript.
24+
25+
The original example was hand-tuned for specific content, but the technique itself caught my attention. Could this approach work with dynamic content?
26+
27+
## Adapting the Span Trick
28+
29+
The core idea is simple:
30+
31+
```css
32+
.grid {
33+
display: grid;
34+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
35+
}
36+
37+
.item {
38+
aspect-ratio: var(--width) / var(--height);
39+
grid-row: span calc(var(--height) / var(--width) * var(--precision));
40+
}
41+
```
42+
43+
The trick is elegant: each item maintains its aspect ratio with CSS `aspect-ratio`, then spans multiple grid rows based on that ratio. The `precision` parameter acts as a multiplier - the higher the value, the more accurately the aspect ratio is maintained.
44+
45+
I adapted this technique to work with dynamic content and aspect ratios, and that's how `SpannedMasonryGrid` was born:
46+
47+
```tsx
48+
import { SpannedMasonryGrid as MasonryGrid, SpannedFrame as Frame } from '@masonry-grid/react'
49+
50+
<MasonryGrid
51+
frameWidth={200}
52+
gap={10}
53+
precision={10} // Controls aspect-ratio accuracy
54+
>
55+
<Frame width={16} height={9}>
56+
<img src="photo1.jpg" />
57+
</Frame>
58+
<Frame width={3} height={2}>
59+
<img src="photo2.jpg" />
60+
</Frame>
61+
{/* More frames... */}
62+
</MasonryGrid>
63+
```
64+
65+
## The Obvious Advantages
66+
67+
**No JavaScript Required**
68+
69+
The layout is pure CSS. Once the HTML is rendered, the browser handles everything. No observers, no reflow calculations, no JavaScript at all.
70+
71+
**Perfect SSR Compatibility**
72+
73+
Since the layout is determined entirely by CSS, there's no client-side recalculation after hydration. What the server renders is what the user sees - no layout shifts, no flicker.
74+
75+
## The Trade-offs
76+
77+
But it's not all perfect. There are some notable limitations:
78+
79+
**Aspect Ratio Imprecision**
80+
81+
Due to how `grid-row: span` works with fractional values, frames can't maintain their aspect ratios with 100% accuracy.
82+
83+
The `precision` parameter (default: 10) lets you control this:
84+
85+
- **Lower precision (~10)**: Less accurate aspect ratios, but very stable
86+
- **Higher precision (>=100)**: More accurate sizes, but potential for visual bugs in some browsers with many items
87+
88+
**Gap Workaround Required**
89+
90+
CSS Grid's native `gap` property can't be used with this technique - it would break the span calculations. Instead, we use negative margins and padding:
91+
92+
```css
93+
.grid {
94+
margin: calc(-1 * var(--gap) / 2);
95+
}
96+
97+
.frame > div {
98+
inset: calc(var(--gap) / 2);
99+
}
100+
```
101+
102+
It works, but it's a workaround, not a clean solution.
103+
104+
## Conclusion
105+
106+
This is an **experimental** approach - it has trade-offs, and the classic `BalancedMasonryGrid` and `RegularMasonryGrid` remain the safer options for most cases. But if the CSS-only nature fits your needs, give it a try!
107+
108+
Check out the interactive examples:
109+
110+
- [React](https://masonry-grid.js.org/examples/react-spanned/)
111+
- [Preact](https://masonry-grid.js.org/examples/preact-spanned/)
112+
- [Svelte](https://masonry-grid.js.org/examples/svelte-spanned/)
113+
- [SolidJS](https://masonry-grid.js.org/examples/solid-spanned/)
114+
115+
Play with the precision slider and see how it affects the layout. You'll quickly get a feel for the trade-offs.
116+
117+
The library is open source and available on [GitHub](https://github.com/TrigenSoftware/masonry-grid). Contributions, feedback, and bug reports are always welcome!

0 commit comments

Comments
 (0)