Accordion

Visually highlights selected items by sliding a background into view when hovered over or clicked. This smooth transition helps users focus on the active item, making it ideal for interactive lists, menus, or navigations where clear selection feedback is important.

Installation

npm install framer-motion
accordion.tsx
1
// @ts-nocheck
2
import React, { ReactNode } from 'react';
3
import { AnimatePresence, motion } from 'framer-motion';
4
import { ChevronDown } from 'lucide-react';
5
import { cn } from '@/lib/utils';
6
7
const AccordionContext = React.createContext({});
8
const useAccordion = () => React.useContext(AccordionContext);
9
10
export function AccordionContainer({
11
children,
12
className,
13
}: {
14
children: ReactNode;
15
className?: string;
16
}) {
17
return (
18
<div className={cn('grid grid-cols-2 gap-1', className)}>{children}</div>
19
);
20
}
21
export function AccordionWrapper({ children }) {
22
return <div>{children}</div>;
23
}
24
25
export function Accordion({
26
children,
27
multiple,
28
defaultValue,
29
}: {
30
children: ReactNode;
31
multiple?: boolean;
32
defaultValue?: string | undefined | string[];
33
}) {
34
const [activeIndex, setActiveIndex] = React.useState(
35
multiple ? (defaultValue ? [defaultValue] : []) : [defaultValue]
36
);
37
38
function onChangeIndex(value) {
39
setActiveIndex((currentActiveIndex) => {
40
if (!multiple) {
41
return value === currentActiveIndex ? null : value;
42
}
43
44
if (currentActiveIndex.includes(value)) {
45
return currentActiveIndex.filter((i) => i !== value);
46
}
47
48
return [...currentActiveIndex, value];
49
});
50
}
51
52
return React.Children.map(children, (child) => {
53
const value = child.props.value;
54
const isActive = multiple
55
? Array.isArray(activeIndex) && activeIndex.includes(value)
56
: Array.isArray(activeIndex)
57
? activeIndex[0].includes(value)
58
: activeIndex === value;
59
60
return (
61
<AccordionContext.Provider value={{ isActive, value, onChangeIndex }}>
62
<>{child}</>
63
</AccordionContext.Provider>
64
);
65
});
66
}
67
68
export function AccordionItem({ children, value }) {
69
const { isActive } = useAccordion();
70
71
return (
72
<div
73
className={`rounded-lg overflow-hidden mb-2 ${
74
isActive
75
? 'active border-2 dark:border-[#656fe2] border-[#F2F2F2] dark:bg-[#E0ECFB] bg-[#F2F2F2]'
76
: 'bg-transparent border-2 dark:hover:border-[#656fe2]'
77
}
78
`}
79
value={value}
80
>
81
{children}
82
</div>
83
);
84
}
85
86
export function AccordionHeader({
87
children,
88
icon,
89
}: {
90
children: ReactNode;
91
icon?: any;
92
}) {
93
const { isActive, value, onChangeIndex } = useAccordion();
94
95
return (
96
<motion.div
97
className={`p-4 cursor-pointer transition-all font-semibold dark:text-white text-black dark:hover:bg-[#1e2a78] hover:bg-[#F2F2F2] dark:hover:text-white hover:text-black flex justify-between items-center ${
98
isActive
99
? 'active dark:bg-[#1e2a78] bg-[#F2F2F2] '
100
: 'dark:bg-[#11112b] bg-white'
101
}
102
`}
103
onClick={() => onChangeIndex(value)}
104
>
105
{children}
106
{icon ? (
107
<div
108
className={`${
109
isActive ? 'rotate-45 ' : 'rotate-0 '
110
} transition-transform`}
111
>
112
{icon}
113
</div>
114
) : (
115
<>
116
<ChevronDown
117
className={`${
118
isActive ? 'rotate-180 ' : 'rotate-0 '
119
} transition-transform`}
120
/>
121
</>
122
)}
123
</motion.div>
124
);
125
}
126
127
export function AccordionPanel({ children }) {
128
const { isActive } = useAccordion();
129
130
return (
131
<AnimatePresence initial={true}>
132
{isActive && (
133
<motion.div
134
initial={{ height: 0, overflow: 'hidden' }}
135
animate={{ height: 'auto', overflow: 'hidden' }}
136
exit={{ height: 0 }}
137
transition={{ type: 'spring', duration: 0.3, bounce: 0 }}
138
className={`dark:bg-white bg-[#F2F2F2]
139
`}
140
>
141
<motion.article
142
initial={{ clipPath: 'polygon(0 0, 100% 0, 100% 0, 0 0)' }}
143
animate={{ clipPath: 'polygon(0 0, 100% 0, 100% 100%, 0% 100%)' }}
144
exit={{
145
clipPath: 'polygon(0 0, 100% 0, 100% 0, 0 0)',
146
}}
147
transition={{
148
type: 'spring',
149
duration: 0.4,
150
bounce: 0,
151
}}
152
className={`p-3 bg-transparent text-black `}
153
>
154
{children}
155
</motion.article>
156
</motion.div>
157
)}
158
</AnimatePresence>
159
);
160
}

Structure

1
<Accordion defaultValue={['item-1']} multiple>
2
<AccordionItem value='item-1'>
3
<AccordionHeader icon={<Plus />}></AccordionHeader>
4
<AccordionPanel></AccordionPanel>
5
</AccordionItem>
6
<AccordionItem value='item-2'>
7
<AccordionHeader icon={<Plus />}></AccordionHeader>
8
<AccordionPanel></AccordionPanel>
9
</AccordionItem>
10
<AccordionItem value='item-3'>
11
<AccordionHeader icon={<Plus />}></AccordionHeader>
12
<AccordionPanel></AccordionPanel>
13
</AccordionItem>
14
</Accordion>

Props

PropTypeDefaultDescription
defaultValuestringstring[]undefined
multiplebooleanfalseWhether the accordion allows multiple items to be active at the same time
childrenReactNode[]undefinedThe accordion items, including their headers and panels
classNamestring''Optional CSS class for styling the accordion wrapper

Example

Single Layout

What is a UI component?
A UI (User Interface) component is a modular, reusable element that serves a specific function within a graphical user interface. Examples include buttons, input fields, dropdown menus, sliders, and checkboxes.
Why are UI components important?
UI components promote consistency, efficiency, and scalability in software development. They allow developers to reuse code, maintain a consistent look and feel across an application, and easily make updates or modifications without affecting the entire system.
Key characteristics of UI components?

Multi Layout

What is a UI component?
Why are UI components important?
UI components promote consistency, efficiency, and scalability in software development. They allow developers to reuse code, maintain a consistent look and feel across an application, and easily make updates or modifications without affecting the entire system.
Key characteristics of UI components?