nextui.dev
Home
Library
Docs
Pricing
Components26
AI & agents5
  • AI chat inputPro
  • Agent task checklistPro
  • Agent thinking
  • Thinking indicator
  • Prompt deckNew
Forms & inputs7
  • Input with label
  • Input with icon and button
  • Settings switches
  • Password with strength meter
  • One-time code input
  • WaitlistNew
  • File dropzonePro
Cards5
  • Style marquee cardPro
  • Folder card
  • Celebration cardPro
  • Smart PDF stackNew
  • Waitlist pass
Buttons, badges & avatars5
  • Buttons with icons
  • QR scan badge
  • Avatar group
  • Rolling price badge
  • Avatars with status
Feedback & display4
  • Alert banners
  • FAQ accordion
  • Keyboard shortcuts
  • Empty state
Blocks11
Hero4
  • Animated pill heroPro
  • Integrations grid hero
  • Waitlist heroNew
  • Orbit chat heroNew
Features & pricing3
  • Usage cardPro
  • Three-tier pricing
  • Feature with board
Testimonials & team3
  • Testimonial cards
  • Single quote
  • Team carousel
Sign-in1
  • Sign-in cardPro
Motion2
Effects2
  • Text revealPro
  • Sound ringNew
Themes9
  • Default
  • Ember
  • Ocean
  • Forest
  • Grape
  • Rose
  • Amber
  • Clay
  • Brutal
nextui.dev

nextui components, blocks and themes you install with one command.

Product

  • Components
  • Blocks
  • Motion
  • Themes
  • Pricing

Resources

  • Docs
  • Roadmap
  • GitHub

Legal

  • License
  • Privacy
  • Terms
  • Refunds

nextUI

[ 200 OK ] · nextUI

© 2026 NEXTUI.DEV. All rights reserved.

ComponentsAI & agentsPrompt deck

Prompt deckNew

A stack of prompt cards that swipes the front card away and lifts the next one into place, on a loop while it is on screen.

Copy prompt
MCP

Installation

Runs as is, no setup needed. Want a shorter command like @nextui/card-05? See short names.

$pnpm dlx shadcn@latest add https://tinistudio.vercel.app/r/card-05.json

Related

All components
Pro

AI chat inputNew

Rounded AI prompt box with attach tools, a round send button and a shimmering "Searching the web" status below.

Pro

Agent task checklistNew

Collapsible to-do list for an AI agent: each step fills a progress ring, rolls its percentage and strikes through when done.

Agent thinking

Thinking indicator: a wobbling dot ring and a shimmering label.

npx shadcn@latest add https://tinistudio.vercel.app/r/card-05.json
components/nextui/card-05.tsx
"use client";

import { useEffect, useRef, useState } from "react";
import type { LucideIcon } from "lucide-react";
import {
  Asterisk,
  Box,
  ChartLine,
  Code,
  CreditCard,
  Flag,
  Globe,
  ImageIcon,
  Layers,
  LayoutGrid,
  MousePointer2,
  Palette,
  Route,
  ScanSearch,
  Smartphone,
  Sparkles,
} from "lucide-react";
import { cn } from "@/lib/utils";

// Source tiles stay inside the site palette: the red accent, its tint and the ink.
const TONES = ["bg-primary text-primary-foreground", "bg-foreground text-background", "bg-primary/15 text-primary"];

const PROMPTS: { icon: LucideIcon; title: string; prompt: string; sources: LucideIcon[] }[] = [
  { icon: MousePointer2, title: "polish screens", prompt: "can you make this screen feel calmer?", sources: [Palette, Smartphone, Sparkles] },
  { icon: Layers, title: "compare onboarding", prompt: "how is my onboarding different from theirs?", sources: [Globe, LayoutGrid, Layers] },
  { icon: CreditCard, title: "compare paywalls", prompt: "why does their paywall convert better?", sources: [CreditCard, ChartLine, Sparkles] },
  { icon: ScanSearch, title: "find ui elements", prompt: "where do others put the progress chart?", sources: [ChartLine, Smartphone, Code] },
  { icon: Flag, title: "map user flows", prompt: "show the full flow from install to first run", sources: [Route, Globe, Palette] },
];

