accordions
Expand/collapse panels for FAQs and dense settings.
It is a React component (often forwardRef) styled with Tailwind utility classes so you can drop it into layouts or compose it with other kit pieces.
blogs/components/accordions/MinimalAccordion.tsx
import React, { useState, forwardRef } from "react";
import { ChevronDown } from "lucide-react";
export interface AccordionItem {
title: React.ReactNode;
content: React.ReactNode;
}
export interface AccordionProps extends React.HTMLAttributes<HTMLDivElement> {
items?: AccordionItem[];
}
export const MinimalAccordion = forwardRef<HTMLDivElement, AccordionProps>(
(
{
items = [
{
title: "What is your return policy?",
content:
"You can return any item within 30 days of purchase. Items must be in original condition.",
},
],
className = "",
...props
},
ref,
) => {
const [openIdx, setOpenIdx] = useState<number | null>(null);
return (
<div ref={ref} className={`w-64 space-y-2 ${className}`} {...props}>
{items.map((item, idx) => {
const isOpen = openIdx === idx;
return (
<div key={idx} className="border-b border-zinc-200">
<button
onClick={() => setOpenIdx(isOpen ? null : idx)}
className="w-full py-3 flex items-center justify-between text-left text-sm font-medium text-zinc-800 hover:text-black focus:outline-none"
>
{item.title}
<ChevronDown
className={`w-4 h-4 text-zinc-400 transition-transform duration-300 ${
isOpen ? "rotate-180" : ""
}`}
/>
</button>
<div
className={`overflow-hidden transition-all duration-300 ease-in-out ${
isOpen ? "max-h-40 opacity-100 mb-3" : "max-h-0 opacity-0"
}`}
>
<div className="text-sm text-zinc-500">{item.content}</div>
</div>
</div>
);
})}
</div>
);
},
);
MinimalAccordion.displayName = "MinimalAccordion";