A dark, centred hero section with eight rounded rectangles of tinted glass scattered behind the copy. Each shape falls in from above while rotating into its final tilt, then bobs gently on its own timing. The shapes fade from a colour on the left to transparent on the right and carry a faint inner highlight and hairline edge. In front are a status pill, a two line headline whose second line is set in italic serif with a gradient, a paragraph, two buttons and a row of services. On phones the whole shape layer scales down and the buttons stack.
// Adapted from Kokonut UI (https://github.com/kokonut-labs/kokonutui/blob/main/components/kokonutui/shape-hero.tsx)
// Copyright (c) 2025 kokonutUI. MIT License: https://we.inc/components/licenses
import { motion, useReducedMotion } from "framer-motion";
// Each shape: where it sits, its size, tilt, corner radius, tint and when it drops in.
const shapes = [
{ position: "left-[-15%] top-[-10%]", width: 300, height: 500, rotate: -8, radius: 24, tint: "from-indigo-500/40", delay: 0.3 },
{ position: "bottom-[-5%] right-[-20%]", width: 600, height: 200, rotate: 15, radius: 20, tint: "from-rose-500/40", delay: 0.5 },
{ position: "left-[-5%] top-[40%]", width: 300, height: 300, rotate: 24, radius: 32, tint: "from-violet-500/40", delay: 0.4 },
{ position: "right-[10%] top-[5%]", width: 250, height: 100, rotate: -20, radius: 12, tint: "from-amber-500/40", delay: 0.6 },
{ position: "right-[-10%] top-[45%]", width: 400, height: 150, rotate: 35, radius: 16, tint: "from-emerald-500/40", delay: 0.7 },
{ position: "bottom-[10%] left-[20%]", width: 200, height: 200, rotate: -25, radius: 28, tint: "from-blue-500/40", delay: 0.2 },
{ position: "left-[40%] top-[15%]", width: 150, height: 80, rotate: 45, radius: 10, tint: "from-purple-500/40", delay: 0.8 },
{ position: "left-[25%] top-[60%]", width: 450, height: 120, rotate: -12, radius: 18, tint: "from-teal-500/40", delay: 0.9 },
];
const services = ["Identity", "Packaging", "Web design"];
export default function HeroWithFloatingGlassShapes() {
const reduce = useReducedMotion();
const rise = (order: number) => ({
initial: reduce ? false : ({ opacity: 0, y: 30 } as const),
animate: { opacity: 1, y: 0 },
transition: { duration: 0.9, delay: 0.25 + order * 0.15, ease: [0.25, 0.4, 0.25, 1] as const },
});
return (
<section className="relative flex min-h-[700px] w-full items-center justify-center overflow-hidden bg-[#030303] px-4 py-24">
<div className="absolute inset-0 bg-gradient-to-br from-indigo-500/10 via-transparent to-rose-500/10 blur-3xl" aria-hidden="true" />
{/* The shape layer is drawn larger and scaled down on phones, so the shapes shrink but keep their places. */}
<div className="absolute -inset-[40%] scale-[0.55] overflow-hidden sm:inset-0 sm:scale-100" aria-hidden="true">
{shapes.map((shape, i) => (
<motion.div
key={i}
className={`absolute ${shape.position}`}
initial={reduce ? false : { opacity: 0, y: -150, rotate: shape.rotate - 15 }}
animate={{ opacity: 1, y: 0, rotate: shape.rotate }}
transition={{ duration: 1.3, delay: shape.delay * 0.5, ease: [0.23, 0.86, 0.39, 0.96], opacity: { duration: 0.8, delay: shape.delay * 0.5 } }}
>
<motion.div
className="relative"
style={{ width: shape.width, height: shape.height }}
animate={reduce ? undefined : { y: [0, 15, 0] }}
transition={{ duration: 8 + (i % 3) * 2, repeat: Infinity, ease: "easeInOut", delay: i * 0.4 }}
>
<div
className={`absolute inset-0 bg-gradient-to-r to-transparent shadow-[0_2px_16px_-2px_rgba(255,255,255,0.06)] ring-1 ring-white/10 backdrop-blur-[1px] after:absolute after:inset-0 after:rounded-[inherit] after:bg-[radial-gradient(circle_at_50%_50%,rgba(255,255,255,0.14),transparent_70%)] ${shape.tint}`}
style={{ borderRadius: shape.radius }}
/>
</motion.div>
</motion.div>
))}
</div>
{/* Fade the shapes out towards the top and bottom edges. */}
<div className="pointer-events-none absolute inset-0 bg-gradient-to-t from-[#030303] via-transparent to-[#030303]/80" aria-hidden="true" />
<div className="relative z-10 mx-auto max-w-3xl text-center">
<motion.p {...rise(0)} className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/5 px-3.5 py-1.5 text-sm text-neutral-200">
<span className="h-2 w-2 rounded-full bg-rose-400" aria-hidden="true" />
Booking two projects for spring
</motion.p>
<motion.h1 {...rise(1)} className="mt-7 text-5xl font-bold tracking-tight sm:text-7xl md:text-8xl">
<span className="bg-gradient-to-b from-white to-white/80 bg-clip-text text-transparent">Brands with a</span>
<br />
<span className="bg-gradient-to-r from-indigo-300 via-white to-rose-300 bg-clip-text pr-2 font-serif font-normal italic text-transparent">point of view</span>
</motion.h1>
<motion.p {...rise(2)} className="mx-auto mt-7 max-w-xl text-base leading-relaxed text-neutral-300 sm:text-xl">
Halden and Roe is a four person studio that names, draws and builds identities for food, drink and hospitality founders.
</motion.p>
<motion.div {...rise(3)} className="mt-9 flex flex-col items-center justify-center gap-3 sm:flex-row">
<a href="#" className="inline-flex h-12 w-full items-center justify-center rounded-full bg-white px-8 text-sm font-semibold text-neutral-950 transition hover:bg-neutral-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-[#030303] sm:w-auto">
Start a project
</a>
<a href="#" className="inline-flex h-12 w-full items-center justify-center rounded-full border border-white/25 px-8 text-sm font-semibold text-white transition hover:bg-white/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-[#030303] sm:w-auto">
See recent work
</a>
</motion.div>
<motion.ul {...rise(4)} className="mt-12 flex flex-wrap items-center justify-center gap-x-3 gap-y-2 text-xs font-semibold uppercase tracking-[0.2em] text-neutral-300">
{services.map((service, i) => (
<li key={service} className="flex items-center gap-3">
{i > 0 && <span className="h-1 w-1 rounded-full bg-neutral-500" aria-hidden="true" />}
{service}
</li>
))}
</motion.ul>
</div>
</section>
);
}
Install with one command: npx shadcn@latest add https://we.inc/r/hero-with-floating-glass-shapes.json
Each shape is a div with a left to right gradient from a semi transparent colour to nothing, a one pixel white ring at low opacity, a soft shadow and a radial highlight in an `after` layer. Together they read as a pane of tinted glass.
The outer one plays the entrance once: fade, fall and rotate. The inner one loops the float. Keeping them apart means the loop never fights the entrance.
The layer is made 180 percent of the section and scaled to 55 percent on small screens, so every shape shrinks but stays at the same relative position. From the `sm` breakpoint the layer is normal size.
Yes. Set the section to white, change the text to dark colours, lower the tints to about `/25` and swap the top and bottom fade to white.
No. It uses the system serif through `font-serif`. If your site loads a script or display font, put its class on that span instead.
Adapted from Shape Hero in Kokonut UI, used under its open-source licence. Licences and credits
Related: Dark Hero with Spotlight Sweep and Line Reveal, Dark Hero with Gradient Glow and Product Window, Centered Hero with Two Buttons and Logo Row.
More hero sections or all free UI components. Start for free to have a full site built around it.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.