Expand

Collapsible component for expanding hidden sections.

import { Expand } from 'ui-lab-components/expand';
import { Expand } from 'ui-lab-components/expand';
 
export default function Example() {
  return (
    <div>
      <Expand title="What is an Expand component?">
        <p className="text-foreground-300">
          An Expand component is a disclosure widget that expands and collapses content.
          It's built with React Aria for full accessibility support.
        </p>
      </Expand>
    </div>
  );
}
 

expand/01-accordion

Controlled expand group where only one item can be open at a time.

Loading interactive preview
"use client";
 
import { useState } from 'react';
import { Expand } from 'ui-lab-components/expand';
 
const items = [
  {
    id: 'shipping',
    title: 'Shipping & Delivery',
    content: 'Standard shipping takes 3–5 business days. Express options are available at checkout for next-day delivery.',
  },
  {
    id: 'returns',
    title: 'Returns & Refunds',
    content: 'Items can be returned within 30 days of purchase. Refunds are processed within 5–7 business days after we receive the item.',
  },
  {
    id: 'warranty',
    title: 'Warranty',
    content: 'All products include a 12-month manufacturer warranty. Extended coverage can be purchased at checkout.',
  },
];
 
export default function AccordionPreview() {
  const [open, setOpen] = useState<string | null>('shipping');
 
  return (
    <div className="overflow-hidden border border-background-700 rounded-sm divide-y divide-background-700">
      {items.map((item) => (
        <Expand
          key={item.id}
          layout="contained"
          title={item.title}
          isExpanded={open === item.id}
          onExpandedChange={(expanded) => setOpen(expanded ? item.id : null)}
        >
          <p className="text-sm text-foreground-400 px-3 py-3">{item.content}</p>
        </Expand>
      ))}
    </div>
  );
}
 

expand/02-custom-trigger

Compound mode with a fully composed trigger — custom icon, badge, and layout.

Loading interactive preview
import { Expand } from 'ui-lab-components/expand'
import { Flex } from 'ui-lab-components/flex'
import { Badge } from 'ui-lab-components/badge';
import { Icon } from 'ui-lab-components/icon'
 
export default function Example() {
  return (
    <div className="border border-background-700 rounded-sm">
      <Expand>
        <Expand.Trigger>
          <Flex align-center gap="xs" className="flex-1 px-3 py-2.5">
            <Icon id="user" size="1em" className="text-foreground-400 text-xs shrink-0" />
            <span className="text-sm font-medium">Account Settings</span>
            <Badge className="ml-auto mr-2">New</Badge>
          </Flex>
          <Expand.Icon />
        </Expand.Trigger>
        <Expand.Divider />
        <Expand.Content>
          <Flex direction="column" gap="xs" className="px-3 py-3">
            <p className="text-sm text-foreground-400">Manage your profile, password, and notification preferences.</p>
          </Flex>
        </Expand.Content>
      </Expand>
    </div>
  );
}
 

expand/03-reveal-directions

Content can reveal from above or horizontally using the from prop on Expand.Content.

Loading interactive preview
import { Expand } from 'ui-lab-components/expand'
import { Flex } from 'ui-lab-components/flex';
 
export default function Example() {
  return (
    <Flex direction="column" gap="lg">
      <div>
        <p className="text-xs text-foreground-500 mb-2 px-1">from="above"</p>
        <div className="border border-background-700 rounded-sm">
          <Expand title="Reveal above">
            <Expand.Content from="above">
              <p className="text-sm text-foreground-400 px-3 py-3">This content slides in from above the trigger.</p>
            </Expand.Content>
          </Expand>
        </div>
      </div>
      <div>
        <p className="text-xs text-foreground-500 mb-2 px-1">from="right"</p>
        <div className="border border-background-700 rounded-sm">
          <Expand title="Reveal right">
            <Expand.Content from="right">
              <p className="text-sm text-foreground-400 px-3 py-3">Expands horizontally to the right.</p>
            </Expand.Content>
          </Expand>
        </div>
      </div>
    </Flex>
  );
}
 

