Accordion
Accordion component is used to show and hide content with a toggle button.
Basic Accordion
import React, { useState, useRef, useEffect } from "react";
const AnimatedArrow: React.FC<{ isExpanded: boolean }> = ({ isExpanded }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
className={`h-5 w-5 transform transition-transform duration-500 ease-out text-gray-400 dark:text-gray-500 ${
isExpanded ? "rotate-180" : ""
}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M19 9l-7 7-7-7"
/>
</svg>
);
const Accordion: React.FC = () => {
const [isExpanded, setIsExpanded] = useState(false);
const [height, setHeight] = useState<number | undefined>(0);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isExpanded) {
const contentEl = contentRef.current;
setHeight(contentEl?.scrollHeight);
} else {
setHeight(0);
}
}, [isExpanded]);
const toggleAccordion = () => {
setIsExpanded(!isExpanded);
};
return (
<div className="max-w-lg mx-auto sm:max-w-sm lg:max-w-3xl my-4">
<div className="rounded-xl overflow-hidden bg-white dark:bg-gray-900 shadow-sm">
<div
className="flex items-center justify-between cursor-pointer py-4 px-6 transition-colors duration-200 hover:bg-gray-50 dark:hover:bg-gray-800"
onClick={toggleAccordion}
>
<div className="text-base font-medium text-gray-900 dark:text-gray-100">
Accordion Title
</div>
<span className="ml-6 flex items-center">
<AnimatedArrow isExpanded={isExpanded} />
</span>
</div>
<div
className="transition-all duration-500 ease-out overflow-hidden bg-gray-50 dark:bg-gray-800"
style={{ height }}
>
<div ref={contentRef} className="px-6 py-4">
<p className="text-sm leading-relaxed text-gray-600 dark:text-gray-300">
Accordion content goes here...
</p>
</div>
</div>
</div>
</div>
);
};
export default Accordion;
Bordered Accordion
import React, { useState, useRef, useEffect } from "react";
const AnimatedArrow: React.FC<{ isExpanded: boolean }> = ({ isExpanded }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
className={`h-5 w-5 transform transition-transform duration-500 ease-out text-gray-400 dark:text-gray-500 ${
isExpanded ? "rotate-180" : ""
}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
d="M19 9l-7 7-7-7"
/>
</svg>
);
const Accordion: React.FC = () => {
const [isExpanded, setIsExpanded] = useState(false);
const [height, setHeight] = useState<number | undefined>(0);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isExpanded) {
const contentEl = contentRef.current;
setHeight(contentEl?.scrollHeight);
} else {
setHeight(0);
}
}, [isExpanded]);
const toggleAccordion = () => {
setIsExpanded(!isExpanded);
};
return (
<div className="max-w-lg mx-auto sm:max-w-sm lg:max-w-3xl my-4">
<div className="border border-gray-200 dark:border-gray-700 rounded-xl overflow-hidden backdrop-blur-sm bg-white/50 dark:bg-gray-900/50">
<div
className={`flex items-center justify-between cursor-pointer py-4 px-6 transition-colors duration-200
hover:bg-gray-50 dark:hover:bg-gray-800/50
${
isExpanded ? "border-b border-gray-200 dark:border-gray-700" : ""
}`}
onClick={toggleAccordion}
>
<div className="text-base font-medium text-gray-900 dark:text-gray-100">
Accordion Title
</div>
<span className="ml-6 flex items-center">
<AnimatedArrow isExpanded={isExpanded} />
</span>
</div>
<div
className="transition-all duration-500 ease-out overflow-hidden"
style={{ height }}
>
<div ref={contentRef} className="px-6 py-4">
<p className="text-sm leading-relaxed text-gray-600 dark:text-gray-300">
Accordion content goes here...
</p>
</div>
</div>
</div>
</div>
);
};
export default Accordion;