Baktiyar Ablaikhan

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).