const TOOLS = [Asterisk, Box, Globe, ImageIcon, Code, LayoutGrid, Sparkles];

// Pose for each slot: 0 is the front card, 1-2 peek out behind it,
// 3 waits unseen at the back and 4 is the card that just swiped away.
const SLOTS = [
  { transform: "translate3d(0,0,0) rotate(0deg) scale(1)", opacity: 1 },
  { transform: "translate3d(4px,12px,0) rotate(1.5deg) scale(0.95)", opacity: 1 },
  { transform: "translate3d(-4px,22px,0) rotate(-1.5deg) scale(0.9)", opacity: 1 },
  { transform: "translate3d(0,30px,0) rotate(0deg) scale(0.86)", opacity: 0 },
  { transform: "translate3d(-70%,-6px,0) rotate(-9deg) scale(0.92)", opacity: 0 },
];
const Z = [5, 4, 3, 1, 2];
const HOLD = 2200; // ms each prompt stays in front

export default function Card05() {
  const ref = useRef<HTMLDivElement>(null);
  const [active, setActive] = useState(0);
  const [visible, setVisible] = useState(false);
  const [hovered, setHovered] = useState(false);
  const [reduce, setReduce] = useState(false);
  const play = visible && !hovered && !reduce;

  useEffect(() => {
    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    const sync = () => setReduce(mq.matches);
    sync();
    mq.addEventListener("change", sync);
    return () => mq.removeEventListener("change", sync);
  }, []);

  // The deck only shuffles while it is on screen.
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(([entry]) => setVisible(entry.isIntersecting));
    io.observe(el);
    return () => io.disconnect();
  }, []);

  useEffect(() => {
    if (!play) return;
    const id = window.setInterval(() => setActive((a) => (a + 1) % PROMPTS.length), HOLD);
    return () => window.clearInterval(id);
  }, [play]);

  return (
    <div
      ref={ref}
      onPointerEnter={() => setHovered(true)}
      onPointerLeave={() => setHovered(false)}
      className="relative mx-auto h-52 w-full max-w-xs"
    >
      {PROMPTS.map((p, i) => {
        const slot = (i - active + PROMPTS.length) % PROMPTS.length;
        const front = slot === 0;
        return (
          <article
            key={p.title}
            aria-hidden={!front}
            className="absolute inset-x-0 top-0 flex h-44 flex-col rounded-2xl border bg-card p-4 text-card-foreground shadow-[0_1px_2px_rgba(0,0,0,0.04),0_12px_32px_-12px_rgba(0,0,0,0.18)] transition-[transform,opacity] duration-700 ease-[cubic-bezier(0.22,1,0.36,1)] will-change-transform motion-reduce:transition-none"
            style={{ ...SLOTS[slot], zIndex: Z[slot] }}
          >
            {/* Cards behind the front one read as blank paper; the leaving card keeps its text. */}
            <div
              className={cn(
                "flex flex-1 flex-col transition-opacity duration-500 motion-reduce:transition-none",
                front || slot === 4 ? "opacity-100" : "opacity-0",
              )}
            >
              <div className="flex items-start gap-2.5">
                <span className="grid size-7 shrink-0 place-items-center rounded-lg bg-muted text-muted-foreground">
                  <p.icon className="size-4" />
                </span>
                <h3 className="flex-1 truncate pt-0.5 text-base leading-tight font-semibold tracking-tight">{p.title}</h3>
                <div className="flex shrink-0 -space-x-1.5" aria-label="Sources">
                  {p.sources.map((S, j) => (
                    <span key={j} className={cn("grid size-6 place-items-center rounded-md ring-2 ring-card", TONES[j])}>
                      <S className="size-3.5" strokeWidth={2.25} />
                    </span>
                  ))}
                </div>
              </div>
              <p className="mt-3 flex gap-2 pl-0.5 text-sm leading-snug text-pretty">
                <span className="text-muted-foreground">&gt;</span>
                <span>{p.prompt}</span>
              </p>
              <div className="mt-auto flex justify-end gap-1.5 text-muted-foreground">
                {TOOLS.map((T, j) => (
                  <T key={j} className="size-3" aria-hidden />
                ))}
              </div>
            </div>
          </article>
        );
      })}
    </div>
  );
}
Sign in