FAQ accordion
- Source
- After bencho.dev ↗
- Author
- Interaction after bencho.dev
- Licence
- Original code — interaction after bencho.dev
Loading…
Click a question to open it; the others fold away.
Code
"use client";
import { AnimatePresence, motion } from "motion/react";
import { Plus } from "lucide-react";
import { useState } from "react";
import { Surface } from "../ui";
const QA = [
["What is in the box?", "A small, sturdy thing that does one job well, and a card that explains it."],
["Can I return it?", "Within thirty days, no questions asked. We pay the postage both ways."],
["Does it need batteries?", "No. It charges over USB-C and lasts about a month on a full charge."],
];
export default function FaqAccordion() {
const [open, setOpen] = useState<number | null>(0);
return (
<Surface>
<div className="w-[21em] rounded-[1.2em] bg-white p-[0.4em] shadow-[0_12px_32px_-18px_rgba(0,0,0,0.35)]">
{QA.map(([q, a], i) => {
const on = open === i;
return (
<motion.div key={q} layout className={`overflow-hidden rounded-[0.9em] ${on ? "bg-[#f6f5f1]" : ""}`} transition={{ type: "spring", stiffness: 400, damping: 36 }}>
<button
type="button"
onClick={() => setOpen(on ? null : i)}
className="flex w-full items-center justify-between gap-[1em] px-[0.9em] py-[0.8em] text-left text-[0.9em] font-medium"
>
{q}
<motion.span
animate={{ rotate: on ? 45 : 0, backgroundColor: on ? "#151515" : "#f1f0ec", color: on ? "#ffffff" : "#151515" }}
className="grid h-[1.6em] w-[1.6em] shrink-0 place-items-center rounded-full"
>
<Plus className="h-[0.9em] w-[0.9em]" />
</motion.span>
</button>
<AnimatePresence initial={false}>
{on && (
<motion.div
initial={{ height: 0, opacity: 0, filter: "blur(4px)" }}
animate={{ height: "auto", opacity: 1, filter: "blur(0px)" }}
exit={{ height: 0, opacity: 0, filter: "blur(4px)" }}
transition={{ type: "spring", stiffness: 400, damping: 36 }}
>
<p className="px-[1.05em] pb-[0.9em] text-[0.8em] leading-relaxed text-[#6f6d67]">{a}</p>
</motion.div>
)}
</AnimatePresence>
</motion.div>
);
})}
</div>
</Surface>
);
}
Prompt
Build "FAQ accordion", a layout animation. What it does: Click a question to open it; the others fold away. Write it as a single self-contained React component in TypeScript that fills its parent (w-full h-full), styled with Tailwind CSS, using lucide-react, motion. Technique: an HTML canvas, the Motion library, pointer tracking. It sits on a light background, in a 16:10 frame. Respect prefers-reduced-motion with a still state, pause work when the element is off screen, and keep it accessible. Original: https://bencho.dev (Interaction after bencho.dev, Original code — interaction after bencho.dev).