UIUI
Accordion
A vertically stacked set of interactive headings that each reveal an associated section of content.
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
export function AccordionDemo() {
return (
<Accordion type="single" collapsible className="w-full max-w-2xl mx-auto">
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern and includes full keyboard
navigation support.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Is it styled?</AccordionTrigger>
<AccordionContent>
Yes. It comes with default styles that match the design system, but you
can customize them to fit your needs.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Is it animated?</AccordionTrigger>
<AccordionContent>
Yes. It uses smooth CSS animations for expanding and collapsing that
enhance the user experience.
</AccordionContent>
</AccordionItem>
</Accordion>
);
}
Installation
Install following dependencies:
npm install @radix-ui/react-accordion motion class-variance-authority lucide-react
Add required animation and keyframes to your CSS file or tailwind config file based on your Tailwind version.
@theme {
--animate-accordion-down: accordion-down 0.3s ease-out;
--animate-accordion-up: accordion-up 0.3s ease-out;
}
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
module.exports = {
theme: {
extend: {
keyframes: {
accordion-down: {
from: {
height: 0,
},
to: {
height: var(--radix-accordion-content-height),
},
},
accordion-up: {
from: {
height: var(--radix-accordion-content-height),
},
to: {
height: 0,
},
},
}
animations: {
"accordion-down": "accordion-down 0.3s ease-out",
"accordion-up": "accordion-up 0.3s ease-out",
},
}
}
}
Copy and paste the following code into your project.
"use client";
import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { ChevronDown } from "lucide-react";
const accordionVariants = cva("w-full max-w-ele", {
variants: {
variant: {
default:
"border border-border rounded-ele overflow-hidden ",
ghost: "",
outline:
"border border-border rounded-ele ",
},
size: {
sm: "text-sm max-w-lg",
default: "max-w-2xl",
lg: "text-lg max-w-4xl",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
});
const accordionItemVariants = cva(
"border-b border-border last:border-b-0",
{
variants: {
variant: {
default: "",
ghost:
"border-b border-border last:border-b-0 mb-2 last:mb-0",
outline: "",
},
},
defaultVariants: {
variant: "default",
},
}
);
const accordionTriggerVariants = cva(
"flex flex-1 items-center justify-between py-4 px-6 text-left font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group",
{
variants: {
variant: {
default: "",
ghost: "px-0",
outline: "",
},
size: {
sm: "py-3 px-4 text-sm",
default: "py-4 px-6",
lg: "py-5 px-6 text-lg",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
const accordionContentVariants = cva(
"px-6 pb-4 pt-0 text-muted-foreground",
{
variants: {
variant: {
default: "",
ghost: "px-0",
outline: "",
},
size: {
sm: "px-4 pb-3 text-sm",
default: "px-6 pb-4",
lg: "px-6 pb-5",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
export interface AccordionProps extends VariantProps<typeof accordionVariants> {
className?: string;
children?: React.ReactNode;
}
// Single accordion props
export interface AccordionSingleProps extends AccordionProps {
type: "single";
collapsible?: boolean;
defaultValue?: string;
value?: string;
onValueChange?: (value: string) => void;
}
// Multiple accordion props
export interface AccordionMultipleProps extends AccordionProps {
type: "multiple";
defaultValue?: string[];
value?: string[];
onValueChange?: (value: string[]) => void;
}
export type AccordionCombinedProps =
| AccordionSingleProps
| AccordionMultipleProps;
export interface AccordionItemProps
extends VariantProps<typeof accordionItemVariants> {
value: string;
disabled?: boolean;
className?: string;
children?: React.ReactNode;
}
export interface AccordionTriggerProps
extends VariantProps<typeof accordionTriggerVariants> {
icon?: React.ReactNode;
hideChevron?: boolean;
className?: string;
children?: React.ReactNode;
onClick?: () => void;
}
export interface AccordionContentProps
extends VariantProps<typeof accordionContentVariants> {
className?: string;
children?: React.ReactNode;
}
const Accordion = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Root>,
AccordionCombinedProps
>(({ className, variant, size, children, ...props }, ref) => (
<AccordionPrimitive.Root
ref={ref}
className={cn(accordionVariants({ variant, size }), className)}
{...props}
>
{children}
</AccordionPrimitive.Root>
));
Accordion.displayName = "Accordion";
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
AccordionItemProps
>(({ className, variant, children, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn(accordionItemVariants({ variant }), className)}
{...props}
>
{children}
</AccordionPrimitive.Item>
));
AccordionItem.displayName = "AccordionItem";
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
AccordionTriggerProps
>(
(
{ className, children, variant, size, icon, hideChevron = false, ...props },
ref
) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(accordionTriggerVariants({ variant, size }), className)}
{...props}
>
<div className="flex items-center gap-2">
{icon && <span className="shrink-0">{icon}</span>}
<span className="text-left group-hover:underline transition-all duration-200">
{children}
</span>
</div>
{!hideChevron && (
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200 ease-in-out group-data-[state=open]:rotate-180" />
)}
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
)
);
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
AccordionContentProps
>(({ className, children, variant, size, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn(accordionContentVariants({ variant, size }), className)}>
{children}
</div>
</AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
npx hextaui@latest add accordion
Usage
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
</Accordion>
Props
Accordion
Prop | Type | Default |
---|---|---|
type? | "single" | "multiple" | undefined |
collapsible? | boolean | false |
defaultValue? | string | undefined |
defaultValueArray? | string[] | undefined |
value? | string | undefined |
valueArray? | string[] | undefined |
onValueChange? | function | undefined |
variant? | "default" | "ghost" | "outline" | "default" |
size? | "sm" | "default" | "lg" | "default" |
className? | string | undefined |
AccordionItem
Prop | Type | Default |
---|---|---|
value? | string | undefined |
disabled? | boolean | false |
variant? | "default" | "ghost" | "outline" | "default" |
className? | string | undefined |
AccordionTrigger
Prop | Type | Default |
---|---|---|
icon? | ReactNode | undefined |
hideChevron? | boolean | false |
variant? | "default" | "ghost" | "outline" | "default" |
size? | "sm" | "default" | "lg" | "default" |
className? | string | undefined |
children? | ReactNode | undefined |
AccordionContent
Prop | Type | Default |
---|---|---|
variant? | "default" | "ghost" | "outline" | "default" |
size? | "sm" | "default" | "lg" | "default" |
className? | string | undefined |
children? | ReactNode | undefined |
Edit on GitHub
Last updated on