Bento Grid

Asymmetric bento grid layout with large, medium, and small cards. Mix of gradient and card-bg styles. One 2-wide card, one 2-tall card, rest are 1x1.

Preview

Source

tsx
"use client";

import { motion } from "framer-motion";
import { TrendingUp, Users, Zap, Globe, Lock, Layers } from "lucide-react";

const containerVariants = {
  hidden: {},
  visible: { transition: { staggerChildren: 0.08 } },
};

const itemVariants = {
  hidden: { opacity: 0, y: 20, scale: 0.97 },
  visible: { opacity: 1, y: 0, scale: 1, transition: { duration: 0.5, ease: "easeOut" } },
};

export default function BentoGrid() {
  return (
    <section className="py-20 sm:py-28 bg-background">
      <div className="container mx-auto px-6">
        {/* Header */}
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true }}
          transition={{ duration: 0.6 }}
          className="text-center max-w-2xl mx-auto mb-14"
        >
          <span className="text-sm font-semibold uppercase tracking-widest text-primary">
            Everything included
          </span>
          <h2 className="mt-3 text-3xl sm:text-4xl lg:text-5xl font-extrabold tracking-tight text-foreground">
            One platform.
            <br />
            Infinite possibilities.
          </h2>
        </motion.div>

        {/* Bento grid */}
        <motion.div
          variants={containerVariants}
          initial="hidden"
          whileInView="visible"
          viewport={{ once: true, margin: "-60px" }}
          className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 auto-rows-[200px]"
        >
          {/* Card 1: 2-wide, 1-tall — Hero metric */}
          <motion.div
            variants={itemVariants}
            className="sm:col-span-2 rounded-2xl bg-gradient-to-br from-primary to-violet-600 p-6 flex flex-col justify-between overflow-hidden relative group"
          >
            <div className="absolute inset-0 bg-gradient-to-br from-white/10 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
            <div className="flex items-center gap-3">
              <div className="bg-white/20 rounded-xl p-2.5">
                <TrendingUp className="w-5 h-5 text-white" />
              </div>
              <span className="text-white/80 text-sm font-medium">Growth Analytics</span>
            </div>
            <div>
              <p className="text-5xl font-extrabold text-white leading-none">+247%</p>
              <p className="text-white/70 text-sm mt-1">avg. revenue growth in year one</p>
            </div>
          </motion.div>

          {/* Card 2: 1x1 */}
          <motion.div
            variants={itemVariants}
            className="rounded-2xl bg-gradient-to-br from-emerald-400 to-teal-600 p-6 flex flex-col justify-between"
          >
            <div className="bg-white/20 rounded-xl p-2.5 w-fit">
              <Users className="w-5 h-5 text-white" />
            </div>
            <div>
              <p className="text-3xl font-extrabold text-white">12k+</p>
              <p className="text-white/70 text-sm">Active teams</p>
            </div>
          </motion.div>

          {/* Card 3: 1x1 */}
          <motion.div
            variants={itemVariants}
            className="rounded-2xl bg-card border border-border p-6 flex flex-col justify-between"
          >
            <div className="bg-amber-500/10 rounded-xl p-2.5 w-fit">
              <Zap className="w-5 h-5 text-amber-500" />
            </div>
            <div>
              <p className="text-2xl font-bold text-foreground">Sub-50ms</p>
              <p className="text-muted-foreground text-sm mt-1">Global API latency</p>
            </div>
          </motion.div>

          {/* Card 4: 1x1 */}
          <motion.div
            variants={itemVariants}
            className="rounded-2xl bg-gradient-to-br from-rose-400 to-pink-600 p-6 flex flex-col justify-between"
          >
            <div className="bg-white/20 rounded-xl p-2.5 w-fit">
              <Lock className="w-5 h-5 text-white" />
            </div>
            <div>
              <p className="text-xl font-bold text-white">SOC 2 Type II</p>
              <p className="text-white/70 text-sm mt-1">Enterprise-grade security</p>
            </div>
          </motion.div>

          {/* Card 5: 1x2-tall — tall card */}
          <motion.div
            variants={itemVariants}
            className="row-span-2 rounded-2xl bg-gradient-to-b from-indigo-500 to-violet-700 p-6 flex flex-col justify-between"
          >
            <div className="bg-white/20 rounded-xl p-2.5 w-fit">
              <Globe className="w-5 h-5 text-white" />
            </div>
            <div className="flex-1 flex items-center justify-center">
              <div className="relative w-32 h-32">
                <div className="absolute inset-0 rounded-full border-2 border-white/20" />
                <div className="absolute inset-4 rounded-full border border-white/15" />
                <div className="absolute inset-8 rounded-full border border-white/10" />
                <div className="absolute inset-0 flex items-center justify-center">
                  <Globe className="w-10 h-10 text-white/80" />
                </div>
              </div>
            </div>
            <div>
              <p className="text-2xl font-extrabold text-white">50+ regions</p>
              <p className="text-white/70 text-sm mt-1">Global edge network</p>
            </div>
          </motion.div>

          {/* Card 6: 2-wide, 1-tall */}
          <motion.div
            variants={itemVariants}
            className="sm:col-span-2 lg:col-span-2 rounded-2xl bg-card border border-border p-6 flex flex-col justify-between"
          >
            <div className="flex items-center gap-3">
              <div className="bg-blue-500/10 rounded-xl p-2.5">
                <Layers className="w-5 h-5 text-blue-500" />
              </div>
              <span className="text-foreground font-semibold text-sm">Integrations</span>
            </div>
            <div>
              <div className="flex flex-wrap gap-2 mt-3">
                {["Slack", "Salesforce", "HubSpot", "Stripe", "Notion", "Figma", "Linear", "+ 193 more"].map((name) => (
                  <span
                    key={name}
                    className="text-xs font-medium bg-muted text-muted-foreground rounded-full px-3 py-1"
                  >
                    {name}
                  </span>
                ))}
              </div>
            </div>
          </motion.div>

          {/* Card 7: 1x1 */}
          <motion.div
            variants={itemVariants}
            className="rounded-2xl bg-gradient-to-br from-cyan-400 to-sky-600 p-6 flex flex-col justify-between"
          >
            <div className="bg-white/20 rounded-xl p-2.5 w-fit">
              <TrendingUp className="w-5 h-5 text-white" />
            </div>
            <div>
              <p className="text-2xl font-extrabold text-white">99.99%</p>
              <p className="text-white/70 text-sm">Uptime SLA</p>
            </div>
          </motion.div>
        </motion.div>
      </div>
    </section>
  );
}
Claude Code Instructions

CLI Install

npx innovations add bento

Where to use it

Use as a visually striking features or stats section on landing pages. In Astro (src/pages/index.astro): import BentoGrid from '../components/innovations/content/bento'; <BentoGrid client:visible /> In Next.js (app/page.tsx): import BentoGrid from '@/components/innovations/content/bento'; The grid uses CSS grid with auto-rows-[200px]. Each card's span is controlled by col-span-* and row-span-* classes. To rearrange cards, swap these classes. To add a new card, duplicate an existing one and adjust the span. Gradient backgrounds use Tailwind from-/to- classes — swap colors to match your brand.