Usage
Use the Accordion component to display a list of collapsible items.
<script setup lang="ts">
import type { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Is Pohon UI free to use?',
content: 'Yes! Pohon UI is completely free and open source under the MIT license. All 125+ components are available to everyone.'
},
{
label: 'Can I use Pohon UI with Vue without Nuxt?',
content: 'Yes! While optimized for Nuxt, Pohon UI works perfectly with standalone Vue projects via our Vite plugin. You can follow the [installation guide](/pohon/getting-started/installation/vue) to get started.'
},
{
label: 'Is Pohon UI production-ready?',
content: 'Pohon UI is designed for production Vue applications and maintained with component tests and regular updates.'
}
])
</script>
<template>
<PAccordion :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Is Pohon UI free to use?',
content: 'Yes! Pohon UI is completely free and open source under the MIT license. All 125+ components are available to everyone.'
},
{
label: 'Can I use Pohon UI with Vue without Nuxt?',
content: 'Yes! While optimized for Nuxt, Pohon UI works perfectly with standalone Vue projects via our Vite plugin. You can follow the [installation guide](/pohon/getting-started/installation/vue) to get started.'
},
{
label: 'Is Pohon UI production-ready?',
content: 'Pohon UI is designed for production Vue applications and maintained with component tests and regular updates.'
}
])
</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?: anyui?: { item?: ClassNameValue, header?: ClassNameValue, trigger?: ClassNameValue, leadingIcon?: ClassNameValue, label?: ClassNameValue, trailingIcon?: ClassNameValue, content?: ClassNameValue, body?: ClassNameValue }
<script setup lang="ts">
import type { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.'
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</script>
<template>
<PAccordion :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.'
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</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 { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.'
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</script>
<template>
<PAccordion type="multiple" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.'
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</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 { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.'
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</script>
<template>
<PAccordion :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.'
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</script>
<template>
<PAccordion :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 { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.'
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</script>
<template>
<PAccordion :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.'
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</script>
<template>
<PAccordion :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 { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.',
disabled: true
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</script>
<template>
<PAccordion disabled :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.',
disabled: true
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</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 { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.',
trailingIcon: 'i-lucide-plus'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.'
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</script>
<template>
<PAccordion trailing-icon="i-lucide-arrow-down" :items="items" />
</template><script setup lang="ts">
import { ref } from 'vue'
import type { AccordionItem } from 'pohon-ui'
const items = ref<AccordionItem[]>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.',
trailingIcon: 'i-lucide-plus'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.'
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
])
</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 { AccordionItem } from 'pohon-ui';
const items: Array<AccordionItem> = [
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.',
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.',
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.',
},
];
const active = ref('0');
// Note: 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>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import type { AccordionItem } from 'pohon-ui';
const items: Array<AccordionItem> = [
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.',
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.',
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.',
},
];
const active = ref('0');
// Note: 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>
value-key prop to change the key used to match items when a v-model or default-value is provided.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 { AccordionItem } from 'pohon-ui';
import { useSortable } from '@vueuse/integrations/useSortable';
const items = shallowRef<Array<AccordionItem>>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.',
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.',
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.',
},
]);
const accordion = useTemplateRef<HTMLElement>('accordion');
useSortable(accordion, items, {
animation: 150,
});
</script>
<template>
<PAccordion
ref="accordion"
:items="items"
/>
</template>
<script setup lang="ts">
import { shallowRef, useTemplateRef } from 'vue'
import type { AccordionItem } from 'pohon-ui';
import { useSortable } from '@vueuse/integrations/useSortable';
const items = shallowRef<Array<AccordionItem>>([
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.',
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
content: 'Choose a primary and a neutral color from your UnoCSS theme.',
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.',
},
]);
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 { AccordionItem } from 'pohon-ui';
const items: Array<AccordionItem> = [
{
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 { AccordionItem } from 'pohon-ui';
const items: Array<AccordionItem> = [
{
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-text-muted 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 { AccordionItem } from 'pohon-ui';
const items = [
{
label: 'Icons',
icon: 'i-lucide-smile',
content: 'You have nothing to do, @nuxt/icon will handle it automatically.',
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
slot: 'colors' as const,
content: 'Choose a primary and a neutral color from your UnoCSS theme.',
},
{
label: 'Components',
icon: 'i-lucide-box',
content: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.',
},
] satisfies Array<AccordionItem>;
</script>
<template>
<PAccordion :items="items">
<template #colors="{ item }">
<p class="text-sm color-primary pb-3.5">
{{ item.content }}
</p>
</template>
</PAccordion>
</template>
With markdown content
You can use the Markdown component from @comark/vue to render markdown in the accordion items.
<script setup lang="ts">
import { Markdown } from '@comark/vue';
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 125+ 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](/pohon/getting-started/installation/vue) to get started.',
},
{
label: 'Will Pohon UI work with other CSS frameworks like UnoCSS?',
icon: 'i-lucide-circle-help',
content: 'No. Pohon UI is designed exclusively for UnoCSS. UnoCSS 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](https://akar.vinicunca.dev/docs/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 has component tests covering core behavior and accessibility.',
},
{
label: 'Is Pohon UI production-ready?',
icon: 'i-lucide-circle-help',
content: 'Pohon UI is designed for production Vue applications and maintained with component tests and regular updates.',
},
];
</script>
<template>
<PAccordion
type="multiple"
:items="items"
:unmount-on-hide="false"
:default-value="['3']"
:ui="{
trigger: 'text-base',
body: 'text-base color-text-muted',
}"
>
<template #body="{ item }">
<Markdown
:value="item.content"
unwrap="p"
/>
</template>
</PAccordion>
</template>
API
Props
| Prop | Default | Type |
|---|
Slots
| Slot | Type |
|---|
Emits
| Event | Type |
|---|
Theme
export default defineAppConfig({
ui: {
accordion: {
slots: {
root: 'w-full',
item: 'border-b border-border last:border-b-0',
header: 'flex',
trigger: 'group flex-1 flex items-center gap-1.5 font-500 text-sm py-3.5 outline-primary/25 focus-visible:outline-3 min-w-0 rounded-md',
content: 'focus:outline-none data-[state=closed]:overflow-hidden data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
body: 'text-sm pb-3.5',
leadingIcon: 'shrink-0 size-5',
trailingIcon: 'ms-auto shrink-0 size-5 transition-transform-280 ease-out group-data-[state=open]:rotate-180 motion-reduce:transition-none',
label: 'text-start break-words'
},
variants: {
disabled: {
true: {
trigger: 'cursor-not-allowed opacity-75'
}
}
}
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from 'pohon-ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
accordion: {
slots: {
root: 'w-full',
item: 'border-b border-border last:border-b-0',
header: 'flex',
trigger: 'group flex-1 flex items-center gap-1.5 font-500 text-sm py-3.5 outline-primary/25 focus-visible:outline-3 min-w-0 rounded-md',
content: 'focus:outline-none data-[state=closed]:overflow-hidden data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
body: 'text-sm pb-3.5',
leadingIcon: 'shrink-0 size-5',
trailingIcon: 'ms-auto shrink-0 size-5 transition-transform-280 ease-out group-data-[state=open]:rotate-180 motion-reduce:transition-none',
label: 'text-start break-words'
},
variants: {
disabled: {
true: {
trigger: 'cursor-not-allowed opacity-75'
}
}
}
}
}
})
]
})