CollapsibleContent
| Prop | Type | Default | Description |
|---|---|---|---|
class | string | — | Classes appended to the content |
On my first day at the new school I met a girl on the roof holding an old camera. She said it could photograph tomorrow.
<script setup lang="ts">
import {
Button,
Card,
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
DisclosureIcon,
Stack,
Text,
} from '@hina-ui/vue'
</script>
<template>
<Card class="w-full max-w-md">
<Collapsible>
<Stack gap="sm" align="start">
<Text tone="muted" size="sm">
On my first day at the new school I met a girl on the roof holding an old camera. She said
it could photograph tomorrow.
</Text>
<CollapsibleTrigger as-child>
<Button variant="link" size="sm">
<template #trailing>
<DisclosureIcon />
</template>
Read the full synopsis
</Button>
</CollapsibleTrigger>
<CollapsibleContent>
<Text tone="muted" size="sm">
Every day after that we climbed the rusted ladder once class let out. She pressed the
shutter and I kept the time. Then one evening something appeared in the viewfinder that
should not have been there.
</Text>
</CollapsibleContent>
</Stack>
</Collapsible>
</Card>
</template>
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@hina-ui/vue'
Three parts: Collapsible holds the open state, CollapsibleTrigger is the control that toggles it, and CollapsibleContent is the region being folded away.
The trigger is a complete control on its own and brings an indicator that turns with the state. Write the label and nothing else — no button to compose, no transition to handle.
<script setup lang="ts">
import { Collapsible, CollapsibleContent, CollapsibleTrigger, Stack, Text } from '@hina-ui/vue'
</script>
<template>
<Collapsible class="w-full max-w-md">
<Stack gap="sm" align="start">
<CollapsibleTrigger>Reading settings</CollapsibleTrigger>
<CollapsibleContent>
<Text tone="muted" size="sm">
Type size, line height, page direction and background colour all live here.
</Text>
</CollapsibleContent>
</Stack>
</Collapsible>
</template>
Its appearance is fixed at one unobtrusive step, since open and closed are the only two things it has to say. Where the label belongs in the text — a “read the rest” inside a paragraph — swap the skin through as-child, below.
Use it when a single region folds away. For several regions in one group that open one at a time, use Accordion.
v-model:open hands the state to the surrounding page, so other controls can drive the same region. When no outside control is needed, default-open sets the initial state.
open: false
<script setup lang="ts">
import { ref } from 'vue'
import {
Button,
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
Inline,
Stack,
Text,
} from '@hina-ui/vue'
const open = ref(false)
</script>
<template>
<Stack class="w-full max-w-md">
<Inline>
<Button size="sm" variant="soft" tone="neutral" @click="open = !open">
{{ open ? 'Collapse' : 'Expand' }}
</Button>
<Text tone="muted" size="sm">open: {{ open }}</Text>
</Inline>
<Collapsible v-model:open="open">
<Stack gap="sm" align="start">
<CollapsibleTrigger>The component's own trigger</CollapsibleTrigger>
<CollapsibleContent>
<Text tone="muted" size="sm">Both triggers drive the same state.</Text>
</CollapsibleContent>
</Stack>
</Collapsible>
</Stack>
</template>
With disabled the trigger stops responding and the content keeps its current state.
<script setup lang="ts">
import { Collapsible, CollapsibleContent, CollapsibleTrigger, Stack, Text } from '@hina-ui/vue'
</script>
<template>
<Collapsible disabled class="w-full max-w-md">
<Stack gap="sm" align="start">
<CollapsibleTrigger>Edit history (sign in required)</CollapsibleTrigger>
<CollapsibleContent>
<Text tone="muted" size="sm">This section will not open.</Text>
</CollapsibleContent>
</Stack>
</Collapsible>
</template>
The icon slot swaps the indicator’s glyph while the component keeps owning the rotation. Setting icon to false hides the indicator entirely.
as-child lends the behaviour to the sole child, which puts both the appearance and the indicator in your hands. Every change of skin — a text link, an outline, a full-width row with an icon slot — goes this way; drop in a DisclosureIcon and it still finds the state. The example at the top of this page is exactly that, with variant="link".
The component forwards no appearance props: a disclosure toggle only has to express open and closed, and taking on a button’s full range of looks would grow this API into a second Button. Changing the skin means bringing the whole trigger.
<script setup lang="ts">
import { Plus } from '@lucide/vue'
import {
Button,
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
DisclosureIcon,
Stack,
Text,
} from '@hina-ui/vue'
</script>
<template>
<Stack class="w-full max-w-md">
<Collapsible>
<Stack gap="sm" align="start">
<CollapsibleTrigger>
Swap the glyph
<template #icon>
<Plus />
</template>
</CollapsibleTrigger>
<CollapsibleContent>
<Text tone="muted" size="sm">
A plus turns a quarter into a cross, and the component still owns the rotation.
</Text>
</CollapsibleContent>
</Stack>
</Collapsible>
<Collapsible>
<Stack gap="sm" align="start">
<CollapsibleTrigger as-child>
<Button variant="outline" tone="neutral" block class="justify-between">
Bring your own trigger
<template #trailing>
<DisclosureIcon />
</template>
</Button>
</CollapsibleTrigger>
<CollapsibleContent>
<Text tone="muted" size="sm">
On this path the appearance and the indicator are both yours — place a DisclosureIcon
and it still finds the state.
</Text>
</CollapsibleContent>
</Stack>
</Collapsible>
</Stack>
</template>
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Whether it is open, takes v-model:open |
defaultOpen | boolean | false | Whether it starts open |
disabled | boolean | false | Whether it can be operated |
class | string | — | Classes appended to the root |
| Event | Payload | Description |
|---|---|---|
update:open | open: boolean | The state changed |
| Prop | Type | Default | Description |
|---|---|---|---|
icon | boolean | true | Whether the indicator is shown |
asChild | boolean | false | Skip the built-in button and lend behaviour to the child |
class | string | — | Classes appended to the trigger |
| Slot | Description |
|---|---|
default | The trigger’s label |
icon | Replaces the indicator’s glyph |
| Prop | Type | Default | Description |
|---|---|---|---|
class | string | — | Classes appended to the content |