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 AccordionContext = AccordionPrimitive.Context
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
AccordionPrimitive.ItemProps
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b last:border-b-0", className)}
{...props}
/>
))
const AccordionItemContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.ItemContent>,
AccordionPrimitive.ItemContentProps
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.ItemContent
ref={ref}
className="overflow-hidden text-sm 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>
))
const AccordionItemContext = AccordionPrimitive.ItemContext
const AccordionItemTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.ItemTrigger>,
AccordionPrimitive.ItemTriggerProps
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.ItemTrigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between gap-4 rounded-md py-4 text-left font-semibold text-sm outline-none transition-all hover:underline focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}
>
{children}
<AccordionPrimitive.ItemIndicator>
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
</AccordionPrimitive.ItemIndicator>
</AccordionPrimitive.ItemTrigger>
))
const AccordionProvider = AccordionPrimitive.RootProvider
export {
Accordion,
AccordionContext,
AccordionItem,
AccordionItemContent,
AccordionItemContext,
AccordionItemTrigger,
AccordionProvider,
}
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>