I9kCollapsible
Native, independently expanding disclosure surface with slots for rich summary and body content.
ts
import { I9kCollapsible } from '@9klabs/design';Examples
Closed
Course module
Module lessons and resources.
vue
<I9kCollapsible>
<template #summary>Course module</template>
<p>Module lessons and resources.</p>
</I9kCollapsible>Initially open
Course module
This body is visible when the example first renders.
vue
<I9kCollapsible :default-open="true">
<template #summary>Course module</template>
<p>This body is visible when the example first renders.</p>
</I9kCollapsible>Rich summary
Module 03 · 9 topics
- Plan
- Design
- Build
vue
<I9kCollapsible>
<template #summary><span><strong>Module 03</strong> · 9 topics</span></template>
<ol><li>Plan</li><li>Design</li><li>Build</li></ol>
</I9kCollapsible>Flush, in a list
01 · Getting started
- Welcome
- Setup
02 · Prompting
- Context
vue
<div style="display: grid; gap: var(--spacing-2); max-width: 20rem">
<I9kCollapsible variant="flush" :default-open="true">
<template #summary>01 · Getting started</template>
<ol><li>Welcome</li><li>Setup</li></ol>
</I9kCollapsible>
<I9kCollapsible variant="flush">
<template #summary>02 · Prompting</template>
<ol><li>Context</li></ol>
</I9kCollapsible>
</div>Independent instances
First
Open together.
Second
Also open.
vue
<div style="display: grid; gap: var(--spacing-6)">
<I9kCollapsible :default-open="true"><template #summary>First</template><p>Open together.</p></I9kCollapsible>
<I9kCollapsible :default-open="true"><template #summary>Second</template><p>Also open.</p></I9kCollapsible>
</div>Controlled
Course module
The parent owns whether this body is visible.
vue
<button type="button" @click="open = !open">{{ open ? 'Collapse' : 'Expand' }}</button>
<I9kCollapsible v-model:open="open">
<template #summary>Course module</template>
<p>The parent owns whether this body is visible.</p>
</I9kCollapsible>Arabic RTL
الوحدة الأولى
محتوى الوحدة وتفاصيلها.
vue
<div dir="rtl" lang="ar">
<I9kCollapsible :default-open="true">
<template #summary>الوحدة الأولى</template>
<p>محتوى الوحدة وتفاصيلها.</p>
</I9kCollapsible>
</div>Watch out
defaultOpencontrols only the initial state; bindv-model:openfor a controlled disclosure such as expand-all.- A one-way
:openbinding does not learn about user toggles; usev-model:openso the parent value stays in step. - Uncontrolled sibling instances expand independently; control them with
v-model:openif only one section may be open. - Provide visible summary content because the summary slot is the disclosure control label.
- Use
variant="flush"for disclosures listed inside a panel or sidebar; the default card is meant to stand on the page.
API
Props
| Prop | Type | Default | Required |
|---|---|---|---|
defaultOpen | boolean | false | no |
open | boolean | undefined | no |
variant | 'card' | 'flush' | 'card' | no |
Emits
| Event | Payload |
|---|---|
toggle | [open: boolean] |
update:open | [open: boolean] |
Slots
summarydefault
Agent prompt
Paste this into an agent to have it use the component correctly. It is self-contained.
text
Use I9kCollapsible from @9klabs/design for a single branded disclosure that may contain rich Vue markup.
import { I9kCollapsible } from '@9klabs/design';
Props:
- defaultOpen?: boolean (default false) — sets only the initial native open state when `open` is not bound.
- open?: boolean (default undefined) — bind with v-model:open to control the open state from the parent, e.g. an expand-all / collapse-all button. Leave it unbound for the browser-managed, uncontrolled behavior.
- variant?: 'card' | 'flush' (default 'card') — `flush` drops the border and background, for a list of disclosures inside a panel or a sidebar.
Emits:
- toggle(open: boolean) — reports the current native details.open value after every native toggle, including one caused by changing a bound `open`.
- update:open(open: boolean) — emitted only when `open` is bound and a user toggle makes the native state differ from it.
Slots:
- summary — content rendered inside the native <summary>.
- default — content rendered in the disclosure body.
Behavior: the component renders native <details>/<summary>. Unless `open` is bound, each instance owns its browser-managed state, so siblings expand independently and any number may stay open. When `open` is bound, changing it opens or closes the disclosure, and a user toggle is reported through update:open.
IMPORTANT: `defaultOpen` is only an initial-state option; bind `open` (v-model:open) when the parent must set the state, such as expand-all. A bound `open` without a listener for update:open does not follow user toggles back into the parent — use v-model:open, not a one-way :open.
Usage:
<I9kCollapsible :default-open="true" @toggle="(open) => console.log(open)">
<template #summary><strong>Module 01</strong> · 7 topics</template>
<ol><li>What is coding?</li><li>Engineering judgment</li></ol>
</I9kCollapsible>