forked from
standard.site/standard.site
Standard.site landing page built in Next.js
1'use client'
2
3import { useState } from 'react'
4import { motion, AnimatePresence } from 'motion/react'
5import { PlusIcon } from 'lucide-react'
6
7interface AccordionItemProps {
8 question: string
9 answer: string
10 isOpen: boolean
11 onToggle: () => void
12}
13
14function AccordionItem({ question, answer, isOpen, onToggle }: AccordionItemProps) {
15 return (
16 <div className="rounded-xl border border-border bg-card overflow-hidden">
17 <button
18 onClick={ onToggle }
19 className="flex w-full cursor-pointer items-center justify-between p-4 text-left"
20 >
21 <span className="font-medium text-base leading-snug tracking-tight text-base-content">
22 { question }
23 </span>
24 <motion.div
25 animate={{ rotate: isOpen ? 135 : 0 }}
26 transition={{ duration: 0.2, ease: [0.25, 0.1, 0.25, 1] }}
27 >
28 <PlusIcon className="size-5 text-base-content" />
29 </motion.div>
30 </button>
31 <AnimatePresence initial={ false }>
32 { isOpen && (
33 <motion.div
34 initial={{ height: 0, opacity: 0 }}
35 animate={{ height: 'auto', opacity: 1 }}
36 exit={{ height: 0, opacity: 0 }}
37 transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }}
38 className="overflow-hidden"
39 >
40 <div className="border-t border-border p-4">
41 <p className="text-base leading-snug tracking-tight text-muted">
42 { answer }
43 </p>
44 </div>
45 </motion.div>
46 ) }
47 </AnimatePresence>
48 </div>
49 )
50}
51
52interface AccordionProps {
53 items: { question: string; answer: string }[]
54}
55
56export function Accordion({ items }: AccordionProps) {
57 const [openIndex, setOpenIndex] = useState<number | null>(null)
58
59 const handleToggle = (index: number) => {
60 setOpenIndex(openIndex === index ? null : index)
61 }
62
63 return (
64 <>
65 { items.map((item, index) => (
66 <AccordionItem
67 key={ item.question }
68 question={ item.question }
69 answer={ item.answer }
70 isOpen={ openIndex === index }
71 onToggle={ () => handleToggle(index) }
72 />
73 )) }
74 </>
75 )
76}