shadimage
7ovr
blockstartupsource: ok · hit

Startup

Lean startup landing page template for early-stage products and waitlists: centered hero section with inline email capture, stats, feature grid, how it works timeline, testimonial, and waitlist signup.

npx shadcn add "https://7ovr.com/r/startup.json"
free

Where it goes

herotestimonialstatssignupfeature-grid

Interaction

animated-counter

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
ui-primitives@base-ui/react

Registry dependencies

avatarbadgebuttoncardcheckboxinputlabelseparatorsheetsonnerutils

Files10 · source cached 10/14/2026, 4:31:15 PM

registry/templates/startup/install-page.tsx
import { Toaster } from "@/components/ui/sonner"
import Features from "@/components/startup/features"
import Footer from "@/components/startup/footer"
import Header from "@/components/startup/header"
import Hero from "@/components/startup/hero"
import HowItWorks from "@/components/startup/how-it-works"
import Newsletter from "@/components/startup/newsletter"
import Stats from "@/components/startup/stats"
import Testimonials from "@/components/startup/testimonials"

const pool = (size: string, color: string, mid: string, out: string) =>
  `radial-gradient(${size}, ${color}, ${mid} 38%, ${out} 78%)`

const wash = (base: string, a: number, b: number) => {
  const at = (v: number) => `rgba(${base},${v})`
  return [
    pool("38% 13% at 84% 9%", at(a), at(a / 2), at(0)),
    pool("42% 13% at 12% 28%", at(b), at(b / 2), at(0)),
    pool("40% 12% at 70% 47%", at(a), at(a / 2), at(0)),
    pool("42% 12% at 14% 66%", at(b), at(b / 2), at(0)),
    pool("40% 13% at 74% 87%", at(a), at(a / 2), at(0)),
  ].join(",")
}

const WASH_LIGHT = wash("0,0,0", 0.04, 0.034)
const WASH_DARK = wash("255,255,255", 0.06, 0.051)

export default function StartupTemplate() {
  return (
    <div className="relative isolate flex min-h-svh w-full flex-col bg-background text-foreground">
      <div
        aria-hidden="true"
        className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[720px] bg-[radial-gradient(65%_60%_at_50%_-8%,rgba(0,0,0,0.06),transparent_72%)] dark:bg-[radial-gradient(65%_60%_at_50%_-8%,rgba(255,255,255,0.13),transparent_72%)]"
      />
      <div
        aria-hidden="true"
        className="pointer-events-none absolute inset-0 -z-10 dark:hidden"
        style={{ backgroundImage: WASH_LIGHT }}
      />
      <div
        aria-hidden="true"
        className="pointer-events-none absolute inset-0 -z-10 hidden dark:block"
        style={{ backgroundImage: WASH_DARK }}
      />
      <div
        aria-hidden="true"
        className="pointer-events-none absolute inset-x-0 bottom-0 -z-10 h-[560px] bg-[radial-gradient(55%_50%_at_50%_100%,rgba(0,0,0,0.05),transparent_70%)] dark:bg-[radial-gradient(55%_50%_at_50%_100%,rgba(255,255,255,0.08),transparent_70%)]"
      />
      <Toaster />
      <Header />
      <main className="flex w-full flex-col">
        <Hero />
        <Stats />
        <Features />
        <HowItWorks />
        <Testimonials />
        <Newsletter />
      </main>
      <div className="border-t border-border/50">
        <Footer />
      </div>
    </div>
  )
}
registry/templates/startup/install-route.tsx
export { default } from "@/components/startup/page"
registry/templates/startup/components/features.tsx
import {
  Card,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"

type IconProps = { className?: string; size?: number | string }

const features = [
  {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="Flashlight"
        tabler="IconBulb"
        hugeicons="FlashlightIcon"
        phosphor="Flashlight"
        remixicon="RiFlashlightLine"
        {...p}
      />
    ),
    title: "Lightning fast",
    copy: "Ship in milliseconds with an edge-first runtime tuned for speed.",
  },
  {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="ShieldCheck"
        tabler="IconShieldCheck"
        hugeicons="Shield01Icon"
        phosphor="ShieldCheck"
        remixicon="RiShieldCheckLine"
        {...p}
      />
    ),
    title: "Secure by default",
    copy: "Encryption, audit logs, and granular access baked into every layer.",
  },
  {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="LayoutGrid"
        tabler="IconLayoutGrid"
        hugeicons="LayoutGridIcon"
        phosphor="GridFour"
        remixicon="RiLayoutGridLine"
        {...p}
      />
    ),
    title: "Composable blocks",
    copy: "Drop in sharp, accessible components and compose them your way.",
  },
  {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="ChartBar"
        tabler="IconChartBar"
        hugeicons="BarChartIcon"
        phosphor="ChartBar"
        remixicon="RiBarChartBoxLine"
        {...p}
      />
    ),
    title: "Real-time insights",
    copy: "Track usage and performance the moment events happen.",
  },
  {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="LineChart"
        tabler="IconChartLine"
        hugeicons="ChartLineIcon"
        phosphor="ChartLine"
        remixicon="RiLineChartLine"
        {...p}
      />
    ),
    title: "Scales with you",
    copy: "From first user to millions without re-architecting a thing.",
  },
  {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="Settings"
        tabler="IconSettings"
        hugeicons="SettingsIcon"
        phosphor="GearSix"
        remixicon="RiSettings3Line"
        {...p}
      />
    ),
    title: "Fully configurable",
    copy: "Tune defaults, theming, and workflows to fit your team.",
  },
]

