Westy Design Systemv0.2.7
OverviewComponentsExamplesStorybook

Foundations

  • Typography
  • Colors
  • Spacing
  • Radius
  • Shadows
  • Icons

Components

  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Data Table
  • Date Picker
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Menubar
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Questionnaire
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toggle
  • Toggle Group
  • Tooltip

Chat

  • Attachment
  • Bubble
  • Marker
  • Message
  • Message Scroller

Components

Collapsible

An interactive component which expands and collapses a panel.

Playground

@westy starred 3 repositories

@workspace/ui
radix-ui/primitives
tailwindlabs/tailwindcss
—open
—disabled
—dir
Usage
Collapsible wraps a CollapsibleTrigger and the CollapsibleContent it shows or hides. The parts ship no styles of their own — layout, borders and spacing are the consumer's decision. Content outside CollapsibleContent stays visible in both states, which is how the first row here remains on screen while the rest collapse.

@westy starred 3 repositories

@workspace/ui
radix-ui/primitives
tailwindlabs/tailwindcss
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@workspace/ui/components/collapsible"

<Collapsible defaultOpen className="flex w-full max-w-sm flex-col gap-2">
  <div className="flex items-center justify-between gap-4 px-4">
    <h4 className="text-sm font-medium">@westy starred 3 repositories</h4>
    <CollapsibleTrigger asChild>
      <Button variant="ghost" size="icon-sm">
        <CaretUpDownIcon />
        <span className="sr-only">Toggle</span>
      </Button>
    </CollapsibleTrigger>
  </div>
  <div className="rounded-md border px-4 py-2 font-mono text-sm">
    @workspace/ui
  </div>
  <CollapsibleContent className="flex flex-col gap-2">
    <div className="rounded-md border px-4 py-2 font-mono text-sm">
      radix-ui/primitives
    </div>
    <div className="rounded-md border px-4 py-2 font-mono text-sm">
      tailwindlabs/tailwindcss
    </div>
  </CollapsibleContent>
</Collapsible>
Controlled
Pass open and onOpenChange to own the state from outside. A CollapsibleTrigger is optional — here a regular Button drives the panel, so the same state can also feed labels or other UI.

Advanced settings

const [open, setOpen] = React.useState(false)

<div className="w-full max-w-sm space-y-2">
  <div className="flex items-center justify-between gap-4">
    <p className="text-sm font-medium">Advanced settings</p>
    <Button variant="outline" size="sm" onClick={() => setOpen(!open)}>
      {open ? "Hide" : "Show"}
    </Button>
  </div>
  <Collapsible open={open} onOpenChange={setOpen}>
    <CollapsibleContent>
      <div className="space-y-1 rounded-md border px-4 py-3 text-sm">
        <p className="font-medium">Danger zone</p>
        <p className="text-muted-foreground">
          These options are hidden by default because they affect the
          whole workspace.
        </p>
      </div>
    </CollapsibleContent>
  </Collapsible>
</div>
File tree
Collapsibles nest without coordination — each folder is its own root with independent state, unlike an Accordion where one parent owns every item. The caret rotates through group-data-[state=open]:rotate-90 on the trigger.
collapsible.tsx
accordion.tsx
package.json
<Collapsible defaultOpen>
  <CollapsibleTrigger className="group flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground">
    <CaretRightIcon className="size-4 text-muted-foreground transition-transform group-data-[state=open]:rotate-90" />
    <FolderIcon className="size-4 text-muted-foreground" />
    packages/ui
  </CollapsibleTrigger>
  <CollapsibleContent>
    <div className="ms-3 flex flex-col border-s ps-3">
      {/* nested folders and files */}
    </div>
  </CollapsibleContent>
</Collapsible>
RTL
Unlike Accordion, Collapsible has no dir prop — a single trigger has no directional keyboard behavior. Setting dir="rtl" on a wrapping element is enough, as long as the styling uses logical utilities (ps-*, ms-*, border-s).

أضاف @westy ٣ مستودعات إلى المفضلة

@workspace/ui
radix-ui/primitives
tailwindlabs/tailwindcss
<div dir="rtl">
  <Collapsible defaultOpen className="flex w-full flex-col gap-2">
    <div className="flex items-center justify-between gap-4 px-4">
      <h4 className="text-sm font-medium">أضاف @westy ٣ مستودعات إلى المفضلة</h4>
      <CollapsibleTrigger asChild>
        <Button variant="ghost" size="icon-sm">
          <CaretUpDownIcon />
          <span className="sr-only">تبديل</span>
        </Button>
      </CollapsibleTrigger>
    </div>
    <div className="rounded-md border px-4 py-2 font-mono text-sm">
      @workspace/ui
    </div>
    <CollapsibleContent className="flex flex-col gap-2">
      <div className="rounded-md border px-4 py-2 font-mono text-sm">
        radix-ui/primitives
      </div>
    </CollapsibleContent>
  </Collapsible>
</div>
Accessibility
Follows the WAI-ARIA disclosure pattern: the trigger is a button exposing aria-expanded and aria-controls, and the content is removed from the accessibility tree (and hidden) while closed. Enter or Space toggles the panel. All three parts carry data-state="open|closed" and, when disabled, data-disabled for styling.
<div data-slot="collapsible" data-state="open">
  <button
    data-slot="collapsible-trigger"
    data-state="open"
    aria-expanded="true"
    aria-controls="radix-:r1:"
  >
    Toggle
  </button>
  <div data-slot="collapsible-content" data-state="open" id="radix-:r1:">
    Panel content
  </div>
</div>
API Reference
Every part wraps the matching Radix Collapsible primitive and accepts all of its props. The notable ones:
ComponentPropDefault
CollapsibledefaultOpen?: booleanfalse
Collapsibleopen?: boolean, onOpenChange?: (open) => void—
Collapsibledisabled?: booleanfalse
CollapsibleTriggerasChild?: booleanfalse
CollapsibleContentforceMount?: true—
<Collapsible data-slot="collapsible" />          // Root — unstyled state container
<CollapsibleTrigger data-slot="collapsible-trigger" />  // <button aria-expanded>, toggles the panel
<CollapsibleContent data-slot="collapsible-content" />  // the panel, hidden while closed

Tokens used

8 design tokens consumed by the Collapsible examples.

  • --accent

    Accent

    hover:bg-accent
  • --accent-foreground

    Accent Foreground

    hover:text-accent-foreground
  • --muted-foreground

    Muted Foreground

    text-muted-foreground
  • --border

    Border

    borderborder-s
  • --radius-md

    Radius 0.8× (8px)

    rounded-md
  • --font-mono

    Mono font (Geist Mono)

    font-mono
  • --text-sm

    Text Small (14px)

    text-sm
  • --spacing

    Spacing unit (4px)

    gap-2px-4py-2size-4