Yes. It adheres to the WAI-ARIA design pattern.
Yes. It comes with default styles that matches the other components' aesthetic.
Yes. It's animated by default, but you can disable it if you prefer.
Yes. It adheres to the WAI-ARIA design pattern.
Yes. It comes with default styles that matches the other components' aesthetic.
Yes. It's animated by default, but you can disable it if you prefer.
import {
Accordion,
AccordionItem,
AccordionItemContent,
AccordionItemTrigger,
} from "@/components/ui/accordion"
export default function AccordionDemo() {
return (
<Accordion collapsible defaultValue={["React"]} className="w-full">
<AccordionItem value="item-1">
<AccordionItemTrigger>Is it accessible?</AccordionItemTrigger>
<AccordionItemContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionItemContent>
</AccordionItem>{" "}
<AccordionItem value="item-2">
<AccordionItemTrigger>Is it styled?</AccordionItemTrigger>
<AccordionItemContent>
Yes. It comes with default styles that matches the other
components' aesthetic.
</AccordionItemContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionItemTrigger>Is it animated?</AccordionItemTrigger>
<AccordionItemContent>
Yes. It's animated by default, but you can disable it if you
prefer.
</AccordionItemContent>
</AccordionItem>
</Accordion>
)
}
<script setup lang="ts">
import {
Accordion,
AccordionItem,
AccordionItemContent,
AccordionItemTrigger,
} from "@/components/ui/accordion"
</script>
<template>
<Accordion collapsible :defaultValue="['React']" class="w-full">
<AccordionItem value="item-1">
<AccordionItemTrigger>Is it accessible?</AccordionItemTrigger>
<AccordionItemContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionItemContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionItemTrigger>Is it styled?</AccordionItemTrigger>
<AccordionItemContent>
Yes. It comes with default styles that matches the other
components' aesthetic.
</AccordionItemContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionItemTrigger>Is it animated?</AccordionItemTrigger>
<AccordionItemContent>
Yes. It's animated by default, but you can disable it if you
prefer.
</AccordionItemContent>
</AccordionItem>
</Accordion>
</template>
Installation
Copy and paste the following code into your project.
"use client"
import * as React from "react"
import { Accordion as AccordionPrimitive } from "@ark-ui/react/accordion"
import { ChevronDown } from "lucide-react"
import { cn } from "@/lib/utils"
const Accordion = AccordionPrimitive.Root
const AccordionRootProvider = AccordionPrimitive.RootProvider
const AccordionContext = AccordionPrimitive.Context
const AccordionItemContext = AccordionPrimitive.ItemContext
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b", className)}
{...props}
/>
))
AccordionItem.displayName = "AccordionItem"
const AccordionItemTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.ItemTrigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.ItemTrigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.ItemTrigger
ref={ref}
className={cn(
"flex w-full flex-1 items-center justify-between py-4 font-medium transition-all hover:underline data[-state=open]:rotate-180",
className
)}
{...props}
>
{children}
<AccordionPrimitive.ItemIndicator>
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
</AccordionPrimitive.ItemIndicator>
</AccordionPrimitive.ItemTrigger>
))
const AccordionItemContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.ItemContent>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.ItemContent>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.ItemContent
ref={ref}
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn("pt-0 pb-4", className)}>{children}</div>
</AccordionPrimitive.ItemContent>
))
export {
Accordion,
AccordionContext,
AccordionItem,
AccordionItemContent,
AccordionItemContext,
AccordionItemTrigger,
AccordionRootProvider,
}
AccordionItem.vue
<script setup lang="ts">
import { cn } from "@/lib/utils"
import { type HTMLAttributes, computed } from "vue"
import { AccordionItem, type AccordionItemProps } from "@ark-ui/vue/accordion"
const props = defineProps<
AccordionItemProps & { class?: HTMLAttributes["class"] }
>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<AccordionItem
v-bind="delegatedProps"
:class="cn('border-b', props.class)"
>
<slot />
</AccordionItem>
</template>
AccordionItemContent.vue
<script setup lang="ts">
import { cn } from "@/lib/utils"
import type { HTMLAttributes } from "vue"
import {
AccordionItemContent,
type AccordionItemContentProps,
} from "@ark-ui/vue/accordion"
const props = defineProps<
AccordionItemContentProps & { class?: HTMLAttributes["class"] }
>()
</script>
<template>
<AccordionItemContent
v-bind="props"
:class="cn('overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down', props.class)"
>
<div :class="cn('pt-0 pb-4', props.class)">
<slot />
</div>
</AccordionItemContent>
</template>
AccordionItemTrigger.vue
<script setup lang="ts">
import { cn } from "@/lib/utils"
import { ChevronDown } from "lucide-vue-next"
import type { HTMLAttributes } from "vue"
import {
AccordionItemIndicator,
AccordionItemTrigger,
type AccordionItemTriggerProps,
} from "@ark-ui/vue/accordion"
const props = defineProps<
AccordionItemTriggerProps & { class?: HTMLAttributes["class"] }
>()
</script>
<template>
<AccordionItemTrigger
v-bind="props"
:class="cn('flex w-full flex-1 items-center justify-between py-4 font-medium transition-all hover:underline data[-state=open]:rotate-180', props.class)"
>
<slot />
<AccordionItemIndicator>
<ChevronDown class="h-4 w-4 shrink-0 transition-transform duration-200" />
</AccordionItemIndicator>
</AccordionItemTrigger>
</template>
index.ts
export {
AccordionRoot as Accordion,
AccordionContext,
AccordionItemContext,
} from "@ark-ui/vue/accordion"
export { default as AccordionItem } from "./AccordionItem.vue"
export { default as AccordionItemContent } from "./AccordionItemContent.vue"
export { default as AccordionItemTrigger } from "./AccordionItemTrigger.vue"
Update the import paths to match your project setup.
Update tailwind.config.js
Add the following animations to your tailwind.config.js
file:
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
keyframes: {
"collapse-down": {
from: { height: "0" },
to: { height: "var(--height)" },
},
"collapse-up": {
from: { height: "var(--height)" },
to: { height: "0" },
},
},
animation: {
"accordion-down": "collapse-down 0.2s ease-out",
"accordion-up": "collapse-up 0.2s ease-out",
},
},
},
}
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>