export default function Features() {
  return (
    <section className="flex w-full items-center justify-center px-6 py-16 sm:py-24">
      <div className="mx-auto w-full max-w-5xl">
        <div className="mx-auto max-w-2xl text-center">
          <Badge variant="secondary" className="mb-4 tracking-widest uppercase">
            Features
          </Badge>
          <h2 className="font-heading text-3xl font-bold tracking-tight text-balance sm:text-4xl">
            Everything you need to build
          </h2>
          <p className="mt-4 text-muted-foreground">
            A focused toolkit that gets out of your way so you can move fast.
          </p>
        </div>

        <div className="mt-12 grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3">
          {features.map(({ icon: Icon, title, copy }) => (
            <Card key={title} className="p-6">
              <CardHeader className="p-0">
                <span className="flex size-11 items-center justify-center rounded-lg border border-border bg-muted">
                  <Icon className="size-5" aria-hidden="true" />
                </span>
                <CardTitle className="mt-4 text-base font-semibold">
                  {title}
                </CardTitle>
                <CardDescription className="mt-2 text-sm">
                  {copy}
                </CardDescription>
              </CardHeader>
            </Card>
          ))}
        </div>
      </div>
    </section>
  )
}
registry/templates/startup/components/footer.tsx
const NAV_LINKS = [
  { label: "Product", href: "#" },
  { label: "Pricing", href: "#" },
  { label: "Docs", href: "#" },
  { label: "Blog", href: "#" },
  { label: "Privacy", href: "#" },
  { label: "Terms", href: "#" },
]

