Card 1
Columns balance themselves as content settles.
Responsive columns from a breakpoint map, with images the grid holds items back for until they decode.
Column count follows the container width through a breakpoint map — { xs: 1, sm: 2, md: 3, xl: 4, '2xl': 5 }, named against the default scale, and matched against this panel rather than the viewport. Cards with images are held back until decode() resolves, so they never land at the wrong height and shove their neighbours around.
<!-- Responsive by breakpoint, matched against the container. -->
<masonry-grid [options]="options()" (layoutComplete)="stats.set($event)">
@for (card of cards(); track card.id) {
<article masonryGridItem class="card">
@if (card.image) {
<!-- width/height reserve the space so the grid never jumps -->
<img [src]="artwork(card.hue)" width="400" height="260" [alt]="card.title" />
}
<div class="card-body">
<h2>{{ card.title }}</h2>
<p>{{ card.body }}</p>
</div>
</article>
}
</masonry-grid>
<!-- options() -->
{
columns: { xs: 1, sm: 2, md: 3, xl: 4, '2xl': 5 },
gutter: 16,
entryAnimation: {},
}Columns balance themselves as content settles.
Positioning runs on transforms, off the layout path.
A single ResizeObserver watches the whole grid.
Order comes from the DOM, so nothing shuffles.
Options are validated in development and compiled out of production.
Items hold their place until their images decode.
Repeated passes with identical input do no work.
Columns balance themselves as content settles.
Positioning runs on transforms, off the layout path.
A single ResizeObserver watches the whole grid.
Order comes from the DOM, so nothing shuffles.
Options are validated in development and compiled out of production.
Items hold their place until their images decode.
Repeated passes with identical input do no work.
Columns balance themselves as content settles.
Positioning runs on transforms, off the layout path.
A single ResizeObserver watches the whole grid.
Order comes from the DOM, so nothing shuffles.
Options are validated in development and compiled out of production.
Items hold their place until their images decode.
Repeated passes with identical input do no work.
Columns balance themselves as content settles.
Positioning runs on transforms, off the layout path.
A single ResizeObserver watches the whole grid.