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-6 w-6 transform transition-transform duration-300 ease-in-out dark:text-white ${
isExpanded ? "rotate-180" : ""
} ${isExpanded ? "animate-bounce-down" : "animate-bounce-up"}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M19 9l-7 7-7-7"
/>
</svg>
);
const BasicAccordion: 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-lg overflow-hidden">
<div
className="flex items-center justify-between cursor-pointer py-2 px-6 bg-gray-100 dark:bg-gray-800"
onClick={toggleAccordion}
>
<div className="text-lg font-semibold text-gray-900 dark:text-gray-200">Accordion Title</div>
<span>
<AnimatedArrow isExpanded={isExpanded} />
</span>
</div>
<div
className="transition-all duration-300 ease-in-out bg-gray-100 dark:bg-gray-800"
style={{ height: height }}
>
<div ref={contentRef} className="px-6 py-4">
<p className="text-base text-gray-700 dark:text-gray-300">Accordion content goes here...</p>
</div>
</div>
</div>
</div>
);
};
export default BasicAccordion;
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-6 w-6 transform transition-transform duration-300 ease-in-out dark:text-white ${
isExpanded ? "rotate-180" : ""
} ${isExpanded ? "animate-bounce-down" : "animate-bounce-up"}`}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M19 9l-7 7-7-7"
/>
</svg>
);
const BorderedAccordion: 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="border border-gray-300 dark:border-gray-600 rounded-lg overflow-hidden max-w-lg mx-auto sm:max-w-sm lg:max-w-3xl my-4">
<div>
<div
className={`border-b flex items-center justify-between cursor-pointer py-2 px-6
${isExpanded ? "border-b dark:border-gray-600" : "border-none"}`}
onClick={toggleAccordion}
>
<div className="text-lg font-semibold text-gray-900 dark:text-gray-200">Accordion Title</div>
<span>
<AnimatedArrow isExpanded={isExpanded} />
</span>
</div>
<div
className="transition-all duration-300 ease-in-out"
style={{ height: height }}
>
<div ref={contentRef} className="px-6 py-4">
<p className="text-base text-gray-700 dark:text-gray-300">Accordion content goes here...</p>
</div>
</div>
</div>
</div>
);
};
export default BorderedAccordion;