expand/04-settings-panel-expand

Grouped settings sections using compound mode with icons, dividers, and a list of rows.

Loading interactive preview
import { Expand } from 'ui-lab-components/expand'
import { Flex } from 'ui-lab-components/flex';
import { Icon } from 'ui-lab-components/icon'
 
export default function Example() {
  return (
    <div className="overflow-hidden border border-background-700 rounded-sm divide-y divide-background-700">
      <Expand layout="contained" expanded>
        <Expand.Trigger>
          <Flex align-center gap="xs" className="flex-1 px-3 py-2.5">
            <Icon id="gear" size="1em" className="text-foreground-400 text-xs" />
            <span className="text-sm font-medium">General</span>
          </Flex>
          <Expand.Icon />
        </Expand.Trigger>
        <Expand.Divider />
        <Expand.Content>
          <Flex direction="column" className="divide-y divide-background-700">
            {['Language', 'Timezone', 'Date format'].map((setting) => (
              <Flex key={setting} justify-between align-center className="px-3 py-2.5">
                <span className="text-sm text-foreground-300">{setting}</span>
                <span className="text-xs text-foreground-500">Auto</span>
              </Flex>
            ))}
          </Flex>
        </Expand.Content>
      </Expand>
      <Expand layout="contained">
        <Expand.Trigger>
          <Flex align-center gap="xs" className="flex-1 px-3 py-2.5">
            <Icon id="code" size="1em" className="text-foreground-400 text-xs" />
            <span className="text-sm font-medium">Developer</span>
          </Flex>
          <Expand.Icon />
        </Expand.Trigger>
        <Expand.Divider />
        <Expand.Content>
          <Flex direction="column" className="divide-y divide-background-700">
            {['API keys', 'Webhooks', 'Debug mode'].map((setting) => (
              <Flex key={setting} justify-between align-center className="px-3 py-2.5">
                <span className="text-sm text-foreground-300">{setting}</span>
                <span className="text-xs text-foreground-500">Off</span>
              </Flex>
            ))}
          </Flex>
        </Expand.Content>
      </Expand>
    </div>
  );
}
 

expand/05-disabled-state-expand

An disabled section is visually dimmed and blocks interaction.

Loading interactive preview
import { Expand } from 'ui-lab-components/expand';
 
export default function Example() {
  return (
    <div className="border border-background-700 rounded-sm divide-y divide-background-700">
      <Expand title="Available section">
        <p className="text-sm text-foreground-400 px-3 py-3">This section is accessible and can be expanded.</p>
      </Expand>
      <Expand title="Restricted section" disabled>
        <p className="text-sm text-foreground-400 px-3 py-3">This content is not accessible.</p>
      </Expand>
    </div>
  );
}
 

expand/06-inline-info

A contextual disclosure pattern for surfacing supplementary information inline.

Loading interactive preview
import { Expand } from 'ui-lab-components/expand'
import { Flex } from 'ui-lab-components/flex';
import { Icon } from 'ui-lab-components/icon'
 
export default function Example() {
  return (
    <div className="rounded-sm border border-background-700 bg-background-900">
      <Expand>
        <Expand.Trigger>
          <Flex align-center gap="xs" className="flex-1 px-3 py-2.5">
            <Icon id="info" size="1em" className="text-foreground-400 text-xs shrink-0" />
            <span className="text-sm font-medium text-foreground-200">Why do we collect this?</span>
          </Flex>
          <Expand.Icon />
        </Expand.Trigger>
        <Expand.Divider />
        <Expand.Content>
          <p className="text-sm text-foreground-400 px-3 py-3 leading-relaxed">
            We use this information only to verify your identity and improve the service. It is never shared with third parties.
          </p>
        </Expand.Content>
      </Expand>
    </div>
  );
}