Accessible animated sidebar component that slides in from the left or right edge of the viewport, with focus management, scroll locking, and nested navigation support. ## Key Components ### `SlidingSidebar` The primary export. Accepts a `SlidingSidebarConfig` object to control open state, position, navigation items, and lifecycle callbacks. ### `SlidingSidebarProps` ```typescript interface SlidingSidebarProps { config: SlidingSidebarConfig // position, isOpen, onClose, items, footer, className } ``` ### Internal Helpers - **`renderMenuItem`** — Recursively renders `NavigationItem` nodes as expandable groups (with animated `AnimatePresence` children) or leaf buttons/links. Supports custom `element` overrides, badges, icons, and active state styling. - **`toggleExpanded`** — Manages a `Set` of expanded parent item IDs. ### Accessibility & UX Hooks | Hook | Purpose | |---|---| | `usePreventScroll` | Locks body scroll while the sidebar is open | | `useFocusTrap` | Traps/restores focus; fires `onClose` on Escape. Non-modal (`contain: false`) so the z-50 header stays reachable | | `useReducedMotion` | Collapses spring animation to instant when the OS prefers reduced motion | | `useHeaderHeight` | Inserts a dynamic spacer so the panel clears the app header | ## Usage Example ```typescript import { SlidingSidebar } from '@openframe/ui' import { useState } from 'react' export function AppShell() { const [open, setOpen] = useState(false) return ( setOpen(false), position: 'left', items: [ { id: 'dashboard', label: 'Dashboard', href: '/dashboard', isActive: true, }, { id: 'settings', label: 'Settings', children: [ { id: 'profile', label: 'Profile', href: '/settings/profile' }, { id: 'billing', label: 'Billing', href: '/settings/billing' }, ], }, ], footer:

v1.0.0

, }} /> ) } ``` > **Z-index note:** The overlay sits at `z-[45]` and the panel at `z-[46]`, keeping both above the footer (`z-[44]`) but below the app header (`z-[50]`) and any modals (`z-50+`). See `ODS_TOKEN_RULES.md` for the full z-index hierarchy.