export default function Footer() {
  return (
    <section className="flex w-full flex-col items-stretch">
      <footer className="mx-auto w-full max-w-5xl px-6 py-8">
        <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
          <div className="flex items-center gap-3">
            <svg
              viewBox="0 0 24 24"
              fill="currentColor"
              aria-hidden="true"
              className="size-6 shrink-0 text-primary"
            >
              <rect
                x="3"
                y="3"
                width="8"
                height="8"
                transform="rotate(-6 7 7)"
              />
              <rect
                x="3"
                y="13"
                width="8"
                height="8"
                transform="rotate(5 7 17)"
              />
              <rect
                x="13"
                y="13"
                width="8"
                height="8"
                transform="rotate(-4 17 17)"
              />
              <rect
                x="13"
                y="3"
                width="8"
                height="8"
                transform="rotate(15 17 7)"
              />
            </svg>
            <div className="flex flex-col leading-none">
              <span className="text-sm font-bold tracking-tight">Acme</span>
              <span className="text-xs text-muted-foreground">
                Build fast. Ship faster.
              </span>
            </div>
          </div>

          <nav
            aria-label="Footer"
            className="flex flex-wrap items-center gap-x-5 gap-y-1"
          >
            {NAV_LINKS.map((link) => (
              <a
                key={link.label}
                href={link.href}
                className="text-xs text-muted-foreground transition-colors hover:text-foreground"
              >
                {link.label}
              </a>
            ))}
          </nav>

          <p className="shrink-0 text-xs text-muted-foreground">
            &copy; {new Date().getFullYear()} Acme, Inc.
          </p>
        </div>
      </footer>
    </section>
  )
}
registry/templates/startup/components/header.tsx
"use client"
import { Button } from "@/components/ui/button"
import {
  Sheet,
  SheetClose,
  SheetContent,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"

const navLinks = ["Blocks", "Docs", "Pricing", "Changelog"]

export default function Header() {
  return (
    <header className="sticky top-0 z-30 w-full border-b border-border bg-background/80 backdrop-blur">
      <div className="mx-auto flex h-16 w-full max-w-5xl items-center gap-6 px-6">
        <a href="#" className="flex items-center gap-2">
          <svg
            viewBox="0 0 24 24"
            fill="currentColor"
            aria-hidden="true"
            className="size-6 shrink-0 text-primary"
          >
            <rect x="3" y="3" width="8" height="8" transform="rotate(-6 7 7)" />
            <rect
              x="3"
              y="13"
              width="8"
              height="8"
              transform="rotate(5 7 17)"
            />
            <rect
              x="13"
              y="13"
              width="8"
              height="8"
              transform="rotate(-4 17 17)"
            />
            <rect
              x="13"
              y="3"
              width="8"
              height="8"
              transform="rotate(15 17 7)"
            />
          </svg>
          <span className="text-lg font-bold tracking-tight">Acme</span>
        </a>
        <nav className="hidden items-center gap-6 md:flex">
          {navLinks.map((link) => (
            <a
              key={link}
              href="#"
              className="text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
            >
              {link}
            </a>
          ))}
        </nav>
        <div className="ml-auto flex items-center gap-2">
          <Button
            render={<a href="#" />}
            nativeButton={false}
            className="hidden md:inline-flex"
          >
            Book A Demo
            <IconPlaceholder
              lucide="ArrowRight"
              tabler="IconArrowRight"
              hugeicons="ArrowRightIcon"
              phosphor="ArrowRight"
              remixicon="RiArrowRightLine"
              data-icon="inline-end"
              aria-hidden="true"
            />
          </Button>

          <Sheet>
            <SheetTrigger
              render={
                <Button variant="outline" size="icon" className="md:hidden" />
              }
              aria-label="Open menu"
            >
              <IconPlaceholder
                lucide="Menu"
                tabler="IconMenu"
                hugeicons="MenuIcon"
                phosphor="List"
                remixicon="RiMenuLine"
                aria-hidden="true"
              />
            </SheetTrigger>
            <SheetContent side="right" className="w-full sm:max-w-xs">
              <SheetHeader>
                <SheetTitle className="flex items-center gap-2">
                  <svg
                    viewBox="0 0 24 24"
                    fill="currentColor"
                    aria-hidden="true"
                    className="size-5 shrink-0 text-primary"
                  >
                    <rect
                      x="3"
                      y="3"
                      width="8"
                      height="8"
                      transform="rotate(-6 7 7)"
                    />
                    <rect
                      x="3"
                      y="13"
                      width="8"
                      height="8"
                      transform="rotate(5 7 17)"
                    />
                    <rect
                      x="13"
                      y="13"
                      width="8"
                      height="8"
                      transform="rotate(-4 17 17)"
                    />
                    <rect
                      x="13"
                      y="3"
                      width="8"
                      height="8"
                      transform="rotate(15 17 7)"
                    />
                  </svg>
                  Acme
                </SheetTitle>
              </SheetHeader>
              <nav className="flex flex-col px-2">
                {navLinks.map((link) => (
                  <SheetClose
                    key={link}
                    render={<a href="#" />}
                    nativeButton={false}
                    className="rounded-md px-2 py-2.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
                  >
                    {link}
                  </SheetClose>
                ))}
              </nav>
              <div className="mt-auto p-4">
                <Button
                  render={<a href="#" />}
                  nativeButton={false}
                  className="w-full"
                >
                  Book A Demo
                  <IconPlaceholder
                    lucide="ArrowRight"
                    tabler="IconArrowRight"
                    hugeicons="ArrowRightIcon"
                    phosphor="ArrowRight"
                    remixicon="RiArrowRightLine"
                    data-icon="inline-end"
                    aria-hidden="true"
                  />
                </Button>
              </div>
            </SheetContent>
          </Sheet>
        </div>
      </div>
    </header>
  )
}
registry/templates/startup/components/hero.tsx
"use client"

import { useState } from "react"
import { toast } from "sonner"

import { cn } from "@/lib/utils"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"

export default function Hero() {
  const [email, setEmail] = useState("")
  const [submitted, setSubmitted] = useState(false)

  function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault()
    if (!email) return
    setSubmitted(true)
    toast.success("You're on the list", {
      description: `We'll reach out to ${email} when your spot is ready.`,
    })
  }

  return (
    <section className="relative isolate flex w-full items-center justify-center overflow-hidden px-6 py-16 sm:py-24">
      <div
        aria-hidden="true"
        className={cn(
          "pointer-events-none absolute inset-0 -z-10",
          "bg-[linear-gradient(to_right,var(--color-border)_1px,transparent_1px),linear-gradient(to_bottom,var(--color-border)_1px,transparent_1px)]",
          "bg-[size:56px_56px] opacity-40",
          "[mask-image:radial-gradient(ellipse_70%_60%_at_50%_40%,black,transparent)]"
        )}
      />
      <div
        aria-hidden="true"
        className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(ellipse_60%_50%_at_50%_0%,var(--color-primary)/0.12,transparent)]"
      />

      <div className="mx-auto flex w-full max-w-2xl flex-col items-center text-center">
        <Badge variant="secondary" className="px-3 py-1">
          <IconPlaceholder
            lucide="Sparkles"
            tabler="IconSparkles"
            hugeicons="SparklesIcon"
            phosphor="Sparkle"
            remixicon="RiSparkling2Line"
            data-icon="inline-start"
            className="size-3.5"
          />
          Now in public beta
        </Badge>

        <h1 className="mt-6 font-heading text-4xl font-bold tracking-tight text-balance sm:text-5xl md:text-6xl">
          Ship your next idea with Acme
        </h1>

        <p className="mt-5 max-w-xl text-base text-balance text-muted-foreground sm:text-lg">
          The all-in-one platform that turns rough sketches into
          production-ready apps. Build faster, launch sooner, and scale without
          the busywork.
        </p>

        <form
          onSubmit={handleSubmit}
          className="mt-9 flex w-full max-w-md flex-col gap-3 sm:flex-row"
        >
          <div className="flex-1">
            <Label htmlFor="hero-email" className="sr-only">
              Email address
            </Label>
            <Input
              id="hero-email"
              type="email"
              required
              placeholder="you@acme.com"
              value={email}
              onChange={(event) => setEmail(event.target.value)}
              className="h-11"
            />
          </div>
          <Button type="submit" size="lg" className="h-11 shrink-0">
            {submitted ? "You're on the list" : "Get Started"}
            {!submitted && (
              <IconPlaceholder
                lucide="ArrowRight"
                tabler="IconArrowRight"
                hugeicons="ArrowRightIcon"
                phosphor="ArrowRight"
                remixicon="RiArrowRightLine"
                data-icon="inline-end"
                className="size-4"
              />
            )}
          </Button>
        </form>

        <p className="mt-4 flex items-center justify-center gap-1.5 text-xs text-muted-foreground">
          <IconPlaceholder
            lucide="ShieldCheck"
            tabler="IconShieldCheck"
            hugeicons="Shield01Icon"
            phosphor="ShieldCheck"
            remixicon="RiShieldCheckLine"
            className="size-3.5"
          />
          No credit card required. Unsubscribe anytime.
        </p>
      </div>
    </section>
  )
}
registry/templates/startup/components/how-it-works.tsx
import { Badge } from "@/components/ui/badge"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"

