Interactive Bento Grid
Dark premium bento grid on black. Framer Motion stagger reveal, spring hover lift (-6px), icon scale, tag hover, and md:col-span-2 persistent highlight tiles. Pipeline Orchestrator, Unified Inbox, Contract Intelligence, Fleet Observability.
Dependencies
react
framer-motion
lucide-react
npm install framer-motion lucide-react"use client";
import { motion } from "framer-motion";
function cn(...inputs: (string | undefined | null | false | Record<string, boolean>)[]) {
return inputs
.flatMap((x) => {
if (!x) return [];
if (typeof x === "string") return [x];
return Object.entries(x).filter(([, v]) => v).map(([k]) => k);
})
.join(" ");
}
export interface BentoItem {
title: string;
description: string;
icon: React.ReactNode;
status?: string;
tags?: string[];
meta?: string;
cta?: string;
colSpan?: number;
hasPersistentHover?: boolean;
}
interface BentoGridProps {
items?: BentoItem[];
}
import {
Activity,
FileSearch,
GitBranch,
Inbox,
} from "lucide-react";
const items: BentoItem[] = [
{
title: "Pipeline Orchestrator",
meta: "v4.0.2",
description: "Visual builder with conditional branches, delays, and webhook triggers across 120+ integrated apps.",
icon: <GitBranch className="h-4 w-4 text-violet-400" />,
status: "Live",
tags: ["Automation", "Webhooks", "No-code"],
colSpan: 2,
hasPersistentHover: true,
cta: "Open builder →",
},
{
title: "Unified Inbox",
meta: "89 assigned",
description: "Route support, sales, and ops messages into one queue with SLA timers and smart auto-assignment.",
icon: <Inbox className="h-4 w-4 text-amber-400" />,
status: "Syncing",
tags: ["Support", "SLA"],
cta: "View queue →",
},
{
title: "Contract Intelligence",
meta: "214 parsed",
description: "Extract renewal dates, obligations, and risk clauses from PDFs with built-in document AI.",
icon: <FileSearch className="h-4 w-4 text-rose-400" />,
tags: ["Legal", "AI", "OCR"],
colSpan: 2,
cta: "Review docs →",
},
{
title: "Fleet Observability",
meta: "18 nodes",
description: "Monitor workflow health, error rates, and queue depth across US, EU, and APAC edge clusters.",
icon: <Activity className="h-4 w-4 text-cyan-400" />,
status: "Early access",
tags: ["DevOps", "Metrics"],
cta: "View metrics →",
},
];
function BentoGrid({ items = items }: BentoGridProps) {
return (
<div className="grid grid-cols-1 gap-3 p-4 md:grid-cols-3 mx-auto max-w-7xl">
{items.map((item, index) => (
<motion.div
key={item.title}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-40px" }}
transition={{ duration: 0.45, delay: index * 0.07, ease: [0.22, 1, 0.36, 1] }}
whileHover={{ y: -6, scale: 1.012 }}
whileTap={{ scale: 0.995 }}
className={cn(
"group relative overflow-hidden rounded-xl border border-white/10 bg-black p-4",
"shadow-[0_2px_12px_rgba(255,255,255,0.02)] transition-shadow duration-300",
"hover:border-white/20 hover:shadow-[0_8px_32px_rgba(255,255,255,0.06)]",
item.colSpan === 2 ? "md:col-span-2" : "col-span-1",
item.hasPersistentHover && "-translate-y-1.5 shadow-[0_8px_32px_rgba(255,255,255,0.06)] border-white/20"
)}
>
<motion.div
className="pointer-events-none absolute inset-0"
initial={false}
animate={{ opacity: item.hasPersistentHover ? 1 : 0 }}
whileHover={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
<div className="absolute inset-0 bg-[length:4px_4px] bg-[radial-gradient(circle_at_center,rgba(255,255,255,0.04)_1px,transparent_1px)]" />
</motion.div>
<div className="relative flex flex-col space-y-3">
<div className="flex items-center justify-between">
<motion.div
whileHover={{ scale: 1.08, rotate: 3 }}
transition={{ type: "spring", stiffness: 400, damping: 18 }}
className="flex h-8 w-8 items-center justify-center rounded-lg bg-white/10"
>
{item.icon}
</motion.div>
<span className="rounded-lg bg-white/10 px-2 py-1 text-xs font-medium text-zinc-300 backdrop-blur-sm">
{item.status || "Active"}
</span>
</div>
<div className="space-y-2">
<h3 className="text-[15px] font-medium tracking-tight text-zinc-100">
{item.title}
{item.meta ? (
<span className="ml-2 text-xs font-normal text-zinc-500">{item.meta}</span>
) : null}
</h3>
<p className="text-sm leading-snug text-zinc-400">{item.description}</p>
</div>
<div className="mt-2 flex items-center justify-between">
<div className="flex flex-wrap items-center gap-2 text-xs text-zinc-500">
{item.tags?.map((tag) => (
<motion.span
key={tag}
whileHover={{ scale: 1.05, backgroundColor: "rgba(255,255,255,0.14)" }}
className="rounded-md bg-white/10 px-2 py-1 backdrop-blur-sm"
>
#{tag}
</motion.span>
))}
</div>
<motion.span
initial={{ opacity: 0, x: -6 }}
whileHover={{ opacity: 1, x: 0 }}
className="text-xs text-zinc-400 opacity-0 transition-opacity group-hover:opacity-100"
>
{item.cta || "Explore →"}
</motion.span>
</div>
</div>
<div
className={cn(
"pointer-events-none absolute inset-0 -z-10 rounded-xl bg-gradient-to-br from-transparent via-white/10 to-transparent p-px",
item.hasPersistentHover ? "opacity-100" : "opacity-0 group-hover:opacity-100",
"transition-opacity duration-300"
)}
/>
</motion.div>
))}
</div>
);
}
export function InteractiveBentoFeatures() {
return (
<section className="bg-black px-4 py-20 sm:px-6 lg:px-8">
<div className="mx-auto max-w-7xl">
<motion.div
initial={{ opacity: 0, y: 12 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="mb-10 max-w-2xl"
>
<p className="text-sm font-medium text-zinc-500">Platform modules</p>
<h2 className="mt-2 text-2xl font-semibold tracking-tight text-zinc-50 sm:text-3xl">Everything your ops stack needs</h2>
<p className="mt-2 text-sm text-zinc-400">Composable tiles with motion hover — double-width for hero capabilities.</p>
</motion.div>
<BentoGrid />
</div>
</section>
);
}
export { BentoGrid };
Please implement this code in the current project and add it to (Your Desired Section, e.g. homepage hero, pricing page, dashboard). Change the primary accent color to (YOUR HEX CODE COLOR, e.g. #6366F1).
Requirements:
- Use React + Tailwind CSS (match the project's existing setup)
- Install dependencies if needed: npm install framer-motion lucide-react
- Keep the layout responsive on mobile and desktop
- Replace placeholder copy with content that fits the project
The UI code:
"use client";
import { motion } from "framer-motion";
function cn(...inputs: (string | undefined | null | false | Record<string, boolean>)[]) {
return inputs
.flatMap((x) => {
if (!x) return [];
if (typeof x === "string") return [x];
return Object.entries(x).filter(([, v]) => v).map(([k]) => k);
})
.join(" ");
}
export interface BentoItem {
title: string;
description: string;
icon: React.ReactNode;
status?: string;
tags?: string[];
meta?: string;
cta?: string;
colSpan?: number;
hasPersistentHover?: boolean;
}
interface BentoGridProps {
items?: BentoItem[];
}
import {
Activity,
FileSearch,
GitBranch,
Inbox,
} from "lucide-react";
const items: BentoItem[] = [
{
title: "Pipeline Orchestrator",
meta: "v4.0.2",
description: "Visual builder with conditional branches, delays, and webhook triggers across 120+ integrated apps.",
icon: <GitBranch className="h-4 w-4 text-violet-400" />,
status: "Live",
tags: ["Automation", "Webhooks", "No-code"],
colSpan: 2,
hasPersistentHover: true,
cta: "Open builder →",
},
{
title: "Unified Inbox",
meta: "89 assigned",
description: "Route support, sales, and ops messages into one queue with SLA timers and smart auto-assignment.",
icon: <Inbox className="h-4 w-4 text-amber-400" />,
status: "Syncing",
tags: ["Support", "SLA"],
cta: "View queue →",
},
{
title: "Contract Intelligence",
meta: "214 parsed",
description: "Extract renewal dates, obligations, and risk clauses from PDFs with built-in document AI.",
icon: <FileSearch className="h-4 w-4 text-rose-400" />,
tags: ["Legal", "AI", "OCR"],
colSpan: 2,
cta: "Review docs →",
},
{
title: "Fleet Observability",
meta: "18 nodes",
description: "Monitor workflow health, error rates, and queue depth across US, EU, and APAC edge clusters.",
icon: <Activity className="h-4 w-4 text-cyan-400" />,
status: "Early access",
tags: ["DevOps", "Metrics"],
cta: "View metrics →",
},
];
function BentoGrid({ items = items }: BentoGridProps) {
return (
<div className="grid grid-cols-1 gap-3 p-4 md:grid-cols-3 mx-auto max-w-7xl">
{items.map((item, index) => (
<motion.div
key={item.title}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-40px" }}
transition={{ duration: 0.45, delay: index * 0.07, ease: [0.22, 1, 0.36, 1] }}
whileHover={{ y: -6, scale: 1.012 }}
whileTap={{ scale: 0.995 }}
className={cn(
"group relative overflow-hidden rounded-xl border border-white/10 bg-black p-4",
"shadow-[0_2px_12px_rgba(255,255,255,0.02)] transition-shadow duration-300",
"hover:border-white/20 hover:shadow-[0_8px_32px_rgba(255,255,255,0.06)]",
item.colSpan === 2 ? "md:col-span-2" : "col-span-1",
item.hasPersistentHover && "-translate-y-1.5 shadow-[0_8px_32px_rgba(255,255,255,0.06)] border-white/20"
)}
>
<motion.div
className="pointer-events-none absolute inset-0"
initial={false}
animate={{ opacity: item.hasPersistentHover ? 1 : 0 }}
whileHover={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
<div className="absolute inset-0 bg-[length:4px_4px] bg-[radial-gradient(circle_at_center,rgba(255,255,255,0.04)_1px,transparent_1px)]" />
</motion.div>
<div className="relative flex flex-col space-y-3">
<div className="flex items-center justify-between">
<motion.div
whileHover={{ scale: 1.08, rotate: 3 }}
transition={{ type: "spring", stiffness: 400, damping: 18 }}
className="flex h-8 w-8 items-center justify-center rounded-lg bg-white/10"
>
{item.icon}
</motion.div>
<span className="rounded-lg bg-white/10 px-2 py-1 text-xs font-medium text-zinc-300 backdrop-blur-sm">
{item.status || "Active"}
</span>
</div>
<div className="space-y-2">
<h3 className="text-[15px] font-medium tracking-tight text-zinc-100">
{item.title}
{item.meta ? (
<span className="ml-2 text-xs font-normal text-zinc-500">{item.meta}</span>
) : null}
</h3>
<p className="text-sm leading-snug text-zinc-400">{item.description}</p>
</div>
<div className="mt-2 flex items-center justify-between">
<div className="flex flex-wrap items-center gap-2 text-xs text-zinc-500">
{item.tags?.map((tag) => (
<motion.span
key={tag}
whileHover={{ scale: 1.05, backgroundColor: "rgba(255,255,255,0.14)" }}
className="rounded-md bg-white/10 px-2 py-1 backdrop-blur-sm"
>
#{tag}
</motion.span>
))}
</div>
<motion.span
initial={{ opacity: 0, x: -6 }}
whileHover={{ opacity: 1, x: 0 }}
className="text-xs text-zinc-400 opacity-0 transition-opacity group-hover:opacity-100"
>
{item.cta || "Explore →"}
</motion.span>
</div>
</div>
<div
className={cn(
"pointer-events-none absolute inset-0 -z-10 rounded-xl bg-gradient-to-br from-transparent via-white/10 to-transparent p-px",
item.hasPersistentHover ? "opacity-100" : "opacity-0 group-hover:opacity-100",
"transition-opacity duration-300"
)}
/>
</motion.div>
))}
</div>
);
}
export function InteractiveBentoFeatures() {
return (
<section className="bg-black px-4 py-20 sm:px-6 lg:px-8">
<div className="mx-auto max-w-7xl">
<motion.div
initial={{ opacity: 0, y: 12 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5 }}
className="mb-10 max-w-2xl"
>
<p className="text-sm font-medium text-zinc-500">Platform modules</p>
<h2 className="mt-2 text-2xl font-semibold tracking-tight text-zinc-50 sm:text-3xl">Everything your ops stack needs</h2>
<p className="mt-2 text-sm text-zinc-400">Composable tiles with motion hover — double-width for hero capabilities.</p>
</motion.div>
<BentoGrid />
</div>
</section>
);
}
export { BentoGrid };
Replace (Your Desired Section) and (YOUR HEX CODE COLOR) before pasting into Cursor, Copilot, or ChatGPT.