Библиотека masonry/bento-сеток для Vue 3, React и Nuxt — карточки разной высоты сами упаковываются в ровные колонки, с широкими bento-карточками, плавной анимацией перестроения и поддержкой больших списков.

Сетка карточек с естественно разной высотой обычно означает либо CSS columns (которая заполняет сверху вниз, колонка за колонкой — редко совпадает с визуальным порядком чтения), либо более тяжёлую masonry-библиотеку со своими требованиями к разметке. Masonry Kit измеряет уже существующие карточки и расставляет их, оставляя разметку и стилизацию полностью вашими.
Карточки естественно разной высоты нужно упаковать в визуально сбалансированные колонки — skyline-упаковка кладёт каждую карточку в наименее заполненную колонку, вместо произвольного порядка заполнения у CSS columns.
Часть карточек шире остальных — через colSpan/rowSpan они участвуют в той же skyline-упаковке, что и обычные, а не размещаются вручную в CSS Grid-шаблоне, который приходится держать в синхроне с данными.
Виртуализация держит пересчёт лёгким за счёт оценки размера элементов вне видимой области вместо измерения всего списка при каждом скролле.
До и во время гидратации рендерится приближение через CSS columns, а в тот же кадр, когда движок реально измеряет карточки, оно сменяется точной раскладкой — без заметного скачка.

Классическая masonry-упаковка «в наименее заполненную колонку», обобщённая на карточки с colSpan/rowSpan с первой строчки кода, а не пристроенная поверх позже. 'balanced' (по умолчанию) выбирает наименее заполненную группу колонок под каждую карточку, 'ordered' — строгий round-robin.

direction: 'vertical' (колонки) и 'horizontal' (строки) используют одну и ту же геометрию ядра, а не два отдельных алгоритма, которые приходится держать в синхроне.

Каждая карточка измеряется через ResizeObserver и расставляется через transform: translate(), так что reflow никогда не вызывает собственный layout — а изменение высоты картинки после загрузки само пересобирает сетку.

Двухфазная раскладка: карточки вне видимой области (± overscan) вообще пропускают измерение DOM и используют оценку размера — пересчёт остаётся дешёвым независимо от размера списка.

Карточки плавно съезжают на новое место вместо телепортации, и проявляются/исчезают при добавлении/удалении — целиком через инлайн-стили, без обязательного CSS.

Взять/переместить/отпустить через пробел и стрелки, с озвучиванием через live-регион — указательный drag намеренно не реализован: skyline-упаковка делает live-перетаскивание мышью непредсказуемым (кто окажется соседом, зависит от точного пути курсора, а не только от точки посадки) так, как дискретный шаг клавиатурой — нет.
createMasonryEngine() измеряет карточки через ResizeObserver и упаковывает их skyline-алгоритмом — без ручного расчёта колонок и без угадывания с CSS columns.
import { createMasonryEngine } from '@macrulez/masonry-kit-core'
const container = document.querySelector('#grid') as HTMLElement
const engine = createMasonryEngine(container, { columns: 'auto', minLaneSize: 240 })
engine.setItems([
{ id: 'a', el: document.querySelector('#card-a') as HTMLElement },
{ id: 'b', el: document.querySelector('#card-b') as HTMLElement },
]) Более широкая карточка с colSpan участвует в той же самой skyline-упаковке, что и обычные — отдельного CSS Grid-шаблона, который нужно держать в синхроне с данными, не требуется.
engine.setItems([
{ id: 'hero', el: heroEl, colSpan: 2 },
{ id: 'a', el: aEl },
{ id: 'b', el: bEl },
])