type IconProps = { className?: string; size?: number | string }

const steps = [
  {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="UserPlus"
        tabler="IconUserPlus"
        hugeicons="UserAddIcon"
        phosphor="UserPlus"
        remixicon="RiUserAddLine"
        {...p}
      />
    ),
    title: "Create your account",
    copy: "Sign up in under two minutes, no credit card required. Your workspace is ready the moment you confirm your email.",
  },
  {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="Settings"
        tabler="IconSettings"
        hugeicons="SettingsIcon"
        phosphor="GearSix"
        remixicon="RiSettings4Line"
        {...p}
      />
    ),
    title: "Configure your workflow",
    copy: "Choose from pre-built templates or define your own pipeline. Acme adapts to how your team already works.",
  },
  {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="Users"
        tabler="IconUsersGroup"
        hugeicons="UserGroupIcon"
        phosphor="UsersThree"
        remixicon="RiTeamLine"
        {...p}
      />
    ),
    title: "Invite your team",
    copy: "Send role-based invites in bulk. Colleagues join with a single click and inherit the right permissions automatically.",
  },
  {
    icon: (p: IconProps) => (
      <IconPlaceholder
        lucide="Rocket"
        tabler="IconRocket"
        hugeicons="RocketIcon"
        phosphor="Rocket"
        remixicon="RiRocketLine"
        {...p}
      />
    ),
    title: "Ship with confidence",
    copy: "Run automated checks, review the audit trail, and deploy, knowing Acme has your back at every stage.",
  },
]

