AppSidebar

A ChatGPT-style collapsible app sidebar. Animates between full-width and icon rail. Logo click expands; collapse button only appears when open. Wrap the layout in AppSidebarProvider and use AppSidebarInset to shift main content.

Installation
# Install @opencosmos/ui to use AppSidebar
pnpm add @opencosmos/ui
Preview
Loading preview...
Customize
— Icon element always visible (32×32). Clicking it toggles open/closed.
— Wordmark shown to the right of the logo when expanded.
Usage
Import
import { AppSidebar } from '@opencosmos/ui';
Example
<AppSidebar>
  Content
</AppSidebar>
Eject
Copy this component into your project for full customization

API Reference

PropTypeDefaultDescription
logotext""Icon element always visible (32×32). Clicking it toggles open/closed.
titletext"OpenCosmos"Wordmark shown to the right of the logo when expanded.

Accessibility

  • Uses <aside> as the root element.
  • Collapse button has aria-label="Collapse sidebar".
  • Logo button aria-label describes the action (expand or app name).
  • Nav items in collapsed state use title + aria-label for tooltip and screen reader.
  • tabIndex={-1} on the collapse button when hidden prevents keyboard access to invisible controls.
  • All transitions are gated by useMotionPreference — intensity 0 gives instant transitions.

Examples

Collapsible Sidebar
Sign-in Prompt (signed-out)

Code Examples

Full Layout

AppSidebarProvider manages open/close state (persisted in localStorage). AppSidebarInset shifts the main content in sync with the sidebar width.

import {
  AppSidebarProvider, AppSidebar, AppSidebarInset, OpenCosmosIcon
} from '@opencosmos/ui';
import { MessageSquare, BookOpen } from 'lucide-react';

export default function AppLayout({ children }) {
  return (
    <AppSidebarProvider defaultOpen={true}>
      <AppSidebar
        logo={<OpenCosmosIcon size={18} />}
        title="OpenCosmos"
        items={[
          { icon: <MessageSquare className="w-4 h-4" />, label: 'Dialog',    href: '/dialog' },
          { icon: <BookOpen     className="w-4 h-4" />, label: 'Knowledge', href: '/knowledge' },
        ]}
        footer={<AuthButton />}
      />
      <AppSidebarInset>
        {children}
      </AppSidebarInset>
    </AppSidebarProvider>
  );
}

Accessing state in children

useAppSidebar() gives any descendant access to the sidebar state.

import { useAppSidebar } from '@opencosmos/ui';

function MyHeader() {
  const { isOpen, toggle } = useAppSidebar();
  return <button onClick={toggle}>{isOpen ? 'Collapse' : 'Expand'}</button>;
}