Aura Design System

Collapsible

An interactive component which expands and collapses content.

Preview

Loading...
import { useState } from "react";
import {
  Collapsible,
  CollapsibleTrigger,
  CollapsibleContent,
} from "@/components/ui/Collapsible";
import { Button } from "@/components/ui/Button";

export function CollapsibleDemo() {
  const [isOpen, setIsOpen] = useState(false);

  const items = [
    "React",
    "TypeScript",
    "Tailwind CSS",
    "Radix UI",
    "Vite",
    "Vitest",
  ];

  return (
    <Collapsible className="w-full" open={isOpen} onOpenChange={setIsOpen}>
      <div className="flex w-full items-center justify-between gap-1">
        <span className="small font-semibold text-gray-12">
          Technologies ({items.length})
        </span>
        <CollapsibleTrigger asChild>
          <Button variant="menu" size="sm">
            {isOpen ? "Hide All" : "Show All"}
          </Button>
        </CollapsibleTrigger>
      </div>
      <CollapsibleContent>
        <ul className="small space-y-0.5 pt-1 font-normal text-gray-11">
          {items.map((item) => (
            <li key={item} className="flex items-center gap-0.5">
              <span
                aria-hidden="true"
                className="size-0.5 shrink-0 rounded-full bg-accent-9"
              />
              {item}
            </li>
          ))}
        </ul>
      </CollapsibleContent>
    </Collapsible>
  );
}

Usage

Open

Loading...
import { useState } from "react";
import {
  Collapsible,
  CollapsibleTrigger,
  CollapsibleContent,
} from "@/components/ui/Collapsible";
import { Button } from "@/components/ui/Button";

export function CollapsibleDemoOpen() {
  const [isOpen, setIsOpen] = useState(true);

  const items = [
    "React",
    "TypeScript",
    "Tailwind CSS",
    "Radix UI",
    "Vite",
    "Vitest",
  ];

  return (
    <Collapsible className="w-full" open={isOpen} onOpenChange={setIsOpen}>
      <div className="flex w-full items-center justify-between gap-1">
        <span className="small font-semibold text-gray-12">
          Technologies ({items.length})
        </span>
        <CollapsibleTrigger asChild>
          <Button variant="menu" size="sm">
            {isOpen ? "Hide All" : "Show All"}
          </Button>
        </CollapsibleTrigger>
      </div>
      <CollapsibleContent>
        <ul className="small space-y-0.5 pt-1 font-normal text-gray-11">
          {items.map((item) => (
            <li key={item} className="flex items-center gap-0.5">
              <span
                aria-hidden="true"
                className="size-0.5 shrink-0 rounded-full bg-accent-9"
              />
              {item}
            </li>
          ))}
        </ul>
      </CollapsibleContent>
    </Collapsible>
  );
}

Controlled

Extract the Preview markup into TechnologiesCollapsible with open and onOpenChange props, then own that state in the parent.

Loading...
import { useState } from "react";

export function CollapsibleDemoControlled() {
  const [isOpen, setIsOpen] = useState(false);

  return <TechnologiesCollapsible open={isOpen} onOpenChange={setIsOpen} />;
}

Installation

Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces

pnpm dlx shadcn@latest add @aura/collapsible

Manual

Install the following dependencies:

pnpm install radix-ui

Copy and paste the class names utility into your utils/class-names.ts file.

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Copy and paste the Collapsible component into your components/ui/Collapsible.tsx file.

"use client";
import { Collapsible as CollapsibleRadix } from "radix-ui";

import { cn } from "@/utils/class-names";

function Collapsible({
  ...props
}: React.ComponentProps<typeof CollapsibleRadix.Root>) {
  return <CollapsibleRadix.Root data-slot="collapsible" {...props} />;
}

function CollapsibleTrigger({
  ...props
}: React.ComponentProps<typeof CollapsibleRadix.CollapsibleTrigger>) {
  return (
    <CollapsibleRadix.CollapsibleTrigger
      data-slot="collapsible-trigger"
      {...props}
    />
  );
}

function CollapsibleContent({
  className,
  ...props
}: React.ComponentProps<typeof CollapsibleRadix.CollapsibleContent>) {
  return (
    <CollapsibleRadix.CollapsibleContent
      className={cn(
        "overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down",
        className
      )}
      data-slot="collapsible-content"
      {...props}
    />
  );
}

export { Collapsible, CollapsibleTrigger, CollapsibleContent };