export default function HowItWorks() {
  return (
    <section className="flex w-full items-center justify-center px-6 py-16 sm:py-24">
      <div className="mx-auto grid w-full max-w-5xl grid-cols-1 gap-12 md:grid-cols-[1fr_1.3fr] md:gap-16">
        <div className="md:sticky md:top-24 md:self-start">
          <Badge variant="secondary" className="mb-4 tracking-widest uppercase">
            How It Works
          </Badge>
          <h2 className="font-heading text-3xl font-bold tracking-tight text-balance sm:text-4xl">
            Up and running in four steps
          </h2>
          <p className="mt-4 text-muted-foreground">
            Acme is designed for momentum. Go from sign-up to full team
            collaboration without a single support ticket.
          </p>
        </div>

        <ol className="flex flex-col">
          {steps.map(({ icon: Icon, title, copy }, index) => {
            const isLast = index === steps.length - 1
            return (
              <li key={title} className="flex gap-6">
                <div className="flex flex-col items-center">
                  <span className="flex size-10 shrink-0 items-center justify-center rounded-lg border border-border bg-muted">
                    <Icon
                      className="size-4 text-foreground"
                      aria-hidden="true"
                    />
                  </span>
                  {!isLast && <span className="mt-1 w-px flex-1 bg-border" />}
                </div>

                <div className={isLast ? "pb-0" : "pb-10"}>
                  <h3 className="font-heading text-base font-semibold">
                    {title}
                  </h3>
                  <p className="mt-1.5 text-sm text-muted-foreground">{copy}</p>
                </div>
              </li>
            )
          })}
        </ol>
      </div>
    </section>
  )
}
registry/templates/startup/components/newsletter.tsx
"use client"
import { toast } from "sonner"

import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { Input } from "@/components/ui/input"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"

export default function Newsletter() {
  return (
    <section className="flex w-full items-center justify-center px-6 py-16 sm:py-24">
      <div className="mx-auto w-full max-w-5xl rounded-xl border border-border bg-card px-8 py-10 sm:px-12 sm:py-14">
        <div className="flex flex-col gap-8 md:flex-row md:items-center md:justify-between md:gap-16">
          <div className="flex flex-col gap-4 md:max-w-sm">
            <Badge variant="secondary" className="tracking-widest uppercase">
              Acme Weekly
            </Badge>
            <h2 className="font-heading text-3xl leading-tight font-bold tracking-tight text-balance sm:text-4xl">
              Insights that move your work forward
            </h2>
            <p className="text-sm text-muted-foreground">
              Product deep-dives, industry trends, and practical guides from the
              Acme team, landing in your inbox every Tuesday morning.
            </p>
          </div>

          <div className="flex flex-col gap-3 md:max-w-md md:min-w-0 md:flex-1">
            <form
              className="flex flex-col gap-3"
              onSubmit={(e) => {
                e.preventDefault()
                toast.success("You're subscribed. See you Tuesday!")
              }}
            >
              <div className="flex flex-col gap-2 sm:flex-row">
                <Input
                  type="email"
                  placeholder="work@company.com"
                  aria-label="Email address"
                  className="flex-1 border-border bg-background"
                />
                <Button type="submit" className="shrink-0">
                  Subscribe
                  <IconPlaceholder
                    lucide="ArrowRight"
                    tabler="IconArrowRight"
                    hugeicons="ArrowRightIcon"
                    phosphor="ArrowRight"
                    remixicon="RiArrowRightLine"
                    data-icon="inline-end"
                    aria-hidden="true"
                  />
                </Button>
              </div>
              <div className="flex items-center gap-2">
                <Checkbox id="newsletter-consent" name="consent" />
                <label
                  htmlFor="newsletter-consent"
                  className="text-sm font-normal text-muted-foreground"
                >
                  I agree to receive the Acme Weekly newsletter.
                </label>
              </div>
            </form>
            <p className="flex items-center gap-1.5 text-xs text-muted-foreground">
              <IconPlaceholder
                lucide="Lock"
                tabler="IconLock"
                hugeicons="LockIcon"
                phosphor="Lock"
                remixicon="RiLockLine"
                aria-hidden="true"
                className="shrink-0"
                size={12}
              />
              Your data stays private. Unsubscribe any time, no questions asked.
            </p>
          </div>
        </div>
      </div>
    </section>
  )
}
registry/templates/startup/components/stats.tsx
import { Badge } from "@/components/ui/badge"
const stats = [
  { value: "$2.4B", label: "Revenue processed" },
  { value: "38 ms", label: "Median latency" },
  { value: "99.98%", label: "Uptime last 12 mo" },
  { value: "6,200+", label: "Businesses onboarded" },
]

