Bento Grid
Asymmetric grid layout for showcasing content in varied tile sizes.
Code
components/BentoGrid.astro
---import type { ImageMetadata } from "astro"; interface BentoItem { title: string; description?: string; size?: "sm" | "md" | "lg" | "featured"; imageSrc?: ImageMetadata | string; imageOverlay?: boolean; class?: string;} interface Props { items: BentoItem[]; class?: string;} const { items, class: className = "",} = Astro.props; const sizeClasses = { sm: "md:col-span-1 md:row-span-1", md: "md:col-span-1 md:row-span-2", lg: "md:col-span-2 md:row-span-1", featured: "md:col-span-2 md:row-span-2",};--- <div class:list={["grid grid-cols-1 md:grid-cols-4 gap-3 md:gap-4 auto-rows-auto md:auto-rows-fr", className]}> {items.map((item) => { const bgUrl = item.imageSrc ? typeof item.imageSrc === "string" ? item.imageSrc : item.imageSrc.src : null; const showOverlay = item.imageSrc && item.imageOverlay !== false; return ( <div class:list={[ "rounded-xl p-5 md:p-6 flex flex-col justify-center bg-cover bg-center relative", sizeClasses[item.size || "sm"], item.imageSrc ? "text-white-ghost" : item.class || "bg-black-onyx/5 text-black-onyx", ]} style={bgUrl ? { backgroundImage: `url(${bgUrl})` } : undefined} > {showOverlay && ( <div class="absolute inset-0 bg-black-onyx/50 rounded-xl" /> )} <div class="relative z-10"> <h3 class="text-sm md:text-base font-semibold">{item.title}</h3> {item.description && ( <p class="text-xs md:text-sm mt-1 leading-relaxed opacity-80"> {item.description} </p> )} </div> </div> ); })}</div>
Preview
Default Bento Grid
Lightweight
Only 15KB minified. No bloat, no unnecessary dependencies.
Copy-Paste
No npm install. Copy the code and paste it directly.
Customizable
CSS variables for every component.
Responsive
Mobile-first design. Works on every screen size.
With Background Image
Desktop Setup
Clean workspace for focused development.
Minimal
Less is more.
Clean
Organized and tidy.
Modern
Contemporary design.
Productive
Built for efficiency.
Usage
<BentoGrid items={[ { title: "Lightweight", description: "Only 15KB minified. No bloat, no unnecessary dependencies.", size: "featured", class: "bg-blue-300 text-white-ghost" }, { title: "Copy-Paste", description: "No npm install. Copy the code and paste it directly.", size: "sm" }, { title: "Customizable", description: "CSS variables for every component.", size: "sm" }, { title: "Responsive", description: "Mobile-first design. Works on every screen size.", size: "lg", class: "bg-black-onyx/5" }, ]}/>
<BentoGrid items={[ { title: "Desktop Setup", description: "Clean workspace for focused development.", size: "featured", imageSrc: Desktop }, { title: "Minimal", description: "Less is more.", size: "sm", class: "bg-blue-bell/10 text-black-onyx" }, { title: "Clean", description: "Organized and tidy.", size: "sm", class: "bg-green-300 text-white-ghost" }, { title: "Modern", description: "Contemporary design.", size: "sm", class: "bg-blue-bell/10 text-black-onyx" }, { title: "Productive", description: "Built for efficiency.", size: "sm", class: "bg-black-onyx/5" }, ]}/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| items | BentoItem[] | — | Array of grid items with title, description, size, imageSrc, and optional class. |
| class | string | — | Additional CSS classes. |
Item Fields
| Field | Type | Default | Description |
|---|---|---|---|
| title | string | — | Card title. |
| description | string | — | Optional description text. |
| size | "sm" | "md" | "lg" | "featured" | "sm" | Grid span size. |
| imageSrc | ImageMetadata | string | — | Background image (imported or URL). |
| imageOverlay | boolean | true | Dark overlay for text readability. |
| class | string | — | Custom background/text color (ignored when imageSrc is set). |
Item Sizes
| Size | Mobile | Desktop |
|---|---|---|
| sm | 1 col | 1 col × 1 row |
| md | 1 col | 1 col × 2 rows |
| lg | 1 col | 2 cols × 1 row |
| featured | 1 col | 2 cols × 2 rows |