Accordion
A collapsible content panel that can be used to show and hide content in an organized way. Content padding to be customized by the dev.
Default
A basic accordion where expanding one panel collapses the others.
<template>
<HLAccordion>
<HLAccordionItem title="Title 1" name="1" id="1">
<div class="p-2">Content for section 1</div>
</HLAccordionItem>
<HLAccordionItem title="Title 2" name="2" id="2">
<div class="p-2">Content for section 2</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
</script>Sizes
Use size to render the accordion in small, medium (default), or large dimensions.
<template>
<!-- Small Size -->
<HLAccordion size="sm">
<HLAccordionItem title="Small Size" name="1" id="1">
<div class="p-2">Small accordion content</div>
</HLAccordionItem>
</HLAccordion>
<!-- Medium Size (default) -->
<HLAccordion size="md">
<HLAccordionItem title="Medium Size" name="2" id="2">
<div class="p-2">Medium accordion content</div>
</HLAccordionItem>
</HLAccordion>
<!-- Large Size -->
<HLAccordion size="lg">
<HLAccordionItem title="Large Size" name="3" id="3">
<div class="p-2">Large accordion content</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
</script>Arrow Placement
Use arrowPlacement to position the expand/collapse arrow on the left or right.
<template>
<!-- Left Arrow -->
<HLAccordion arrowPlacement="left">
<HLAccordionItem title="Left Arrow" name="1" id="1">
<div class="p-2">Content with left arrow</div>
</HLAccordionItem>
</HLAccordion>
<!-- Right Arrow -->
<HLAccordion arrowPlacement="right">
<HLAccordionItem title="Right Arrow" name="2" id="2">
<div class="p-2">Content with right arrow</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
</script>Border Styles
Use borderPosition to draw the border around the whole accordion (default) or around each item (item).
<template>
<!-- Default Border -->
<HLAccordion borderPosition="default">
<HLAccordionItem title="Default Border" name="1" id="1">
<div class="p-2">Content with default border - item 1</div>
</HLAccordionItem>
<HLAccordionItem title="Default Border" name="2" id="2">
<div class="p-2">Content with default border - item 2</div>
</HLAccordionItem>
</HLAccordion>
<!-- Item Border -->
<HLAccordion borderPosition="item">
<HLAccordionItem title="Item Border" name="2" id="2">
<div class="p-2">Content with item border - item 1</div>
</HLAccordionItem>
<HLAccordionItem title="Item Border" name="3" id="3">
<div class="p-2">Content with item border - item 2</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
</script>No Border
Set :border="false" to render the accordion without any borders.
<template>
<HLAccordion :border="false">
<HLAccordionItem title="No Border" name="1" id="1">
<div class="p-2">This item has no border</div>
</HLAccordionItem>
<HLAccordionItem title="No Border 2" name="2" id="2">
<div class="p-2">This item has no border</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
</script>Zero Padding
Set zeroPadding on the accordion to strip the built-in header and content padding. The component intentionally leaves spacing to you in this mode, so wrap your content (and any custom header) in your own padded container to control the layout — handy when a panel holds an edge-to-edge element such as a table, list, or image.
<template>
<!-- Without your own padding the content sits flush against the edges -->
<HLAccordion zeroPadding :defaultExpandedNames="['flush']">
<HLAccordionItem title="Flush content" name="flush" id="1">
<div>Content sits flush — no built-in padding</div>
</HLAccordionItem>
</HLAccordion>
<!-- Add your own wrapper to control the spacing -->
<HLAccordion zeroPadding :defaultExpandedNames="['custom']">
<HLAccordionItem title="Custom padding" name="custom" id="2">
<div class="px-4 py-3">Content with developer-controlled padding</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
</script>Disabled
Set disabled on an item to prevent it from being expanded.
<template>
<HLAccordion>
<HLAccordionItem title="Enabled Item" name="1" id="1">
<div>This item can be expanded</div>
</HLAccordionItem>
<HLAccordionItem title="Disabled Item" name="2" id="2" :disabled="true">
<div>This item cannot be expanded</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
</script>Hover Effect
hoverEffect is set on each HLAccordionItem and highlights the item's header background on hover. It is enabled by default (true); set :hoverEffect="false" to turn the highlight off.
<template>
<HLAccordion>
<!-- hoverEffect defaults to true -->
<HLAccordionItem title="Hover me (effect on)" name="1" id="hover-on">
<div class="p-2">Header highlights on hover — the default.</div>
</HLAccordionItem>
<HLAccordionItem title="Hover me (effect off)" name="2" id="hover-off" :hoverEffect="false">
<div class="p-2">No hover highlight on this item.</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
</script>Multiple Open Panels
Setting the accordion prop to false will allow opening multiple panels at the same time.
<template>
<HLAccordion id="multiple-panels-open" :accordion="false">
<HLAccordionItem title="Expandable Item" name="1" id="1">
<div class="p-2">This section can be expanded</div>
</HLAccordionItem>
<HLAccordionItem title="Expandable Item" name="2" id="2">
<div class="p-2">This section can be expanded irrespective of the other sections</div>
</HLAccordionItem>
<HLAccordionItem title="Expandable Item" name="3" id="3">
<div class="p-2">This section can be expanded irrespective of the other sections</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
</script>Pre-expanded Panels
Use defaultExpandedNames to set which panels start open on first render.
defaultExpandedNames vs expandedNames
defaultExpandedNames— uncontrolled. Sets the initial open panels only; the user can freely collapse and expand them afterwards. The accordion manages its own state from then on.expandedNames— controlled. Forces the listed panels open and keeps them open — the user cannot collapse them unless you update the bound value yourself. Use this when the expanded state is driven by your own application state.
Pass one or the other, not both.
<template>
<!-- Single pre-expanded panel -->
<HLAccordion :defaultExpandedNames="['first']">
<HLAccordionItem title="Pre-expanded Item" name="first" id="1">
<div class="p-2">This section starts expanded</div>
</HLAccordionItem>
<HLAccordionItem title="Collapsed Item" name="second" id="2">
<div class="p-2">This section starts collapsed</div>
</HLAccordionItem>
</HLAccordion>
<!-- Multiple pre-expanded panels -->
<HLAccordion :defaultExpandedNames="['first', 'second']" :accordion="false">
<HLAccordionItem title="Pre-expanded Item 1" name="first" id="3">
<div class="p-2">This section starts expanded</div>
</HLAccordionItem>
<HLAccordionItem title="Pre-expanded Item 2" name="second" id="4">
<div class="p-2">This section also starts expanded</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
</script>Controlled State
When you drive the expanded panels from your own reactive state, pass expandedNames (controlled) and handle @item-header-click to update it. On its own expandedNames forces the listed panels open and the user cannot collapse them — the click handler is what lets a click toggle the state.
Open panels: 1
<template>
<HLAccordion
:accordion="false"
:expandedNames="expandedNames"
@item-header-click="handleToggle"
>
<HLAccordionItem title="Section 1" name="1" id="ctrl-1">
<div class="p-2">Content for section 1</div>
</HLAccordionItem>
<HLAccordionItem title="Section 2" name="2" id="ctrl-2">
<div class="p-2">Content for section 2</div>
</HLAccordionItem>
<HLAccordionItem title="Section 3" name="3" id="ctrl-3">
<div class="p-2">Content for section 3</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
const expandedNames = ref<Array<string | number>>(['1'])
// Toggle the clicked panel in our own state.
// Without this handler the panels stay locked open.
const handleToggle = ({ name }: { name: string | number }) => {
expandedNames.value = expandedNames.value.includes(name)
? expandedNames.value.filter(n => n !== name)
: [...expandedNames.value, name]
}
</script>Dynamic Items
Render items from an array with v-for. Give each item a stable, unique :key, :id, and :name — name is the value that shows up in @item-header-click and is matched against expandedNames / defaultExpandedNames, so it must be unique within the accordion.
<template>
<HLAccordion :accordion="false">
<HLAccordionItem
v-for="section in sections"
:key="section.key"
:id="`section-${section.key}`"
:name="section.key"
:title="section.title"
>
<div class="p-2">{{ section.body }}</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
const sections = ref([
{ key: 'general', title: 'General', body: 'General settings live here.' },
{ key: 'notifications', title: 'Notifications', body: 'Notification preferences live here.' },
{ key: 'billing', title: 'Billing', body: 'Billing details live here.' },
])
</script>Custom Title
Both slots used here belong to HLAccordionItem, not HLAccordion. Use the item's header slot to replace the plain title prop with custom markup — headings, icons, badges, or any component — and the item's header-extra slot to render content beside the expand/collapse arrow (for example a status tag or a secondary action). When you provide a header slot, it takes the place of the title prop, so you don't need to pass title as well.
Header slot
<template>
<HLAccordion>
<HLAccordionItem name="1" id="1">
<template #header>
<div class="p-2 bg-green-50 w-full">
<HLText size="xl" weight="semibold">Header slot</HLText>
</div>
</template>
<template #header-extra>
<div class="p-2 bg-gray-50 w-full">Header Extra</div>
</template>
<div class="p-2">Content for section 1</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem, HLText } from '@platform-ui/highrise'
</script>Custom Content
The panel body is the default slot of HLAccordionItem — the content shown when that item is expanded. It accepts any markup, so a panel can hold rich content like images, text, and actions. Note this is different from the default slot of HLAccordion, which holds the HLAccordionItem children themselves: the accordion's default slot defines which panels exist, while each item's default slot defines what that panel contains.
<template>
<!-- Custom content -->
<HLAccordion>
<HLAccordionItem title="Rich Content Example" name="2" id="2">
<div class="p-2">
<div class="flex gap-4">
<div class="w-[100px] h-[100px] bg-[#D9D9D9] rounded"></div>
<div>
<HLText size="lg" weight="semibold">Content Title</HLText>
<p>This is an example of rich content with image placeholder and text.</p>
<HLButton color="blue" variant="primary" size="sm">Action Button</HLButton>
</div>
</div>
</div>
</HLAccordionItem>
</HLAccordion>
</template>
<script setup lang="ts">
import { HLAccordion, HLAccordionItem, HLText, HLButton } from '@platform-ui/highrise'
</script>Display Directive
The displayDirective property specifies which Vue directive to use for controlling the visibility of the accordion's content. This choice affects how the content is managed in the DOM, impacting both performance and behavior.
if - When the accordion is collapsed, the content is completely removed from the DOM. It is only added back when the accordion is expanded. This approach is more performant when the content is not needed initially.
show - The content remains in the DOM at all times, but it is hidden from view when the accordion is collapsed. The visibility is toggled using CSS. This approach is more flexible and easier to manage when the content needs to be toggled on and off.
Event Testing
This example shows handling of the @item-header-click event. Click on the header of an accordion item to trigger the @item-header-click event.
Event Log:
<script setup>
import { ref } from 'vue'
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'
const eventLog = ref([])
const handleHeaderClick = item => {
eventLog.value.unshift({
event: 'Header clicked',
expanded: item.expanded,
name: item.name,
timestamp: new Date().toLocaleTimeString(),
})
// Keep only last 5 events
if (eventLog.value.length > 5) {
eventLog.value.pop()
}
}
</script>
<template>
<HLAccordion @item-header-click="handleHeaderClick">
<HLAccordionItem title="Item 1" name="1" id="1">
<div class="p-2">Content for item 1</div>
</HLAccordionItem>
<HLAccordionItem title="Item 2" name="2" id="2">
<div class="p-2">Content for item 2</div>
</HLAccordionItem>
</HLAccordion>
<div class="text-sm">
<p class="font-bold mb-2">Event Log:</p>
<div v-if="eventLog.length === 0" class="text-gray-500">No events logged yet. Try clicking the headers above.</div>
<div v-for="(log, index) in eventLog" :key="index" class="text-gray-700">
{{ log.timestamp }}: {{ log.event }} - Expanded: {{ log.expanded }} - Name: {{ log.name }}
</div>
</div>
</template>Accessibility
- Each
HLAccordionItemheader is rendered as a real<button>witharia-expanded,aria-controls, androle="region"behavior. Give every item a uniqueidand human-readabletitleso those generated attributes read clearly for assistive tech. - Make sure to handle
aria-*attributes for elements passed to the slots
Imports
import { HLAccordion, HLAccordionItem } from '@platform-ui/highrise'Props
Accordion
| Name | Type | Default | Description |
|---|---|---|---|
| id * | string | undefined | undefined | Unique identifier for the accordion |
| accordion | boolean | true | When set to true, the accordian operates in a traditional manner where only one panel can be open at a time, opening one will close the other. When set to false, the accordian will allow multiple panels to be open at the same time. |
| arrowPlacement | 'left' | 'right' | 'right' | Position of the expand/collapse arrow |
| size | 'sm' | 'md' | 'lg' | 'md' | Size of the accordion |
| borderPosition | 'default' | 'item' | 'default' | Position of the border |
| border | boolean | true | Display border |
| defaultExpandedNames | string[] | string | undefined | undefined | Pre-expanded panels that can be collapsed |
| displayDirective | 'if' | 'show' | 'if' | Vue directive to use for content display |
| expandedNames | string[] | string | undefined | undefined | Expanded panels that cannot be collapsed |
| zeroPadding | boolean | false | Remove padding from accordion items |
AccordionItem
| Name | Type | Default | Description |
|---|---|---|---|
| id * | string | undefined | undefined | Unique identifier for the accordion item |
| title | string | undefined | undefined | Title of the accordion item |
| name | string | undefined | undefined | Name of the accordion item |
| disabled | boolean | false | Disable the accordion item |
| hoverEffect | boolean | true | Highlight the item's header on hover |
Emits
| Name | Arguments | Description |
|---|---|---|
@item-header-click | (item: { name: string | number, expanded: boolean, event: MouseEvent }) => void | Triggered when an item header is clicked. expanded reflects the item's state after the click. |
Slots
Accordion
| Name | Parameters | Description |
|---|---|---|
| default | () | Content for the accordion items |
AccordionItem
| Name | Parameters | Description |
|---|---|---|
| default | () | Content for the accordion item |
| header | () | Content for the accordion header |
| header-extra | () | Content for the extra header beside arrow |