🎨
UI Knihovna
Kolekce nádherných, znovupoužitelných React komponent a CSS fragmentů vytvořená pomocí Tailwind CSS a Framer Motion.
Rozbalovací panely
Vertikálně skládací panely, které skrývají a odhalují obsah.
Základní panel
If you're unhappy with your purchase, we'll refund you in full.
Accordion.tsxLanguage: tsx
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
export function BasicAccordion() {
const [openIndex, setOpenIndex] = useState<number | null>(0);
const items = [
{ title: "What is your refund policy?", content: "If you're unhappy with your purchase, we'll refund you in full." },
{ title: "Do you offer technical support?", content: "Yes, we offer 24/7 technical support for all our premium customers." },
{ title: "Can I upgrade my plan later?", content: "Absolutely. You can upgrade or downgrade your plan at any time." },
];
return (
<div className="w-full max-w-lg mx-auto bg-surface border border-border rounded-2xl overflow-hidden divide-y divide-border">
{items.map((item, index) => {
const isOpen = openIndex === index;
return (
<div key={index} className="overflow-hidden">
<button
onClick={() => setOpenIndex(isOpen ? null : index)}
className="w-full flex justify-between items-center p-5 text-left font-medium text-foreground hover:bg-black/5 dark:hover:bg-white/5 transition-colors"
>
{item.title}
<motion.svg
animate={{ rotate: isOpen ? 180 : 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
className="w-5 h-5 text-muted shrink-0"
fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
</motion.svg>
</button>
<AnimatePresence initial={false}>
{isOpen && (
<motion.div
initial={{ height: 0 }}
animate={{ height: "auto" }}
exit={{ height: 0 }}
transition={{ duration: 0.3, ease: "easeInOut" }}
>
<div className="p-5 pt-0 text-muted leading-relaxed">
{item.content}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
})}
</div>
);
}