bits-ui
componenthero-carouselsource: ok · refreshed
Hero Carousel
A 3-step hero carousel with animated step cards for landing pages.
npx shadcn add "https://bits-ui.netlify.app/r/hero-carousel.json"free
Where it goes
hero
Interaction
carousel
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · mediumanimationframer-motion
iconslucide-react
Files2 · source cached 10/8/2026, 6:15:02 AM
registry/new-york/ui/hero-carousel.tsx
// ./registry/new-york/ui/hero-carousel.tsx
"use client";
import React, { useState } from "react";
import { motion } from "framer-motion";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import { StepCard } from "./step-card";
const steps = [1, 2, 3] as const;
export function HeroCarousel() {
const [currentIndex, setCurrentIndex] = useState(0);
const handleNext = () => {
setCurrentIndex((prev) => (prev + 1) % steps.length);
};
const handlePrev = () => {
setCurrentIndex((prev) => (prev - 1 + steps.length) % steps.length);
};
const getPosition = (index: number) => {
if (index === currentIndex) return "center";
if (index === (currentIndex + 1) % steps.length) return "right";
return "left";
};
const variants = {
center: { x: "0%", scale: 1, zIndex: 10, opacity: 1 },
right: { x: "50%", scale: 0.8, zIndex: 5, opacity: 0.4 },
left: { x: "-50%", scale: 0.8, zIndex: 5, opacity: 0.4 },
};
return (
<div className="relative w-full max-w-lg mx-auto h-[400px] flex items-center justify-center">
{/* Controls */}
<div className="absolute inset-x-0 top-1/2 -translate-y-1/2 flex justify-between px-4 z-30 pointer-events-none">
<Button
size="icon"
onClick={handlePrev}
className="pointer-events-auto w-12 h-12 rounded-full bg-white/10 backdrop-blur-md border border-white/20 text-white hover:bg-white/20 transition-all"
>
<ChevronLeft className="w-6 h-6" />
</Button>
<Button
size="icon"
onClick={handleNext}
className="pointer-events-auto w-12 h-12 rounded-full bg-white/10 backdrop-blur-md border border-white/20 text-white hover:bg-white/20 transition-all"
>
<ChevronRight className="w-6 h-6" />
</Button>
</div>
<div className="relative w-full h-full flex items-center justify-center perspective-1000">
{steps.map((step, index) => (
<motion.div
key={step}
initial="center"
animate={getPosition(index)}
variants={variants}
transition={{ duration: 0.5, type: "spring", stiffness: 300, damping: 30 }}
className="absolute w-[85%] sm:w-[70%] h-full"
>
<StepCard step={step} />
</motion.div>
))}
</div>
</div>
);
}
registry/new-york/ui/step-card.tsx
// ./registry/new-york/ui/step-card.tsx
"use client";
import { motion } from "framer-motion";
import { SearchNormal } from "iconsax-react";
import { Search, MousePointer2 } from "lucide-react";
interface StepCardProps {
step: 1 | 2 | 3;
}
export function StepCard({ step }: StepCardProps) {
return (
<div className="relative group overflow-hidden rounded-2xl bg-[#09090b] border border-white/10 p-6 flex flex-col justify-between h-full min-h-[300px]">
{/* Background Gradient */}
<div
className={`absolute inset-0 bg-linear-to-br opacity-0 group-hover:opacity-100 transition-opacity duration-500 ${step === 1 ? "from-primary/5 to-transparent" :
step === 2 ? "from-purple-500/5 to-transparent" :
"from-green-500/5 to-transparent"
}`}
/>
{/* Interactive Element */}
<div className="relative h-40 mb-6 flex items-center justify-center">
{step === 1 && (
<div className="relative">
<motion.div
animate={{ y: [0, -10, 0] }}
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }}
className="bg-white/5 backdrop-blur-md border border-white/10 rounded-xl p-4 shadow-2xl transform -rotate-6 z-10 relative"
>
<div className="flex items-center gap-3 mb-2">
<div className="h-8 w-8 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400">
<SearchNormal variant="TwoTone" color="currentColor" size={16} />
</div>
<div className="h-2 w-20 bg-white/10 rounded-full" />
</div>
<div className="space-y-2">
<div className="h-2 w-32 bg-white/5 rounded-full" />
<div className="h-2 w-24 bg-white/5 rounded-full" />
</div>
</motion.div>
<motion.div
animate={{ y: [0, 10, 0] }}
transition={{ duration: 5, repeat: Infinity, ease: "easeInOut", delay: 1 }}
className="absolute top-4 -right-12 bg-white/5 backdrop-blur-md border border-white/10 rounded-xl p-4 shadow-2xl transform rotate-6 scale-90 opacity-60"
>
<div className="flex items-center gap-3 mb-2">
<div className="h-8 w-8 rounded-full bg-purple-500/20 flex items-center justify-center text-purple-400">
<MousePointer2 size={16} />
</div>
<div className="h-2 w-20 bg-white/10 rounded-full" />
</div>
<div className="space-y-2">
<div className="h-2 w-32 bg-white/5 rounded-full" />
</div>
</motion.div>
</div>
)}
{step === 2 && (
<>
<div className="grid grid-cols-2 gap-3">
{[1, 2, 3, 4].map((i) => (
<motion.div
key={i}
initial={{ opacity: 0.5, scale: 0.9 }}
whileHover={{ scale: 1.05, opacity: 1, backgroundColor: "rgba(255,255,255,0.1)" }}
className="bg-white/5 border border-white/10 rounded-lg p-3 w-24 h-16 flex flex-col justify-center items-center gap-2 cursor-pointer transition-colors"
>
<div className={`h-2 w-12 rounded-full ${i === 2 ? 'bg-green-500' : 'bg-white/10'}`} />
<div className="h-1 w-8 bg-white/5 rounded-full" />
</motion.div>
))}
</div>
<motion.div
className="absolute inset-0 flex items-center justify-center pointer-events-none"
animate={{ rotate: 360 }}
transition={{ duration: 20, repeat: Infinity, ease: "linear" }}
>
<div className="absolute top-0 w-full h-full border border-dashed border-white/5 rounded-full scale-150" />
</motion.div>
</>
)}
{step === 3 && (
<div className="flex items-end justify-center gap-2 px-8 h-full">
{[40, 70, 50, 90, 60].map((height, i) => (
<motion.div
key={i}
initial={{ height: 0 }}
whileInView={{ height: `${height}%` }}
transition={{ duration: 1, delay: i * 0.1 }}
className="w-8 bg-linear-to-t from-primary/20 to-primary rounded-t-sm relative group/bar"
>
<div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-white text-black text-[10px] font-bold px-1.5 py-0.5 rounded opacity-0 group-hover/bar:opacity-100 transition-opacity">
{height}%
</div>
</motion.div>
))}
</div>
)}
</div>
<div className="relative z-10">
<div className="text-xs font-bold text-primary mb-2 uppercase tracking-wider">Step {step}</div>
<h3 className="text-xl font-bold text-white mb-2">
{step === 1 ? "Select Your Niche" : step === 2 ? "AI Analysis" : "Launch & Profit"}
</h3>
<p className="text-sm text-muted-foreground">
{step === 1 ? "Choose from high-profit categories like Pets, Beauty, or Home Decor." :
step === 2 ? "Our agents scan thousands of ads, analyze copy, and score potential winners." :
"Get a curated list of high-scoring products with ad creatives ready to launch."}
</p>
</div>
</div>
);
}