"use client";
import { Accordion, Stack } from "@vortexlabs/vortex";
export const AccordionExample = () => {
return (
<Accordion.Root defaultValue="shipping" divider gap={3}>
<Accordion.Item value="shipping">
<Accordion.Trigger paddingBlock={3}>
When will my order arrive?
</Accordion.Trigger>
<Accordion.Content>
<Stack paddingBottom={4}>
Standard delivery takes three to five business days.
</Stack>
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="returns">
<Accordion.Trigger paddingBlock={3}>Can I return an item?</Accordion.Trigger>
<Accordion.Content>
<Stack paddingBottom={4}>
You can request a return within 30 days.
</Stack>
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
);
};Features
- Supports single or multiple expanded sections.
- Supports controlled values and initially expanded items.
- Controls trigger sizes, spacing, and divider lines.
- Can disable individual items.
- Supports custom triggers with your own buttons.
Anatomy
<Accordion.Root>
<Accordion.Item value="shipping">
<Accordion.Trigger>Shipping</Accordion.Trigger>
<Accordion.Content>Three to five business days.</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="returns">
<Accordion.Trigger>Returns</Accordion.Trigger>
<Accordion.Content>Within 30 days.</Accordion.Content>
</Accordion.Item>
</Accordion.Root>value.Multiple Sections
The default type="single" allows one open item. Set type="multiple" to expand items independently and pass an array to defaultValue.
"use client";
import { Accordion, Stack } from "@vortexlabs/vortex";
export const AccordionMultiple = () => {
return (
<Accordion.Root
type="multiple"
defaultValue={["delivery", "returns"]}
divider
gap={3}
>
<Accordion.Item value="delivery">
<Accordion.Trigger paddingBlock={3}>Delivery</Accordion.Trigger>
<Accordion.Content>
<Stack paddingBottom={4}>
Delivery is free for orders over $75.
</Stack>
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="returns">
<Accordion.Trigger paddingBlock={3}>Returns</Accordion.Trigger>
<Accordion.Content>
<Stack paddingBottom={4}>
Keep the original packaging for your return.
</Stack>
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
);
};Controlled State
defaultValue chooses the initially expanded section. Use value and onChange when another control needs to open or close an item. In single mode, null closes every item; multiple mode uses a string array.
"use client";
import { Accordion, Button, Stack, Text } from "@vortexlabs/vortex";
import React from "react";
export const AccordionControlled = () => {
const [value, setValue] = React.useState<string | null>(null);
return (
<Stack gap={4}>
<Stack direction="row" gap={3} wrap>
<Button size="sm" onClick={() => setValue("details")}>Show details</Button>
<Button size="sm" variant="secondary" onClick={() => setValue(null)}>
Close all
</Button>
</Stack>
<Accordion.Root value={value} onChange={setValue} gap={3}>
<Accordion.Item value="details">
<Accordion.Trigger>Order details</Accordion.Trigger>
<Accordion.Content>Your order contains two items.</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
<Text size="body-sm-desktop">Open section: {value ?? "none"}</Text>
</Stack>
);
};Sizes
Trigger size styles the label as md or lg. Set iconSize on the root to change every expand icon.
"use client";
import { Accordion, Stack } from "@vortexlabs/vortex";
export const AccordionSizes = () => {
return (
<Stack gap={5}>
<Accordion.Root iconSize={4}>
<Accordion.Item value="compact">
<Accordion.Trigger size="md">Medium label and smaller icon</Accordion.Trigger>
<Accordion.Content>Compact order information.</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
<Accordion.Root iconSize={{ xs: 5, md: 6 }}>
<Accordion.Item value="large">
<Accordion.Trigger size="lg">
Large label and responsive icon
</Accordion.Trigger>
<Accordion.Content>More prominent order information.</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
</Stack>
);
};Spacing and Dividers
Use divider to separate items. Trigger padding sets the clickable area, and root gap adds space above expanded content. Pad the bottom of Content to keep text off the next divider.
"use client";
import { Accordion, Stack } from "@vortexlabs/vortex";
export const AccordionSpacing = () => {
return (
<Accordion.Root divider gap={{ xs: 2, md: 4 }} defaultValue="billing">
<Accordion.Item value="billing">
<Accordion.Trigger paddingBlock={{ xs: 3, md: 4 }} paddingInline={2}>
Billing address
</Accordion.Trigger>
<Accordion.Content>
<Stack paddingInline={2} paddingBottom={4}>
Use the address associated with your payment method.
</Stack>
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="payment">
<Accordion.Trigger paddingBlock={{ xs: 3, md: 4 }} paddingInline={2}>
Payment methods
</Accordion.Trigger>
<Accordion.Content>
<Stack paddingInline={2} paddingBottom={4}>
We accept credit and debit cards.
</Stack>
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
);
};Clickable Items
Set clickableItems on the root to toggle an item by clicking anywhere on it. Buttons, links, form controls, and text selection inside keep their own behavior. Add item padding and borderRadius to style items as cards. Target data-vortex-accordion-state and data-vortex-accordion-disabled to style open and disabled items.
"use client";
import { Accordion, Button, Stack } from "@vortexlabs/vortex";
const surface = {
style: { background: "var(--vortex-color-background-surface-subtlest)" },
};
export const AccordionClickable = () => {
return (
<Accordion.Root clickableItems gap={3}>
<Stack gap={3}>
<Accordion.Item
value="starter"
padding={{ xs: 4, md: 5 }}
borderRadius="md"
attributes={surface}
>
<Accordion.Trigger size="md">Starter plan</Accordion.Trigger>
<Accordion.Content>One project and up to three teammates.</Accordion.Content>
</Accordion.Item>
<Accordion.Item
value="team"
padding={{ xs: 4, md: 5 }}
borderRadius="md"
attributes={surface}
>
<Accordion.Trigger size="md">Team plan</Accordion.Trigger>
<Accordion.Content>
<Stack gap={3} align="start">
Unlimited projects and shared workspaces.
<Button size="sm">Compare plans</Button>
</Stack>
</Accordion.Content>
</Accordion.Item>
</Stack>
</Accordion.Root>
);
};Disabled Items
Set disabled on an Item to prevent it from being toggled. Other items remain interactive.
"use client";
import { Accordion, Stack } from "@vortexlabs/vortex";
export const AccordionDisabled = () => {
return (
<Accordion.Root divider gap={3}>
<Accordion.Item value="available">
<Accordion.Trigger paddingBlock={3}>Delivery information</Accordion.Trigger>
<Accordion.Content>
<Stack paddingBottom={4}>Your delivery details appear here.</Stack>
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="unavailable" disabled>
<Accordion.Trigger paddingBlock={3}>
Tracking available after dispatch
</Accordion.Trigger>
<Accordion.Content>
<Stack paddingBottom={4}>
Your tracking number appears after dispatch.
</Stack>
</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
);
};Custom Triggers
Pass a render function to Trigger to use your own button. Pass its attributes to the button to keep the expanded state and panel link, and forward disabled.
"use client";
import { Accordion, Button } from "@vortexlabs/vortex";
export const AccordionCustom = () => {
return (
<Accordion.Root gap={3}>
<Accordion.Item value="summary">
<Accordion.Trigger>
{(attributes, { active, disabled }) => (
<Button
type="button"
size="sm"
variant="secondary"
disabled={disabled}
attributes={attributes}
>
{active ? "Hide order summary −" : "Show order summary +"}
</Button>
)}
</Accordion.Trigger>
<Accordion.Content>Two items · Free delivery · $84 total</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
);
};