Collapsible
A single disclosure that ships zero JavaScript. The open state is the
browser's (<details>), toggling is the browser's, and the expanded /
collapsed state is announced natively to assistive technology. It renders as
a Server Component and works with JavaScript disabled.
What ships to the browser?
Does it work with JavaScript disabled?
import { Collapsible, Box } from "astralis-ui";
// No "use client": this demo IS a Server Component. The open/close behavior
// you're using is the browser's <details> element, not JavaScript.
export function CollapsibleDemo() {
return (
<Box w="full" maxW="md">
<Collapsible defaultOpen>
<Collapsible.Trigger>What ships to the browser?</Collapsible.Trigger>
<Collapsible.Content>
Nothing. This entire component renders to HTML on the server. The
toggle you just used is the native details element.
</Collapsible.Content>
</Collapsible>
<Collapsible>
<Collapsible.Trigger>Does it work with JavaScript disabled?</Collapsible.Trigger>
<Collapsible.Content>
Yes: open, close, keyboard, and screen-reader state announcements
are all platform behavior.
</Collapsible.Content>
</Collapsible>
</Box>
);
}What ships to the browser?
import { Collapsible } from "astralis-ui";
<Collapsible>
<Collapsible.Trigger>What ships to the browser?</Collapsible.Trigger>
<Collapsible.Content>Nothing but HTML: the toggle is the native details element.</Collapsible.Content>
</Collapsible>
Import#
import { Collapsible } from "astralis-ui";Exclusive groups (accordion, no JS)#
Collapsibles sharing a name form a native exclusive accordion: opening one
closes its siblings via the <details name> attribute:
Only one of us stays open
Try me
Or me
import { Collapsible, Box } from "astralis-ui";
// Collapsibles sharing a `name` form a native exclusive accordion: opening
// one closes its siblings, with zero JavaScript (`<details name>`).
export function CollapsibleExclusive() {
return (
<Box w="full" maxW="md">
<Collapsible name="faq" defaultOpen>
<Collapsible.Trigger>Only one of us stays open</Collapsible.Trigger>
<Collapsible.Content>Open my sibling and I close.</Collapsible.Content>
</Collapsible>
<Collapsible name="faq">
<Collapsible.Trigger>Try me</Collapsible.Trigger>
<Collapsible.Content>The grouping is the name attribute.</Collapsible.Content>
</Collapsible>
<Collapsible name="faq">
<Collapsible.Trigger>Or me</Collapsible.Trigger>
<Collapsible.Content>Still no JavaScript.</Collapsible.Content>
</Collapsible>
</Box>
);
}Uncontrolled by design#
There is no open/onOpenChange pair: a controlled variant would move the
state into React and forfeit exactly the property this component exists for.
Use defaultOpen for the initial state; if you need programmatic control,
Accordion is the stateful sibling.
Accessibility#
- The trigger stays a real
<summary>with its nativelist-itemdisplay: only the marker is hidden. Restyling summary's display is the documented way to lose expanded/collapsed announcements in some screen readers, so the layout row is an inner span instead. - Find-in-page can auto-open a collapsed section in Chromium: content is searchable, not trapped.
Props#
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | false | Open on first render. There is no controlled open prop: the state belongs to the browser. |
name | string | None | Collapsibles sharing a name form a native exclusive group, where opening one closes the others. |
Collapsible.Trigger#
| Prop | Type | Default | Description |
|---|---|---|---|
hideIndicator | boolean | false | Hides the built-in chevron, for when you draw your own. |
Parts#
| Prop | Type | Default | Description |
|---|---|---|---|
Collapsible.Trigger | summary props | None | The always-visible summary line that toggles the content. |
Collapsible.Content | div props | None | The content shown while open. Adds spacing and typography only. |
Keyboard#
The trigger is a native <summary>, so it is a tab stop that toggles with
Enter or Space, with or without JavaScript.