UI Components Medium
Bento Grid
Responsive bento-style grid layout with varied cell sizes, some spanning multiple columns or rows, for modern dashboard and showcase designs.
Open in Lab
MCP
css javascript vue svelte
Targets: TS JS HTML React Vue Svelte
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, sans-serif;
min-height: 100vh;
display: grid;
place-items: center;
background: #0a0a0a;
padding: clamp(0.75rem, 3vw, 2rem);
}
/* --- Bento Grid --- */
.bento-grid {
--bento-gap: 1rem;
--bento-cols: 3;
display: grid;
grid-template-columns: repeat(var(--bento-cols), 1fr);
gap: var(--bento-gap);
width: min(900px, 100%);
}
@media (max-width: 768px) {
.bento-grid {
--bento-cols: 2;
}
}
@media (max-width: 480px) {
.bento-grid {
--bento-cols: 1;
}
.col-span-2 {
grid-column: span 1 !important;
}
.row-span-2 {
grid-row: span 1 !important;
}
}
/* --- Span utilities --- */
.col-span-2 {
grid-column: span 2;
}
.row-span-2 {
grid-row: span 2;
}
/* --- Bento Item --- */
.bento-item {
padding: clamp(1rem, 3vw, 1.5rem);
overflow: hidden;
border-radius: 1rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.08);
display: flex;
flex-direction: column;
gap: 0.75rem;
transition: border-color 0.2s ease, background 0.2s ease;
}
.bento-item:hover {
border-color: rgba(255, 255, 255, 0.15);
background: rgba(255, 255, 255, 0.05);
}
/* --- Icon --- */
.bento-icon {
width: 40px;
height: 40px;
display: grid;
place-items: center;
border-radius: 0.75rem;
font-size: 1.125rem;
flex-shrink: 0;
}
.bento-icon--cyan {
background: rgba(34, 211, 238, 0.1);
color: #22d3ee;
}
.bento-icon--purple {
background: rgba(168, 85, 247, 0.1);
color: #a855f7;
}
.bento-icon--green {
background: rgba(52, 211, 153, 0.1);
color: #34d399;
}
.bento-icon--orange {
background: rgba(245, 158, 11, 0.1);
color: #f59e0b;
}
.bento-icon--pink {
background: rgba(236, 72, 153, 0.1);
color: #ec4899;
}
.bento-icon--blue {
background: rgba(59, 130, 246, 0.1);
color: #3b82f6;
}
/* --- Typography --- */
.bento-title {
font-size: 1rem;
font-weight: 700;
color: #f1f5f9;
letter-spacing: -0.01em;
}
.bento-desc {
font-size: 0.8125rem;
line-height: 1.55;
color: #94a3b8;
}
/* --- Bar chart visual (for large item) --- */
.bento-visual {
display: flex;
align-items: flex-end;
gap: 0.5rem;
height: 80px;
margin-top: auto;
padding-top: 1rem;
}
.bento-bar {
flex: 1;
border-radius: 0.25rem 0.25rem 0 0;
background: linear-gradient(to top, rgba(34, 211, 238, 0.3), rgba(34, 211, 238, 0.1));
border: 1px solid rgba(34, 211, 238, 0.2);
border-bottom: none;
transition: height 0.3s ease;
}
.bento-item:hover .bento-bar {
background: linear-gradient(to top, rgba(34, 211, 238, 0.5), rgba(34, 211, 238, 0.15));
}
/* --- Avatars (for collab item) --- */
.bento-avatars {
display: flex;
margin-top: 0.5rem;
}
.bento-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
display: grid;
place-items: center;
font-size: 0.6875rem;
font-weight: 700;
color: #0a0a0a;
border: 2px solid #0a0a0a;
margin-left: -8px;
}
.bento-avatar:first-child {
margin-left: 0;
}
.bento-avatar--more {
background: rgba(255, 255, 255, 0.1);
color: #94a3b8;
font-weight: 600;
}
/* --- File list (for storage item) --- */
.bento-files {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-top: auto;
}
.bento-file {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
border-radius: 0.5rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
}
.bento-file__icon {
font-size: 1rem;
flex-shrink: 0;
}
.bento-file__name {
font-size: 0.8125rem;
color: #e2e8f0;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bento-file__size {
font-size: 0.75rem;
color: #64748b;
}// Bento Grid — mostly CSS-driven layout.
// This script adds staggered entrance animations for each bento item.
(function () {
"use strict";
var items = document.querySelectorAll(".bento-item");
if (!items.length) return;
// Staggered fade-in on load
items.forEach(function (item, i) {
item.style.opacity = "0";
item.style.transform = "translateY(16px)";
item.style.transition = "opacity 0.5s ease, transform 0.5s ease";
item.style.transitionDelay = i * 80 + "ms";
});
// Trigger after a frame to allow CSS transition
requestAnimationFrame(function () {
requestAnimationFrame(function () {
items.forEach(function (item) {
item.style.opacity = "1";
item.style.transform = "translateY(0)";
});
});
});
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bento Grid</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="bento-grid">
<!-- Large feature item: 2 cols, 2 rows -->
<div class="bento-item col-span-2 row-span-2">
<div class="bento-icon bento-icon--cyan">◆</div>
<h3 class="bento-title">Analytics Dashboard</h3>
<p class="bento-desc">
Real-time metrics and charts with interactive filters and drill-down capabilities.
</p>
<div class="bento-visual">
<div class="bento-bar" style="height: 60%"></div>
<div class="bento-bar" style="height: 85%"></div>
<div class="bento-bar" style="height: 45%"></div>
<div class="bento-bar" style="height: 70%"></div>
<div class="bento-bar" style="height: 90%"></div>
<div class="bento-bar" style="height: 55%"></div>
<div class="bento-bar" style="height: 75%"></div>
</div>
</div>
<!-- Standard item -->
<div class="bento-item">
<div class="bento-icon bento-icon--purple">★</div>
<h3 class="bento-title">Authentication</h3>
<p class="bento-desc">Secure login with OAuth, 2FA, and session management.</p>
</div>
<!-- Standard item -->
<div class="bento-item">
<div class="bento-icon bento-icon--green">♦</div>
<h3 class="bento-title">API Gateway</h3>
<p class="bento-desc">Rate limiting, caching, and request routing.</p>
</div>
<!-- Wide item: 2 cols -->
<div class="bento-item col-span-2">
<div class="bento-icon bento-icon--orange">✿</div>
<h3 class="bento-title">Team Collaboration</h3>
<p class="bento-desc">
Real-time editing, comments, and shared workspaces for your entire team.
</p>
<div class="bento-avatars">
<div class="bento-avatar" style="background: #22d3ee;">SC</div>
<div class="bento-avatar" style="background: #a855f7;">JD</div>
<div class="bento-avatar" style="background: #34d399;">MK</div>
<div class="bento-avatar" style="background: #f59e0b;">AL</div>
<div class="bento-avatar bento-avatar--more">+5</div>
</div>
</div>
<!-- Standard item -->
<div class="bento-item">
<div class="bento-icon bento-icon--pink">♥</div>
<h3 class="bento-title">Notifications</h3>
<p class="bento-desc">Push, email, and in-app alerts.</p>
</div>
<!-- Tall item: 2 rows -->
<div class="bento-item row-span-2">
<div class="bento-icon bento-icon--blue">●</div>
<h3 class="bento-title">File Storage</h3>
<p class="bento-desc">Cloud storage with versioning, preview, and sharing.</p>
<div class="bento-files">
<div class="bento-file">
<span class="bento-file__icon">📄</span>
<span class="bento-file__name">report.pdf</span>
<span class="bento-file__size">2.4 MB</span>
</div>
<div class="bento-file">
<span class="bento-file__icon">📸</span>
<span class="bento-file__name">photo.png</span>
<span class="bento-file__size">1.1 MB</span>
</div>
<div class="bento-file">
<span class="bento-file__icon">🎵</span>
<span class="bento-file__name">audio.mp3</span>
<span class="bento-file__size">5.8 MB</span>
</div>
</div>
</div>
<!-- Standard item -->
<div class="bento-item">
<div class="bento-icon bento-icon--cyan">⚡</div>
<h3 class="bento-title">Edge Functions</h3>
<p class="bento-desc">Deploy serverless logic at the edge.</p>
</div>
<!-- Standard item -->
<div class="bento-item">
<div class="bento-icon bento-icon--purple">☃</div>
<h3 class="bento-title">CDN</h3>
<p class="bento-desc">Global content delivery with smart caching.</p>
</div>
</div>
<script src="script.js"></script>
</body>
</html>import { type CSSProperties, type ReactNode, useEffect, useRef, useState } from "react";
interface BentoGridProps {
children: ReactNode;
columns?: number;
gap?: string;
className?: string;
}
export function BentoGrid({ children, columns = 3, gap = "1rem", className = "" }: BentoGridProps) {
const gridStyle: CSSProperties = {
display: "grid",
gridTemplateColumns: `repeat(${columns}, 1fr)`,
gap,
width: "100%",
};
return (
<div style={gridStyle} className={className}>
{children}
</div>
);
}
interface BentoGridItemProps {
children: ReactNode;
colSpan?: number;
rowSpan?: number;
className?: string;
index?: number;
}
export function BentoGridItem({
children,
colSpan = 1,
rowSpan = 1,
className = "",
index = 0,
}: BentoGridItemProps) {
const ref = useRef<HTMLDivElement>(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const timer = setTimeout(() => setVisible(true), index * 80);
return () => clearTimeout(timer);
}, [index]);
const itemStyle: CSSProperties = {
gridColumn: `span ${colSpan}`,
gridRow: `span ${rowSpan}`,
padding: "1.5rem",
borderRadius: "1rem",
background: "rgba(255,255,255,0.03)",
border: "1px solid rgba(255,255,255,0.08)",
display: "flex",
flexDirection: "column",
gap: "0.75rem",
transition:
"border-color 0.2s ease, background 0.2s ease, opacity 0.5s ease, transform 0.5s ease",
opacity: visible ? 1 : 0,
transform: visible ? "translateY(0)" : "translateY(16px)",
};
return (
<div
ref={ref}
style={itemStyle}
className={className}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = "rgba(255,255,255,0.15)";
e.currentTarget.style.background = "rgba(255,255,255,0.05)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = "rgba(255,255,255,0.08)";
e.currentTarget.style.background = "rgba(255,255,255,0.03)";
}}
>
{children}
</div>
);
}
// Demo icon box
function Icon({ children, color }: { children: ReactNode; color: string }) {
return (
<div
style={{
width: 40,
height: 40,
display: "grid",
placeItems: "center",
borderRadius: "0.75rem",
fontSize: "1.125rem",
background: `${color}1a`,
color,
flexShrink: 0,
}}
>
{children}
</div>
);
}
// Demo usage
export default function BentoGridDemo() {
return (
<div
style={{
minHeight: "100vh",
display: "grid",
placeItems: "center",
background: "#0a0a0a",
fontFamily: "system-ui, -apple-system, sans-serif",
padding: "2rem",
}}
>
<BentoGrid columns={3} gap="1rem">
{/* Large feature item */}
<BentoGridItem colSpan={2} rowSpan={2} index={0}>
<Icon color="#22d3ee">{"\u2666"}</Icon>
<h3 style={{ fontSize: "1rem", fontWeight: 700, color: "#f1f5f9", margin: 0 }}>
Analytics Dashboard
</h3>
<p style={{ fontSize: "0.8125rem", lineHeight: 1.55, color: "#94a3b8", margin: 0 }}>
Real-time metrics and charts with interactive filters and drill-down capabilities.
</p>
<div
style={{
display: "flex",
alignItems: "flex-end",
gap: "0.5rem",
height: 80,
marginTop: "auto",
paddingTop: "1rem",
}}
>
{[60, 85, 45, 70, 90, 55, 75].map((h, i) => (
<div
key={i}
style={{
flex: 1,
height: `${h}%`,
borderRadius: "0.25rem 0.25rem 0 0",
background: "linear-gradient(to top, rgba(34,211,238,0.3), rgba(34,211,238,0.1))",
border: "1px solid rgba(34,211,238,0.2)",
borderBottom: "none",
}}
/>
))}
</div>
</BentoGridItem>
<BentoGridItem index={1}>
<Icon color="#a855f7">{"\u2733"}</Icon>
<h3 style={{ fontSize: "1rem", fontWeight: 700, color: "#f1f5f9", margin: 0 }}>
Authentication
</h3>
<p style={{ fontSize: "0.8125rem", lineHeight: 1.55, color: "#94a3b8", margin: 0 }}>
Secure login with OAuth, 2FA, and session management.
</p>
</BentoGridItem>
<BentoGridItem index={2}>
<Icon color="#34d399">{"\u2726"}</Icon>
<h3 style={{ fontSize: "1rem", fontWeight: 700, color: "#f1f5f9", margin: 0 }}>
API Gateway
</h3>
<p style={{ fontSize: "0.8125rem", lineHeight: 1.55, color: "#94a3b8", margin: 0 }}>
Rate limiting, caching, and request routing.
</p>
</BentoGridItem>
{/* Wide item */}
<BentoGridItem colSpan={2} index={3}>
<Icon color="#f59e0b">{"\u2747"}</Icon>
<h3 style={{ fontSize: "1rem", fontWeight: 700, color: "#f1f5f9", margin: 0 }}>
Team Collaboration
</h3>
<p style={{ fontSize: "0.8125rem", lineHeight: 1.55, color: "#94a3b8", margin: 0 }}>
Real-time editing, comments, and shared workspaces for your entire team.
</p>
<div style={{ display: "flex", marginTop: "0.5rem" }}>
{[
{ initials: "SC", bg: "#22d3ee" },
{ initials: "JD", bg: "#a855f7" },
{ initials: "MK", bg: "#34d399" },
{ initials: "AL", bg: "#f59e0b" },
].map((a, i) => (
<div
key={a.initials}
style={{
width: 32,
height: 32,
borderRadius: "50%",
display: "grid",
placeItems: "center",
fontSize: "0.6875rem",
fontWeight: 700,
color: "#0a0a0a",
background: a.bg,
border: "2px solid #0a0a0a",
marginLeft: i === 0 ? 0 : -8,
}}
>
{a.initials}
</div>
))}
<div
style={{
width: 32,
height: 32,
borderRadius: "50%",
display: "grid",
placeItems: "center",
fontSize: "0.6875rem",
fontWeight: 600,
color: "#94a3b8",
background: "rgba(255,255,255,0.1)",
border: "2px solid #0a0a0a",
marginLeft: -8,
}}
>
+5
</div>
</div>
</BentoGridItem>
<BentoGridItem index={4}>
<Icon color="#ec4899">{"\u2665"}</Icon>
<h3 style={{ fontSize: "1rem", fontWeight: 700, color: "#f1f5f9", margin: 0 }}>
Notifications
</h3>
<p style={{ fontSize: "0.8125rem", lineHeight: 1.55, color: "#94a3b8", margin: 0 }}>
Push, email, and in-app alerts.
</p>
</BentoGridItem>
</BentoGrid>
</div>
);
}<script setup>
import { onMounted } from "vue";
const items = [
{
colSpan: 2,
rowSpan: 2,
icon: "♦",
iconColor: "#22d3ee",
title: "Analytics Dashboard",
desc: "Real-time metrics and charts with interactive filters and drill-down capabilities.",
hasChart: true,
},
{
colSpan: 1,
rowSpan: 1,
icon: "✳",
iconColor: "#a855f7",
title: "Authentication",
desc: "Secure login with OAuth, 2FA, and session management.",
},
{
colSpan: 1,
rowSpan: 1,
icon: "✦",
iconColor: "#34d399",
title: "API Gateway",
desc: "Rate limiting, caching, and request routing.",
},
{
colSpan: 2,
rowSpan: 1,
icon: "❇",
iconColor: "#f59e0b",
title: "Team Collaboration",
desc: "Real-time editing, comments, and shared workspaces.",
hasAvatars: true,
},
{
colSpan: 1,
rowSpan: 1,
icon: "♥",
iconColor: "#ec4899",
title: "Notifications",
desc: "Push, email, and in-app alerts.",
},
];
const chartBars = [60, 85, 45, 70, 90, 55, 75];
const avatars = [
{ initials: "SC", bg: "#22d3ee" },
{ initials: "JD", bg: "#a855f7" },
{ initials: "MK", bg: "#34d399" },
{ initials: "AL", bg: "#f59e0b" },
];
const vReveal = {
mounted(el, binding) {
const idx = binding.value ?? 0;
el.style.opacity = "0";
el.style.transform = "translateY(16px)";
el.style.transition =
"opacity 0.5s ease, transform 0.5s ease, border-color 0.2s ease, background 0.2s ease";
setTimeout(() => {
el.style.opacity = "1";
el.style.transform = "translateY(0)";
}, idx * 80);
},
};
</script>
<template>
<div style="min-height:100vh;display:grid;place-items:center;background:#0a0a0a;font-family:system-ui,-apple-system,sans-serif;padding:2rem">
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;width:100%;max-width:900px">
<div v-for="(item, idx) in items" :key="item.title" v-reveal="idx" :style="{ gridColumn:`span ${item.colSpan}`, gridRow:`span ${item.rowSpan}`, padding:'1.5rem', borderRadius:'1rem', background:'rgba(255,255,255,0.03)', border:'1px solid rgba(255,255,255,0.08)', display:'flex', flexDirection:'column', gap:'0.75rem' }"
@mouseenter="e => { e.currentTarget.style.borderColor='rgba(255,255,255,0.15)'; e.currentTarget.style.background='rgba(255,255,255,0.05)' }"
@mouseleave="e => { e.currentTarget.style.borderColor='rgba(255,255,255,0.08)'; e.currentTarget.style.background='rgba(255,255,255,0.03)' }"
>
<div :style="{ width:'40px',height:'40px',display:'grid',placeItems:'center',borderRadius:'0.75rem',fontSize:'1.125rem',background:item.iconColor+'1a',color:item.iconColor }">{{ item.icon }}</div>
<h3 style="font-size:1rem;font-weight:700;color:#f1f5f9;margin:0">{{ item.title }}</h3>
<p style="font-size:0.8125rem;line-height:1.55;color:#94a3b8;margin:0">{{ item.desc }}</p>
<div v-if="item.hasChart" style="display:flex;align-items:flex-end;gap:0.5rem;height:80px;margin-top:auto;padding-top:1rem">
<div v-for="h in chartBars" :key="h" :style="{ flex:1, height:h+'%', borderRadius:'0.25rem 0.25rem 0 0', background:'linear-gradient(to top,rgba(34,211,238,0.3),rgba(34,211,238,0.1))', border:'1px solid rgba(34,211,238,0.2)', borderBottom:'none' }"></div>
</div>
<div v-if="item.hasAvatars" style="display:flex;margin-top:0.5rem">
<div v-for="(a, i) in avatars" :key="a.initials" :style="{ width:'32px',height:'32px',borderRadius:'50%',display:'grid',placeItems:'center',fontSize:'0.6875rem',fontWeight:700,color:'#0a0a0a',background:a.bg,border:'2px solid #0a0a0a',marginLeft:i===0?'0':'-8px' }">{{ a.initials }}</div>
<div style="width:32px;height:32px;border-radius:50%;display:grid;place-items:center;font-size:0.6875rem;font-weight:600;color:#94a3b8;background:rgba(255,255,255,0.1);border:2px solid #0a0a0a;margin-left:-8px">+5</div>
</div>
</div>
</div>
</div>
</template><script>
import { onMount } from "svelte";
let visibleItems = new Set();
function reveal(node, { index = 0 }) {
const timer = setTimeout(() => {
node.style.opacity = "1";
node.style.transform = "translateY(0)";
}, index * 80);
return {
destroy() {
clearTimeout(timer);
},
};
}
const items = [
{
colSpan: 2,
rowSpan: 2,
icon: "♦",
iconColor: "#22d3ee",
title: "Analytics Dashboard",
desc: "Real-time metrics and charts with interactive filters and drill-down capabilities.",
hasChart: true,
},
{
colSpan: 1,
rowSpan: 1,
icon: "✳",
iconColor: "#a855f7",
title: "Authentication",
desc: "Secure login with OAuth, 2FA, and session management.",
},
{
colSpan: 1,
rowSpan: 1,
icon: "✦",
iconColor: "#34d399",
title: "API Gateway",
desc: "Rate limiting, caching, and request routing.",
},
{
colSpan: 2,
rowSpan: 1,
icon: "❇",
iconColor: "#f59e0b",
title: "Team Collaboration",
desc: "Real-time editing, comments, and shared workspaces for your entire team.",
hasAvatars: true,
},
{
colSpan: 1,
rowSpan: 1,
icon: "♥",
iconColor: "#ec4899",
title: "Notifications",
desc: "Push, email, and in-app alerts.",
},
];
const chartBars = [60, 85, 45, 70, 90, 55, 75];
const avatars = [
{ initials: "SC", bg: "#22d3ee" },
{ initials: "JD", bg: "#a855f7" },
{ initials: "MK", bg: "#34d399" },
{ initials: "AL", bg: "#f59e0b" },
];
</script>
<div style="min-height:100vh;display:grid;place-items:center;background:#0a0a0a;font-family:system-ui,-apple-system,sans-serif;padding:2rem">
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;width:100%;max-width:900px">
{#each items as item, idx}
<div
use:reveal={{ index: idx }}
style="grid-column:span {item.colSpan};grid-row:span {item.rowSpan};padding:1.5rem;border-radius:1rem;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.08);display:flex;flex-direction:column;gap:0.75rem;opacity:0;transform:translateY(16px);transition:opacity 0.5s ease, transform 0.5s ease, border-color 0.2s ease, background 0.2s ease"
on:mouseenter={e => { e.currentTarget.style.borderColor='rgba(255,255,255,0.15)'; e.currentTarget.style.background='rgba(255,255,255,0.05)'; }}
on:mouseleave={e => { e.currentTarget.style.borderColor='rgba(255,255,255,0.08)'; e.currentTarget.style.background='rgba(255,255,255,0.03)'; }}
>
<div style="width:40px;height:40px;display:grid;place-items:center;border-radius:0.75rem;font-size:1.125rem;background:{item.iconColor}1a;color:{item.iconColor}">{item.icon}</div>
<h3 style="font-size:1rem;font-weight:700;color:#f1f5f9;margin:0">{item.title}</h3>
<p style="font-size:0.8125rem;line-height:1.55;color:#94a3b8;margin:0">{item.desc}</p>
{#if item.hasChart}
<div style="display:flex;align-items:flex-end;gap:0.5rem;height:80px;margin-top:auto;padding-top:1rem">
{#each chartBars as h}
<div style="flex:1;height:{h}%;border-radius:0.25rem 0.25rem 0 0;background:linear-gradient(to top,rgba(34,211,238,0.3),rgba(34,211,238,0.1));border:1px solid rgba(34,211,238,0.2);border-bottom:none"></div>
{/each}
</div>
{/if}
{#if item.hasAvatars}
<div style="display:flex;margin-top:0.5rem">
{#each avatars as a, i}
<div style="width:32px;height:32px;border-radius:50%;display:grid;place-items:center;font-size:0.6875rem;font-weight:700;color:#0a0a0a;background:{a.bg};border:2px solid #0a0a0a;margin-left:{i === 0 ? '0' : '-8px'}">{a.initials}</div>
{/each}
<div style="width:32px;height:32px;border-radius:50%;display:grid;place-items:center;font-size:0.6875rem;font-weight:600;color:#94a3b8;background:rgba(255,255,255,0.1);border:2px solid #0a0a0a;margin-left:-8px">+5</div>
</div>
{/if}
</div>
{/each}
</div>
</div>Bento Grid
A responsive bento-style grid layout inspired by Apple’s design language. Cells can span multiple columns or rows to create visual hierarchy, perfect for dashboards and feature showcases.
How it works
- CSS Grid with
grid-template-columns: repeat(3, 1fr)creates the base layout - Items use
grid-column: span 2orgrid-row: span 2for varied sizes - Responsive breakpoints collapse to fewer columns on smaller screens
- Each item has a consistent card style with dark theme
Customization
- Add
col-span-2orrow-span-2classes to items for larger cells - Adjust
--bento-gapfor spacing between cells - Grid columns change at responsive breakpoints (3 cols -> 2 -> 1)
When to use it
- Feature showcase sections
- Dashboard widgets layout
- Portfolio or project grids