@lattice-ui/accordion Stable direction
import Accordion
depends on core , layer , motion Accordion is the primitive for a stack of disclosure sections where each section can expand to reveal content: FAQ lists, settings groups, inventory categories, and quest logs. It owns which items are open, keyed by a string value, and handles the reveal/exit motion of each section’s content so your component only renders the header, trigger, and body.
Reach for Accordion when you have multiple labelled sections that share an expansion policy — only one open at a time (single) or any number open at once (multiple) — and you want that policy, plus per-section motion, handled for you.
Import
import { Accordion } from "@lattice-ui/accordion";Anatomy
Compose Root around a list of Items. Each item carries a unique value, wraps a Header/Trigger pair, and a Content body that mounts only while the item is open.
<Accordion.Root> <Accordion.Item value="..."> <Accordion.Header> <Accordion.Trigger /> </Accordion.Header> <Accordion.Content /> </Accordion.Item></Accordion.Root>| Part | Required | Responsibility |
|---|---|---|
Accordion.Root | yes | Owns the open-value state and the expansion policy, sharing both through context. |
Accordion.Item | yes | Declares one section by value; derives its own open state from the root. |
Accordion.Header | no | A transparent layout frame for the trigger; purely structural. |
Accordion.Trigger | yes | A button that toggles its item open or closed. |
Accordion.Content | yes | The body that mounts and animates in while the item is open. |
Example
A single, collapsible settings panel where one section is open at a time and the open section can be collapsed back to none.
import { useState } from "@rbxts/react";import { Accordion } from "@lattice-ui/accordion";
export function SettingsAccordion() { const [value, setValue] = useState<string | Array<string>>("audio");
return ( <Accordion.Root type="single" collapsible value={value} onValueChange={setValue} > <Accordion.Item value="audio"> <Accordion.Header> <Accordion.Trigger> <textlabel BackgroundTransparency={1} Size={UDim2.fromScale(1, 1)} Text="Audio" TextColor3={Color3.fromRGB(236, 241, 249)} /> </Accordion.Trigger> </Accordion.Header> <Accordion.Content Size={UDim2.fromOffset(260, 60)}> <textlabel BackgroundTransparency={1} Size={UDim2.fromScale(1, 1)} Text="Master volume, music, and SFX sliders." TextColor3={Color3.fromRGB(205, 211, 224)} /> </Accordion.Content> </Accordion.Item>
<Accordion.Item value="controls"> <Accordion.Header> <Accordion.Trigger> <textlabel BackgroundTransparency={1} Size={UDim2.fromScale(1, 1)} Text="Controls" TextColor3={Color3.fromRGB(236, 241, 249)} /> </Accordion.Trigger> </Accordion.Header> <Accordion.Content Size={UDim2.fromOffset(260, 60)}> <textlabel BackgroundTransparency={1} Size={UDim2.fromScale(1, 1)} Text="Rebind movement and action keys." TextColor3={Color3.fromRGB(205, 211, 224)} /> </Accordion.Content> </Accordion.Item> </Accordion.Root> );}How it behaves
Open-value state
Accordion.Root is controllable on value/onValueChange, with defaultValue for uncontrolled usage. State is stored as the item value(s) that are open, not booleans. The type prop selects the policy:
type="single"(the default) keeps at most one item open. The value is treated as a single string; opening a new item replaces the previously open one.type="multiple"lets any number of items be open at once. The value is an array of open item values, de-duplicated.
Accordion.Item reads the root’s open values and considers itself open when its value is in that set. Each Accordion.Trigger toggles its own item through the shared toggle, so controlled and uncontrolled accordions behave identically.
Collapsible
collapsible only affects type="single". When true, activating the currently open trigger collapses it, leaving nothing open. When false (the default), the open item cannot be collapsed by clicking its own trigger — one item always stays open. In type="multiple", items can always be toggled closed regardless of collapsible.
Focus and selection
Accordion.Trigger renders a textbutton that toggles on Activated and also on the Return and Space keys via InputBegan, so gamepad and keyboard activation both work without extra wiring. A disabled item’s trigger ignores activation and key input alike. The default trigger sets Selectable={false} on itself; if you need it reachable by gamepad selection, render your own selectable element through asChild.
Motion and presence
Accordion.Content is presence-driven: it mounts when its item opens and runs a default surface-reveal recipe, then plays the exit animation and unmounts when the item closes. The default trigger also animates its background between an active and inactive color as the item toggles. Override the content animation with transition, or pass forceMount to keep the body mounted through its exit (and while closed) so you can drive motion or measurement yourself.
Match the shape of value/defaultValue to type. With type="single" pass a string; with type="multiple" pass an array of strings. The root normalizes mismatches, but keeping the shape consistent makes your onValueChange handler predictable.
Accordion.Header renders a transparent frame purely to position the trigger — it carries no state and no behavior. Skip it if your layout does not need it, or replace it via asChild.
API reference
Accordion.Root
| Prop | Type | Description |
|---|---|---|
| type | "single" | "multiple" | Expansion policy. "single" keeps at most one item open; "multiple" allows many. Defaults to "single". |
| value | string | Array<string> | Controlled open value(s). Use a string for single mode and an array for multiple mode. Pair with onValueChange. |
| defaultValue | string | Array<string> | Initial open value(s) for uncontrolled usage. Defaults to "" in single mode and [] in multiple mode. |
| onValueChange | (value: string | Array<string>) => void | Called whenever the open value(s) change. |
| collapsible | boolean | In single mode, allows the open item to be collapsed back to none by re-activating its trigger. Defaults to false. No effect in multiple mode. |
| children | React.ReactNode | The accordion items. |
Accordion.Item
| Prop | Type | Description |
|---|---|---|
| value | string | Unique key identifying this item. Determines whether the item is open based on the root's open value(s). |
| disabled | boolean | Prevents the item's trigger from toggling. Defaults to false. |
| asChild | boolean | Merge item layout onto the single child element instead of rendering the default frame. |
| children | React.ReactNode | The header/trigger and content for this item. |
Accordion.Header
| Prop | Type | Description |
|---|---|---|
| asChild | boolean | Merge the header onto the single child element instead of rendering the default transparent frame. |
| children | React.ReactElement | The header contents, typically an Accordion.Trigger. |
Accordion.Trigger
| Prop | Type | Description |
|---|---|---|
| asChild | boolean | Merge trigger behavior onto the single child element instead of rendering the default textbutton. |
| children | React.ReactElement | The element to render. Required when asChild is set; otherwise rendered inside the default button. |
Accordion.Content
In addition to the props below, Accordion.Content forwards any extra GUI props (such as Size, Position, and BackgroundColor3) onto the rendered frame.
| Prop | Type | Description |
|---|---|---|
| forceMount | boolean | Keeps the content mounted while closed and through its exit motion, instead of unmounting when the item closes. |
| transition | PresenceMotionConfig | Overrides the default surface-reveal/exit recipe. |
| asChild | boolean | Merge content behavior onto the single child element instead of rendering the default frame. |
| children | React.ReactNode | The body contents shown while the item is open. |