Column masonry of tiles with mixed heights and optional tint.
01
Magnetic
02
Orbit
03
Dock
04
Flip
05
Book
06
Split
npx @agents-npm-packages/vibeui add masonry --dir ./src/components/uiImport
import { Masonry } from "@/components/ui/masonry"Generated from the component's TypeScript types, so it always matches the source. * marks a required prop.
| Prop | Type | Default | Description |
|---|---|---|---|
| items* | { id: string; title: string; height: "sm" | "md" | "lg"; tone?: string | undefined; }[] | — | The items to render. |
| className | string | — | Extra classes, merged with the component's own. |
import { cn } from "@/lib/utils";
const heights = { sm: "h-28", md: "h-40", lg: "h-56" } as const;
/** Column masonry of tiles with mixed heights and optional tint. */
export function Masonry({
items,
className,
}: {
items: { id: string; title: string; height: keyof typeof heights; tone?: string }[];
className?: string;
}) {
return (
<div className={cn("columns-2 gap-3 md:columns-3", className)}>
{items.map((item) => (
<div
key={item.id}
className={cn(
"group relative mb-3 flex break-inside-avoid flex-col justify-between overflow-hidden rounded-md border border-line bg-surface p-4 shadow-quiet transition-[border-color,transform,box-shadow] duration-300 ease-out hover:-translate-y-0.5 hover:border-line-strong hover:shadow-lift",
heights[item.height],
)}
>
{item.tone ? (
<span
aria-hidden
className="pointer-events-none absolute inset-0 opacity-80"
style={{
background: `linear-gradient(165deg, transparent 35%, ${item.tone})`,
}}
/>
) : null}
<p className="relative font-mono text-[11px] text-muted">{item.id}</p>
<p className="relative text-sm font-medium tracking-[-0.01em] text-ink">
{item.title}
</p>
</div>
))}
</div>
);
}
Exports Masonry. Peer dependencies: clsx, tailwind-merge.