export default function Stats() {
  return (
    <section className="flex w-full items-center justify-center px-6 py-16 sm:py-24">
      <div className="mx-auto w-full max-w-5xl">
        <div className="mb-12 text-center">
          <Badge variant="secondary" className="mb-4 tracking-widest uppercase">
            By The Numbers
          </Badge>
          <h2 className="font-heading text-3xl font-bold tracking-tight text-balance sm:text-4xl">
            Acme by the numbers
          </h2>
        </div>

        <dl className="grid grid-cols-2 gap-y-10 md:grid-cols-4 md:gap-y-0">
          {stats.map(({ value, label }) => (
            <div
              key={label}
              className="flex flex-col items-center gap-2 text-center"
            >
              <dt className="text-5xl font-bold tracking-tight tabular-nums sm:text-6xl">
                {value}
              </dt>
              <dd className="text-sm text-muted-foreground">{label}</dd>
            </div>
          ))}
        </dl>
      </div>
    </section>
  )
}
registry/templates/startup/components/testimonials.tsx
import { Badge } from "@/components/ui/badge"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Separator } from "@/components/ui/separator"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"

const testimonial = {
  quote:
    "Acme's platform compressed what used to be a six-week integration cycle down to a single sprint. Our engineers finally have breathing room to work on things that matter, and our customers felt the difference immediately.",
  name: "Isabelle Fontaine",
  role: "Chief Product Officer",
  company: "Meridian Labs",
  initials: "IF",
  avatar: "https://i.pravatar.cc/150?img=45",
}

const metrics = [
  { value: "6×", label: "faster integrations" },
  { value: "40%", label: "less engineer toil" },
  { value: "1 sprint", label: "to full deployment" },
]

export default function Testimonials() {
  return (
    <section className="flex w-full items-center justify-center px-6 py-16 sm:py-24">
      <div className="mx-auto w-full max-w-5xl">
        <Badge variant="secondary" className="mb-4 tracking-widest uppercase">
          Customer Story
        </Badge>
        <div className="relative rounded-xl border border-border bg-card p-10 sm:p-14">
          <div
            className="absolute top-10 left-0 h-16 w-[3px] bg-foreground"
            aria-hidden="true"
          />

          <IconPlaceholder
            lucide="Quote"
            tabler="IconQuotes"
            hugeicons="QuotesIcon"
            phosphor="Quotes"
            remixicon="RiDoubleQuotesR"
            className="mb-8 size-8 text-muted-foreground"
            aria-hidden="true"
          />

          <blockquote className="max-w-3xl text-xl leading-[1.65] font-medium tracking-tight text-foreground sm:text-2xl">
            &ldquo;{testimonial.quote}&rdquo;
          </blockquote>

          <Separator className="my-10" />

          <div className="flex flex-col gap-6 sm:flex-row sm:items-center sm:justify-between">
            <div className="flex items-center gap-4">
              <Avatar className="size-14 border border-border">
                <AvatarImage
                  src={testimonial.avatar}
                  alt={testimonial.name}
                  className="grayscale"
                />
                <AvatarFallback className="text-sm font-semibold">
                  {testimonial.initials}
                </AvatarFallback>
              </Avatar>

              <div className="flex flex-col gap-1">
                <span className="text-sm leading-tight font-semibold text-foreground">
                  {testimonial.name}
                </span>
                <span className="text-xs leading-tight text-muted-foreground">
                  {testimonial.role},{" "}
                  <span className="font-medium text-foreground">
                    {testimonial.company}
                  </span>
                </span>
              </div>
            </div>

            <div className="flex items-center gap-6 sm:gap-8">
              {metrics.map((m, i) => (
                <div
                  key={i}
                  className="flex flex-col items-center gap-0.5 text-center"
                >
                  <span className="text-lg leading-none font-bold tracking-tight text-foreground">
                    {m.value}
                  </span>
                  <span className="text-[11px] leading-tight text-muted-foreground">
                    {m.label}
                  </span>
                </div>
              ))}
            </div>
          </div>
        </div>
      </div>
    </section>
  )
}

More from 7ovr

Browse all components in this library →