Accordion
A vertically stacked set of interactive headings that each reveal an associated section of content.
Installation
To install the Accordion follow these steps :
Install the themer package
Run the following command in your terminal:
npm install @tailus/themer-accordion @radix-ui/react-accordion
Copy and paste
Copy and paste the following code in a .tsx
file
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { cn } from "lib/utils";
import React from "react";
import { ChevronDownIcon } from "@radix-ui/react-icons";
import {
accordion as defaultTheme,
outlinedVariant as outlinedTheme,
elevatedVariant as elevatedTheme,
ghostVariant as ghostTheme,
softVariant as softTheme,
outlinedElevatedVariant as outlinedElevatedTheme,
} from "@tailus/themer-accordion"
import { cva } from "class-variance-authority";
type Variant = "default" | "outlined" | "elevated" | "ghost" | "soft" | "outlinedElevated";
type ElementType = keyof typeof defaultTheme;
const variantTheme = (element: ElementType) => cva('', {
variants: {
variant: {
default: defaultTheme[element],
outlined: outlinedTheme[element],
elevated: elevatedTheme[element],
ghost: ghostTheme[element],
soft: softTheme[element],
outlinedElevated: outlinedElevatedTheme[element],
}
}
})
const defaultContextValue: Variant = "default";
const Context = React.createContext<Variant>(defaultContextValue);
interface AccordionRootProps {
variant?: Variant;
}
const AccordionRoot = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root> & AccordionRootProps
>(({className, variant, ...props}, forwardedRef) => {
return (
<Context.Provider value={variant || defaultContextValue}>
<AccordionPrimitive.Root
className={cn(variantTheme("root")({variant: variant}), className)}
{...props}
ref={forwardedRef}
/>
</Context.Provider>
)
});
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({className, ...props}, forwardedRef) => {
const variant = React.useContext(Context);
return (
<AccordionPrimitive.Item
className={cn(variantTheme("item")({variant: variant}), className)}
{...props}
ref={forwardedRef}
/>
)
});
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({className, children, ...props}, forwardedRef) => {
const generateTriggerClassNames = (theme: Variant) => {
const themes = {
default: defaultTheme,
outlined: outlinedTheme,
elevated: elevatedTheme,
ghost: ghostTheme,
soft: softTheme,
outlinedElevated: outlinedElevatedTheme,
}
return {
parent: themes[theme].trigger.parent,
icon: themes[theme].trigger.icon,
content: themes[theme].trigger.content,
}
}
const variant = React.useContext(Context);
const classNamesIcon = generateTriggerClassNames(variant).icon;
const classNamesParent = generateTriggerClassNames(variant).parent;
const classNamesContent = generateTriggerClassNames(variant).content;
return (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
className={cn(classNamesParent, className)}
{...props}
ref={forwardedRef}
>
<div className={classNamesContent}>
{children}
</div>
<ChevronDownIcon className={cn(classNamesIcon)} aria-hidden={true}/>
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
)
});
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({className, children, ...props}, forwardedRef) => {
const variant = React.useContext(Context);
return (
<AccordionPrimitive.Content
className={cn(variantTheme("content")({variant: variant}), className)}
{...props}
ref={forwardedRef}
>
<div className="pb-4">
{children}
</div>
</AccordionPrimitive.Content>
)
});
const Accordion = {
Root: AccordionRoot,
Item: AccordionItem,
Trigger: AccordionTrigger,
Content: AccordionContent
}
export default Accordion;
Update the configuration
Copy and paste the following code in your tailwind.config.js
file
/** @type {import('tailwindcss').Config} **/
import themer from "@tailus/themer";
module.exports = {
content : [
// ... your other paths here
"./node_modules/@tailus/themer-**/dist/**/*.{js,ts}"
],
theme: {
extend: {
colors: {
// Add your custom colors here
},
keyframes: {
slideDown: {
from: { height: 0 },
to: { height: 'var(--radix-accordion-content-height)' },
},
slideUp: {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: 0 },
},
},
animation: {
slideDown: 'slideDown 300ms cubic-bezier(0.87, 0, 0.13, 1)',
slideUp: 'slideUp 300ms cubic-bezier(0.87, 0, 0.13, 1)',
},
},
},
plugins: [
themer({
// ... themer options
})
],
};
Usage
Import the component
Import the Accordion
component in a .tsx
file
import Accordion from "tailus-ui/Accordion"
Start using
Copy and paste the following code in a .tsx
file
import Accordion from "tailus-ui/Accordion";
const items = [
// ... your questions and answers here
]
export const Default = () => {
return (
<Accordion.Root
type="single"
collapsible
className="max-w-md w-full"
>
{
items.map((item) => (
<Accordion.Item value={item.question} key={items.indexOf(item)}>
<Accordion.Trigger>
{item.question}
</Accordion.Trigger>
<Accordion.Content>
{item.answer}
</Accordion.Content>
</Accordion.Item>
))
}
</Accordion.Root>
)
}
Reference
Root
The parent container of the Accordion
component
variant
enum
default
Customization
To customize the Accordion, you can add new tailwindcss utilities to the className
attribute, or do it using the themer plugin for more consistency.
Properties
The following properties can be customized using the themer
plugin
rounded
: The border radius of the Accordion.
Using the themer plugin
You can use the themer plugin to customize the Accordion component, to do so, add the following code to your tailwind.config.js
file:
import themer from "@tailus/themer";
module.exports = {
// ... other tailwindcss config
plugins: [
themer({
// ... preconfigs
components:{
accordion: {
rounded: "none",
softBg: "200",
ghostBg:"100",
shadow: {
size: "sm",
opacity: 5,
},
dark: {
softBg: "900",
ghostBg: "900",
elevatedBg : "900",
}
},
}
})
]
}
Using the online component customizer
You can use the online component customizer to customize the Accordion component, go to the Overview section and click on the Customize
button placed in the top right corner, Or press ⌘E
After customizing, copy and paste the generated code in your tailwind.config.js
file.
Examples
Default
import Accordion from "tailus-ui/Accordion";
const items = [
// ... your questions and answers here
]
export const Default = () => {
return (
<Accordion.Root
type="single"
collapsible
className="max-w-md w-full"
>
{
items.map((item) => (
<Accordion.Item value={item.question} key={items.indexOf(item)}>
<Accordion.Trigger>
{item.question}
</Accordion.Trigger>
<Accordion.Content>
{item.answer}
</Accordion.Content>
</Accordion.Item>
))
}
</Accordion.Root>
)
}
Outlined
import Accordion from "tailus-ui/Accordion";
const items = [
... your questions and answers here
]
export const Outlined = () => {
return (
<Accordion.Root
type="single"
collapsible
variant="outlined"
className="max-w-md w-full"
>
{
items.map((item) => (
<Accordion.Item value={item.question} key={items.indexOf(item)}>
<Accordion.Trigger>
{item.question}
</Accordion.Trigger>
<Accordion.Content>
{item.answer}
</Accordion.Content>
</Accordion.Item>
))
}
</Accordion.Root>
)
}
Elevated
import Accordion from "tailus-ui/Accordion";
const items = [
// ... your questions and answers here
]
export const Elevated = () => {
return (
<Accordion.Root
type="single"
collapsible
variant="elevated"
className="max-w-md w-full"
>
{
items.map((item) => (
<Accordion.Item value={item.question} key={items.indexOf(item)}>
<Accordion.Trigger>
{item.question}
</Accordion.Trigger>
<Accordion.Content>
{item.answer}
</Accordion.Content>
</Accordion.Item>
))
}
</Accordion.Root>
)
}
Outlined Elevated
import Accordion from "tailus-ui/Accordion";
const items = [
// ... your questions and answers here
]
export const OutlinedElevated = () => {
return (
<Accordion.Root
type="single"
collapsible
variant="outlinedElevated"
className="max-w-md w-full"
>
{
items.map((item) => (
<Accordion.Item value={item.question} key={items.indexOf(item)}>
<Accordion.Trigger>
{item.question}
</Accordion.Trigger>
<Accordion.Content>
{item.answer}
</Accordion.Content>
</Accordion.Item>
))
}
</Accordion.Root>
)
}
Ghost
import Accordion from "tailus-ui/Accordion";
const items = [
// ... your questions and answers here
]
export const Ghost = () => {
return (
<Accordion.Root
type="single"
collapsible
variant="ghost"
className="max-w-md w-full"
>
{
items.map((item) => (
<Accordion.Item value={item.question} key={items.indexOf(item)}>
<Accordion.Trigger>
{item.question}
</Accordion.Trigger>
<Accordion.Content>
{item.answer}
</Accordion.Content>
</Accordion.Item>
))
}
</Accordion.Root>
)
}
Soft
import Accordion from "tailus-ui/Accordion";
const items = [
// ... your questions and answers here
]
export const Soft = () => {
return (
<Accordion.Root
type="single"
collapsible
variant="soft"
className="max-w-md w-full"
>
{
items.map((item) => (
<Accordion.Item value={item.question} key={items.indexOf(item)}>
<Accordion.Trigger>
{item.question}
</Accordion.Trigger>
<Accordion.Content>
{item.answer}
</Accordion.Content>
</Accordion.Item>
))
}
</Accordion.Root>
)
}
Custom
import Accordion from "tailus-ui/Accordion";
const items = [
// ... your questions and answers here
]
export const Custom = () => {
return (
<Accordion.Root
type="single"
collapsible
className="max-w-md w-full"
>
{
items.map((item) => (
<Accordion.Item value={item.question} key={items.indexOf(item)}>
<Accordion.Trigger>
<img className="size-8" src={item.icon} alt="tailus accordion img" loading="lazy" width={512} height={512} />
{item.question}
</Accordion.Trigger>
<Accordion.Content className="ml-12">
{item.answer}
</Accordion.Content>
</Accordion.Item>
))
}
</Accordion.Root>
)
}