← FeaturesOpen in v0
Features 01 — Bento Grid
Asymmetric bento grid of five feature cards with icon chips, a CSS-only product mock in the featured tile, hover glow accents and staggered reveals.
$ npx shadcn@latest add @ludusvibe/features-01Code
Installed to components/sections/features-01.tsx — it's yours to edit.
"use client"
import * as React from "react"
import { motion } from "motion/react"
import {
Blocks,
Globe,
LineChart,
ShieldCheck,
Zap,
type LucideIcon,
} from "lucide-react"
import { fadeUp, stagger, viewport } from "@/lib/motion"
import { cn } from "@/lib/utils"
export type Feature = {
icon: LucideIcon
title: string
description: string
}
export type Features01Props = {
eyebrow?: string
title?: React.ReactNode
description?: string
/**
* Five features fill the bento grid. The first one renders as the
* featured tile (spans two columns) with a visual inside.
*/
features?: Feature[]
/** Replace the CSS-only mock inside the featured tile. */
featuredVisual?: React.ReactNode
/** Heading element — use "h1" when this is the page's main heading. */
as?: "h1" | "h2"
className?: string
}
const defaultFeatures: Feature[] = [
{
icon: LineChart,
title: "Insights in real time",
description:
"Watch signups, activation and revenue move the moment they happen. Live dashboards stream every event with sub-second latency — no refresh, no nightly sync.",
},
{
icon: Zap,
title: "Instant setup",
description:
"Drop in one snippet and Lumen starts learning immediately. Autocapture means your first chart is ready before your coffee is.",
},
{
icon: ShieldCheck,
title: "Private by default",
description:
"SOC 2 Type II, GDPR-ready pipelines and field-level encryption. Your customers' data never trains anyone else's models.",
},
{
icon: Globe,
title: "Fast from anywhere",
description:
"Queries run on an edge network spanning 30+ regions, so dashboards feel local whether your team is in Berlin or Bogotá.",
},
{
icon: Blocks,
title: "Composable by design",
description:
"Every chart, cohort and funnel is an API away. Embed insights in your product or pipe them into the tools you already use.",
},
]
function FeaturedMock() {
return (
<div className="relative mt-8 -mr-6 -mb-6 overflow-hidden rounded-tl-2xl border-t border-l bg-background sm:-mr-8 sm:-mb-8">
<div className="flex items-center gap-1.5 border-b px-4 py-2.5">
<span className="size-2 rounded-full bg-muted-foreground/25" />
<span className="size-2 rounded-full bg-muted-foreground/25" />
<span className="size-2 rounded-full bg-muted-foreground/25" />
<div className="ml-3 h-4 w-32 rounded bg-muted" />
<div className="ml-auto h-4 w-16 rounded-full bg-primary/15" />
</div>
<div className="grid grid-cols-3 gap-3 p-4">
{[0, 1, 2].map((i) => (
<div key={i} className="rounded-lg border bg-card p-3">
<div className="h-2.5 w-1/2 rounded bg-muted" />
<div
className={cn(
"mt-2.5 h-4 rounded",
i === 0 ? "w-3/4 bg-primary/40" : "w-2/3 bg-muted"
)}
/>
</div>
))}
</div>
<div className="flex h-24 items-end gap-1.5 px-4">
{[35, 55, 40, 70, 50, 85, 60, 95, 75, 100, 65, 90, 80, 70].map(
(h, i) => (
<div
key={i}
style={{ height: `${h}%` }}
className={cn(
"flex-1 rounded-t-sm",
i === 9 ? "bg-primary" : "bg-primary/40"
)}
/>
)
)}
</div>
</div>
)
}
export function Features01({
eyebrow = "Features",
title = "Everything you need to see clearly",
description = "Lumen turns raw product events into answers your whole team can act on — without a data team on standby.",
features = defaultFeatures,
featuredVisual,
as: Heading = "h2",
className,
}: Features01Props) {
const [featured, ...rest] = features
return (
<section className={cn("relative bg-background px-6 py-24 md:py-32", className)}>
<div className="mx-auto max-w-6xl">
<motion.div
initial="hidden"
whileInView="visible"
viewport={viewport}
variants={stagger()}
className="max-w-2xl"
>
<motion.p
variants={fadeUp}
className="text-sm font-medium tracking-widest text-primary uppercase"
>
{eyebrow}
</motion.p>
<motion.div variants={fadeUp}>
<Heading className="mt-4 text-balance font-heading text-4xl font-semibold tracking-tight md:text-5xl">
{title}
</Heading>
</motion.div>
<motion.p
variants={fadeUp}
className="mt-5 text-lg text-pretty text-muted-foreground"
>
{description}
</motion.p>
</motion.div>
<motion.div
initial="hidden"
whileInView="visible"
viewport={viewport}
variants={stagger(0.1)}
className="mt-14 grid gap-4 md:grid-cols-2 lg:grid-cols-3"
>
{featured && (
<motion.div
variants={fadeUp}
className="group relative flex flex-col overflow-hidden rounded-2xl border bg-card p-6 transition-colors duration-300 hover:border-primary/40 sm:p-8 md:col-span-2"
>
<div
aria-hidden
className="absolute -top-16 left-1/4 h-32 w-2/3 rounded-full bg-primary/15 opacity-0 blur-3xl transition-opacity duration-500 group-hover:opacity-100"
/>
<div className="relative">
<div className="flex size-10 items-center justify-center rounded-lg bg-primary/10 text-primary ring-1 ring-primary/15">
<featured.icon className="size-5" />
</div>
<h3 className="mt-5 font-heading text-xl font-semibold tracking-tight">
{featured.title}
</h3>
<p className="mt-2 max-w-xl text-sm leading-relaxed text-muted-foreground">
{featured.description}
</p>
</div>
{featuredVisual ?? <FeaturedMock />}
</motion.div>
)}
{rest.map((feature) => (
<motion.div
key={feature.title}
variants={fadeUp}
className="group relative overflow-hidden rounded-2xl border bg-card p-6 transition-colors duration-300 hover:border-primary/40"
>
<div
aria-hidden
className="absolute -top-12 left-1/2 h-24 w-3/4 -translate-x-1/2 rounded-full bg-primary/15 opacity-0 blur-3xl transition-opacity duration-500 group-hover:opacity-100"
/>
<div className="relative">
<div className="flex size-10 items-center justify-center rounded-lg bg-primary/10 text-primary ring-1 ring-primary/15">
<feature.icon className="size-5" />
</div>
<h3 className="mt-5 font-heading text-lg font-semibold tracking-tight">
{feature.title}
</h3>
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
{feature.description}
</p>
</div>
</motion.div>
))}
</motion.div>
</div>
</section>
)
}