Collapsible
Shows and hides a block of content from a button above it.
Collapsible is a button and the content it shows and hides. Useful for detail a reader can ask for rather than has to scroll past, like an advanced settings block or the long half of a summary.
<template>
<Collapsible.Root v-model:open="open" class="w-80">
<Collapsible.Trigger :class="trigger">
<span>Show details</span>
<svg :class="chevron" viewBox="0 0 16 16" aria-hidden="true">
<path
d="m4 6.5 4 4 4-4"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</Collapsible.Trigger>
<Collapsible.Content>
<p :class="body">
The content animates to the height it needs, so nothing here sets a
height, and the text can grow or shrink without anyone measuring it.
</p>
</Collapsible.Content>
</Collapsible.Root>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Collapsible } from '@maas/mirror/vue/Collapsible'
const open = ref(false)
const trigger = [
'group flex h-10 w-full items-center justify-between gap-2 px-3',
'rounded-component-md border-2 border-[transparent] type-component-md',
'text-surface transition-all duration-100 ease-linear',
'outline-4 outline-transparent focus-visible:focus-ring',
'hover:bg-primary-subtle',
'active:bg-primary-subtle-active',
].join(' ')
const chevron = [
'size-4.5 shrink-0 text-surface-muted',
'transition-transform duration-200 ease-out',
'group-data-[content-open=true]:rotate-180',
].join(' ')
const body = 'type-surface-body-md text-surface-muted px-3 pt-2 pb-3'
</script>
Usage guidelines
- Put the padding on something inside the content rather than on the content itself. Content behind
hidden="until-found"keeps a box of its own, so its padding is height the collapsed box never loses.should - The height animates whether the closed content is unmounted or kept, so reach for
forceMountfor what is inside rather than for the motion.should - For several of these that share a heading structure, or where only one should be open at a time, an accordion is the pattern you want. Mirror does not ship one;
MagicAccordionin vue-equipment does.should