Usage
Use the Accordion component to display a list of collapsible items.
<script setup lang="ts">
const items = ref([
{
label: 'Is it accessible?',
content: 'Yes. It adheres to the WAI-ARIA design pattern.'
},
{
label: 'Is it unstyled?',
content: "Yes. It's unstyled by default, giving you freedom over the look and feel."
},
{
label: 'Can it be animated?',
content: 'Yes! You can use the transition prop to configure the animation.'
}
])
</script>
<template>
<PAccordion :items="items" />
</template>
Items
Use the items prop as an array of objects with the following properties:
label?: stringicon?: stringtrailingIcon?: stringcontent?: stringvalue?: stringdisabled?: booleanslot?: stringclass?: anypohon?: { item?: ClassNameValue; header?: ClassNameValue; trigger?: ClassNameValue; leadingIcon?: ClassNameValue; label?: ClassNameValue; trailingIcon?: ClassNameValue; content?: ClassNameValue; body?: ClassNameValue }
<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui'
const items = ref<PAccordionItem[]>([
{
label: 'Is it accessible?',
icon: 'i-lucide:smile',
content: 'Yes. It adheres to the WAI-ARIA design pattern.'
},
{
label: 'Is it unstyled?',
icon: 'i-lucide:swatch-book',
content: "Yes. It's unstyled by default, giving you freedom over the look and feel."
},
{
label: 'Can it be animated?',
icon: 'i-lucide:box',
content: 'Yes! You can use the transition prop to configure the animation.'
}
])
</script>
<template>
<PAccordion :items="items" />
</template>
Multiple
Set the type prop to multiple to allow multiple items to be active at the same time. Defaults to single.
<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui'
const items = ref<PAccordionItem[]>([
{
label: 'Is it accessible?',
icon: 'i-lucide:smile',
content: 'Yes. It adheres to the WAI-ARIA design pattern.'
},
{
label: 'Is it unstyled?',
icon: 'i-lucide:swatch-book',
content: "Yes. It's unstyled by default, giving you freedom over the look and feel."
},
{
label: 'Can it be animated?',
icon: 'i-lucide:box',
content: 'Yes! You can use the transition prop to configure the animation.'
}
])
</script>
<template>
<PAccordion type="multiple" :items="items" />
</template>
Collapsible
When type is single, you can set the collapsible prop to false to prevent the active item from collapsing.
<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui'
const items = ref<PAccordionItem[]>([
{
label: 'Is it accessible?',
icon: 'i-lucide:smile',
content: 'Yes. It adheres to the WAI-ARIA design pattern.'
},
{
label: 'Is it unstyled?',
icon: 'i-lucide:swatch-book',
content: "Yes. It's unstyled by default, giving you freedom over the look and feel."
},
{
label: 'Can it be animated?',
icon: 'i-lucide:box',
content: 'Yes! You can use the transition prop to configure the animation.'
}
])
</script>
<template>
<PAccordion :collapsible="false" :items="items" />
</template>
Unmount
Use the unmount-on-hide prop to prevent the content from being unmounted when the accordion is collapsed. Defaults to true.
<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui'
const items = ref<PAccordionItem[]>([
{
label: 'Is it accessible?',
icon: 'i-lucide:smile',
content: 'Yes. It adheres to the WAI-ARIA design pattern.'
},
{
label: 'Is it unstyled?',
icon: 'i-lucide:swatch-book',
content: "Yes. It's unstyled by default, giving you freedom over the look and feel."
},
{
label: 'Can it be animated?',
icon: 'i-lucide:box',
content: 'Yes! You can use the transition prop to configure the animation.'
}
])
</script>
<template>
<PAccordion :unmount-on-hide="false" :items="items" />
</template>
Disabled
Use the disabled property to disable the Accordion.
You can also disable a specific item by using the disabled property in the item object.
<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui'
const items = ref<PAccordionItem[]>([
{
label: 'Is it accessible?',
icon: 'i-lucide:smile',
content: 'Yes. It adheres to the WAI-ARIA design pattern.'
},
{
label: 'Is it unstyled?',
icon: 'i-lucide:swatch-book',
content: "Yes. It's unstyled by default, giving you freedom over the look and feel."
},
{
label: 'Can it be animated?',
icon: 'i-lucide:box',
content: 'Yes! You can use the transition prop to configure the animation.'
}
])
</script>
<template>
<PAccordion disabled :items="items" />
</template>
Trailing Icon
Use the trailing-icon prop to customize the trailing Icon of each item. Defaults to i-lucide:chevron-down.
trailingIcon property in the item object.<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui'
const items = ref<PAccordionItem[]>([
{
label: 'Is it accessible?',
icon: 'i-lucide:smile',
content: 'Yes. It adheres to the WAI-ARIA design pattern.'
},
{
label: 'Is it unstyled?',
icon: 'i-lucide:swatch-book',
content: "Yes. It's unstyled by default, giving you freedom over the look and feel."
},
{
label: 'Can it be animated?',
icon: 'i-lucide:box',
content: 'Yes! You can use the transition prop to configure the animation.'
}
])
</script>
<template>
<PAccordion trailing-icon="i-lucide:arrow-down" :items="items" />
</template>
Examples
Control active item(s)
You can control the active item by using the default-value prop or the v-model directive with the value of the item. If no value is provided, it defaults to the index as a string.
<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui';
import { onMounted, ref } from 'vue';
const items: Array<PAccordionItem> = [
{
label: 'Is it accessible?',
icon: 'i-lucide:smile',
content: 'Yes. It adheres to the WAI-ARIA design pattern.',
},
{
label: 'Is it unstyled?',
icon: 'i-lucide:swatch-book',
content: 'Yes. It\'s unstyled by default, giving you freedom over the look and feel.',
},
{
label: 'Can it be animated?',
icon: 'i-lucide:box',
content: 'Yes! You can use the transition prop to configure the animation.',
},
];
const active = ref('0');
// !Warning: This is for demonstration purposes only. Don't do this at home.
onMounted(() => {
setInterval(() => {
active.value = String((Number(active.value) + 1) % items.length);
}, 2000);
});
</script>
<template>
<PAccordion
v-model="active"
:items="items"
/>
</template>
type="multiple", ensure to pass an array to the default-value prop or the v-model directive.With drag and drop
Use the useSortable composable from @vueuse/integrations to enable drag and drop functionality on the Accordion. This integration wraps Sortable.js to provide a seamless drag and drop experience.
<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui';
import { useSortable } from '@vueuse/integrations/useSortable';
import { shallowRef, useTemplateRef } from 'vue';
const items = shallowRef<Array<PAccordionItem>>([
{
label: 'Is it accessible?',
icon: 'i-lucide:smile',
content: 'Yes. It adheres to the WAI-ARIA design pattern.',
},
{
label: 'Is it unstyled?',
icon: 'i-lucide:swatch-book',
content: 'Yes. It\'s unstyled by default, giving you freedom over the look and feel.',
},
{
label: 'Can it be animated?',
icon: 'i-lucide:box',
content: 'Yes! You can use the transition prop to configure the animation.',
},
]);
const accordion = useTemplateRef<HTMLElement>('accordion');
useSortable(accordion, items, {
animation: 150,
});
</script>
<template>
<PAccordion
ref="accordion"
:items="items"
/>
</template>
With body slot
Use the #body slot to customize the body of each item.
<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui';
const items: Array<PAccordionItem> = [
{
label: 'Icons',
icon: 'i-lucide:smile',
},
{
label: 'Colors',
icon: 'i-lucide:swatch-book',
},
{
label: 'Components',
icon: 'i-lucide:box',
},
];
</script>
<template>
<PAccordion :items="items">
<template #body="{ item }">
This is the {{ item.label }} panel.
</template>
</PAccordion>
</template>
#body slot includes some pre-defined styles, use the #content slot if you want to start from scratch.With content slot
Use the #content slot to customize the content of each item.
<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui';
const items: Array<PAccordionItem> = [
{
label: 'Icons',
icon: 'i-lucide:smile',
},
{
label: 'Colors',
icon: 'i-lucide:swatch-book',
},
{
label: 'Components',
icon: 'i-lucide:box',
},
];
</script>
<template>
<PAccordion :items="items">
<template #content="{ item }">
<p class="text-sm color-secondary pb-3.5">
This is the {{ item.label }} panel.
</p>
</template>
</PAccordion>
</template>
With custom slot
Use the slot property to customize a specific item.
You will have access to the following slots:
#{{ item.slot }}#{{ item.slot }}-body
<script setup lang="ts">
import type { PAccordionItem } from 'pohon-ui';
const items = [
{
label: 'Is it accessible?',
icon: 'i-lucide:smile',
content: 'Yes. It adheres to the WAI-ARIA design pattern.',
},
{
label: 'Is it unstyled?',
icon: 'i-lucide:swatch-book',
slot: 'colors' as const,
content: 'Yes. It\'s unstyled by default, giving you freedom over the look and feel.',
},
{
label: 'Can it be animated?',
icon: 'i-lucide:box',
content: 'Yes! You can use the transition prop to configure the animation.',
},
] satisfies Array<PAccordionItem>;
</script>
<template>
<PAccordion :items="items">
<template #colors="{ item }">
<p class="text-sm text-primary pb-3.5">
{{ item.content }}
</p>
</template>
</PAccordion>
</template>
With markdown content
You can use the MDC component from @nuxtjs/mdc to render markdown in the accordion items.
<script setup lang="ts">
const items = [
{
label: 'Is Pohon UI free to use?',
icon: 'i-lucide:circle-help',
content: 'Yes! Pohon UI is completely free and open source under the MIT license. All 100+ components are available to everyone.',
},
{
label: 'Can I use Pohon UI with Vue without Nuxt?',
icon: 'i-lucide:circle-help',
content: 'Yes! While optimized for Nuxt, Pohon UI works perfectly with standalone Vue projects via our Vite plugin. You can follow the [installation guide](/docs/pohon/getting-started/installation/vue) to get started.',
},
{
label: 'Will Pohon UI work with other CSS frameworks like Tailwind CSS?',
icon: 'i-lucide:circle-help',
content: 'No. Pohon UI is designed exclusively for UnoCSS. Tailwind CSS support would require significant architecture changes due to different class naming conventions.',
},
{
label: 'How does Pohon UI handle accessibility?',
icon: 'i-lucide:circle-help',
content: 'Through [Akar](/docs/akar/overview/accessibility) integration, Pohon UI provides automatic ARIA attributes, keyboard navigation, focus management, and screen reader support. While offering a strong foundation, testing in your specific use case remains important.',
},
{
label: 'How is Pohon UI tested?',
icon: 'i-lucide:circle-help',
content: 'Pohon UI ensures reliability with 1000+ Vitest tests covering core functionality and accessibility.',
},
{
label: 'Is Pohon UI production-ready?',
icon: 'i-lucide:circle-help',
content: 'Yes! Pohon UI is used in production by thousands of applications with extensive tests, regular updates, and active maintenance.',
},
];
</script>
<template>
<PAccordion
type="multiple"
:items="items"
:unmount-on-hide="false"
:default-value="['3']"
:pohon="{
trigger: 'text-base',
body: 'text-base color-text-muted',
}"
>
<template #body="{ item }">
<MDC
:value="item.content"
unwrap="p"
/>
</template>
</PAccordion>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
as | 'div' | anyThe element or component this component should render as. |
items | T[] | |
trailingIcon | appConfig.pohon.icons.chevronDown | string | objectThe icon displayed on the right side of the trigger. |
labelKey | 'label' | keyof Extract<NestedItem<T>, object> & string | DotPathKeys<Extract<NestedItem<T>, object>>The key used to get the label from the item.
|
defaultValue | string | string[]The default active value of the item(s). Use when you do not need to control the state of the item(s). | |
modelValue | string | string[]The controlled value of the active item(s). Use this when you need to control the state of the items. Can be binded with | |
type | 'single' | "multiple" | "single"Determines whether a "single" or "multiple" items can be pressed at a time. This prop will overwrite the inferred type from |
collapsible | true | booleanWhen type is "single", allows closing content when clicking trigger for an open item. When type is "multiple", this prop has no effect. |
disabled | false | booleanWhen |
unmountOnHide | true | booleanWhen |
pohon | { root?: ClassValue; item?: ClassValue; header?: ClassValue; trigger?: ClassValue; content?: ClassValue; body?: ClassValue; leadingIcon?: ClassValue; trailingIcon?: ClassValue; label?: ClassValue; } |
Emits
| Event | Type |
|---|---|
update:modelValue | [value: string | string[]] |
Slots
| Slot | Type |
|---|---|
leading | { item: T; index: number; open: boolean; pohon: object; } |
default | { item: T; index: number; open: boolean; } |
trailing | { item: T; index: number; open: boolean; pohon: object; } |
content | { item: T; index: number; open: boolean; pohon: object; } |
body | { item: T; index: number; open: boolean; pohon: object; } |
Theme
Below is the theme configuration skeleton for the PAccordion. Since the component is provided unstyled by default, you will need to fill in these values to apply your own custom look and feel. If you prefer to use our pre-built, opinionated styling, you can instead use our UnoCSS preset, this docs is using it as well.
export default defineAppConfig({
pohon: {
accordion: {
slots: {
root: '',
item: '',
header: '',
trigger: '',
content: '',
body: '',
leadingIcon: '',
trailingIcon: '',
label: ''
},
variants: {
disabled: {
true: {
trigger: ''
}
}
},
compoundVariants: []
}
}
};
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import pohon from 'pohon-ui/vite'
export default defineAppConfig({
pohon: {
accordion: {
slots: {
root: '',
item: '',
header: '',
trigger: '',
content: '',
body: '',
leadingIcon: '',
trailingIcon: '',
label: ''
},
variants: {
disabled: {
true: {
trigger: ''
}
}
},
compoundVariants: []
}
}
};
Anatomy
Here is the anatomy of themeable parts of the Accordion component:
Coming soon...
Akar
With Pohon UI, you can achieve similar component functionality with less code and effort, as it comes with built-in styles mechanism and behaviors that are optimized for common use cases. Since it's using unocss-variants it adds a runtime cost, but it can be worth it if you prioritize development speed and ease of use over fine-grained control.
If this is a deal breaker for you, you can always stick to using Akar and build your own custom components on top of it.