Components · Data Display
Collapse
A single expandable panel that hides secondary content until asked.
Examples
Accordion
Surface
Size
Caret
Collapse · accordion · a disabled panel · an extra accessory · click a header
<!-- Paste-and-run: save as .html and open in a browser. No build step.
<aha-collapse> is the SAME shared custom element React and Vue consume — here in its
native form. Theming comes only from the --aha-* tokens in tokens.css.
accordion → one panel open at a time · disabled → read-only · slot="extra" → header accessory
ghost → borderless · size="small" → dense · icon-position="end" → caret trailing. -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/tokens.css">
<script type="module">
import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/icons.js'; // registers <aha-icon>
import 'https://cdn.jsdelivr.net/gh/ahaslides-product/ahaslides-design@master/lib/aha-collapse.js'; // registers <aha-collapse>
</script>
<div style="display:flex; flex-direction:column; gap:8px; max-width:380px">
<aha-collapse accordion open>
<span slot="header">Advanced settings</span>
<aha-icon slot="extra" name="system-star" size="14" aria-hidden="true"></aha-icon>
Let participants join anonymously, and shuffle the answer order.
</aha-collapse>
<aha-collapse accordion id="scoring">
<span slot="header">Scoring</span>
Award points by speed, and show a leaderboard between rounds.
</aha-collapse>
<aha-collapse disabled>
<span slot="header">Billing (read-only)</span>
Managed by your workspace owner.
</aha-collapse>
</div>
<script>
document.getElementById('scoring').addEventListener('toggle', (e) => console.log('open:', e.detail.open));
</script>
import '@ahaslides-product/design/aha-collapse'; // registers <aha-collapse>
import '@ahaslides-product/design/icons'; // registers <aha-icon>
import { useRef, useEffect } from 'react';
// React 18 needs a thin wrapper for the composed `toggle` event; React 19 can bind onToggle directly.
function AhaCollapse({ header, extra, open, disabled, accordion, size, ghost, iconPosition, onToggle, children }) {
const ref = useRef();
useEffect(() => {
const el = ref.current;
const h = (e) => onToggle?.(e.detail.open);
el.addEventListener('toggle', h);
return () => el.removeEventListener('toggle', h);
});
return (
<aha-collapse
ref={ref}
{...(open ? { open: '' } : {})}
{...(disabled ? { disabled: '' } : {})}
{...(accordion ? { accordion: '' } : {})}
{...(ghost ? { ghost: '' } : {})}
{...(size ? { size } : {})}
{...(iconPosition ? { 'icon-position': iconPosition } : {})}
>
<span slot="header">{header}</span>
{extra && <span slot="extra">{extra}</span>}
{children}
</aha-collapse>
);
}
// usage — accordion group: opening one closes its siblings
<AhaCollapse header="Advanced settings" accordion open onToggle={(o) => setOpen(o)}>
Let participants join anonymously, and shuffle the answer order.
</AhaCollapse>
<AhaCollapse header="Scoring" accordion>Award points by speed.</AhaCollapse>
<AhaCollapse header="Billing (read-only)" disabled>Managed by your workspace owner.</AhaCollapse>
// main.ts — register the elements + mark aha-* as custom elements
import '@ahaslides-product/design/aha-collapse';
import '@ahaslides-product/design/icons';
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('aha-');
// Component.vue
<script setup>
import { ref } from 'vue';
const open = ref(true);
</script>
<template>
<!-- accordion group: opening one panel closes its siblings -->
<aha-collapse
accordion
:open="open || undefined"
@toggle="open = $event.detail.open"
>
<span slot="header">Advanced settings</span>
<aha-icon slot="extra" name="system-star" size="14" aria-hidden="true" />
Let participants join anonymously, and shuffle the answer order.
</aha-collapse>
<aha-collapse accordion>
<span slot="header">Scoring</span>
Award points by speed, and show a leaderboard between rounds.
</aha-collapse>
<aha-collapse disabled>
<span slot="header">Billing (read-only)</span>
Managed by your workspace owner.
</aha-collapse>
</template>
API
| Prop | Type | Default | Notes |
|---|---|---|---|
open | boolean | false | Whether the panel is expanded; toggled on header click (observed → aria-expanded synced) |
accordion | boolean | false | Opening this panel closes sibling accordion panels under the same parent (one-open-at-a-time) |
ghost | boolean | false | Borderless, transparent surface that sits seamlessly on the page |
size | 'small' | — | Denser header + body padding |
icon-position | 'start' | 'end' | start | Caret on the leading or trailing edge of the header |
disabled | boolean | false | Non-interactive, dimmed panel (observed → aria-disabled synced) |
header | slot | Section | The always-visible summary row |
extra | slot | — | A trailing header accessory (a tag, a count, an icon) |
toggle | CustomEvent<{open}> | — | Composed event; read e.detail.open |
Install
# .npmrc — once: point the @ahaslides-product scope at GitHub Packages
@ahaslides-product:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} # a GitHub token with read:packages
npm i @ahaslides-product/design
import '@ahaslides-product/design/tokens.css'; // once, at the app root
import '@ahaslides-product/design/aha-collapse'; // registers <aha-collapse>
Agent feed for this component (absolute, fetchable anywhere): collapse.agent.json · collapse.md · collapse.llms.txt
When to use
When to use
- Collapse — hiding secondary detail (advanced settings, an FAQ answer) until wanted
- Tabs — switching between peer views of equal importance
- Card — content that should always be visible
Header text names what's inside — 'Advanced settings', not 'More'. Keep panels shallow; deep nesting hides content people never find.
Surfaces
editor dashboard settings
Spec
Panel border 1px #E3E3E3 · radius 8px · bg #FFFFFF (ghost: borderless, transparent) · Header padding 14/16 · 15/22 SemiBold #1A1A1A · hover #F7F7F7 (small: 8/12 · 14/20) · Caret system-caret-right <aha-icon>, rotate 0°→90° on open, motion-mid ease-in-out · Icon position start (leading, default) or end (trailing edge, via order) · Body grid-rows 0fr→1fr, motion-mid ease-in-out · 14/22 #4A4A4A · Disabled text-disabled #B5B5B5, not-allowed, hover suppressed, aria-disabled