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/BorderedAccordion.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 BorderedAccordion = forwardRef<HTMLDivElement, AccordionProps>(
(
{
items = [
{
title: "Pricing",
content: "Our plans start at $9.99/mo for standard.",
},
{
title: "Support",
content: "24/7 priority email support for all users.",
},
],
className = "",
...props
},
ref,
) => {
const [openIdx, setOpenIdx] = useState<number | null>(0);
return (
<div ref={ref} className={`w-72 space-y-2 ${className}`} {...props}>
{items.map((item, idx) => {
const isOpen = openIdx === idx;
return (
<div
key={idx}
className={`border rounded-lg transition-colors duration-200 ${
isOpen
? "border-blue-200 bg-blue-50/30"
: "border-zinc-200 bg-white"
}`}
>
<button
onClick={() => setOpenIdx(isOpen ? null : idx)}
className="w-full px-4 py-3 flex items-center justify-between text-left text-sm font-medium text-zinc-800 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 px-4 ${
isOpen
? "max-h-40 opacity-100 pb-3"
: "max-h-0 opacity-0 pb-0"
}`}
>
<div className="text-sm text-zinc-600">{item.content}</div>
</div>
</div>
);
})}
</div>
);
},
);
BorderedAccordion.displayName = "BorderedAccordion";