feat(sidebar): comprehensive modernization#527
Open
mattrothenberg wants to merge 14 commits into
Open
Conversation
- Replace border-kumo-hairline with border-kumo-line throughout - Replace bg-kumo-hairline with bg-kumo-line on separator - Add --sidebar-animation-duration, --sidebar-easing, --sidebar-bg CSS custom properties - Replace hardcoded duration-250/ease-cubic-bezier with CSS custom property references - Use bg-(--sidebar-bg) on sidebar root containers for theme overridability - Update focus styles: ring-2/ring-kumo-brand → outline-none/text-kumo-strong/bg-kumo-tint - Add opacity-50 to MenuButton icons, remove [&>svg]:text-kumo-subtle override - Update spacing: header h-[58px], content px-3.5, footer h-12, menu gap-y-px, button min-h-8.5 - Replace MenuSub border-l with absolute w-px bg-kumo-line indicator - Change Separator from hr to div with nested border-b - Update width constants: 16rem→16.25rem, 3rem→57px - Add isolate to sidebar root, reduce z-index from z-20 to z-1/z-2 - Slim resize handle from w-1 to w-0.5
Adds data-state, data-side, data-variant, data-collapsible and group/sidebar class to the mobile dialog inner div so collapsible groups and other state-dependent styles work correctly on mobile. Cherry-picked from PR #450.
- Widen hit area to 16px with thin visual line via ::after pseudo-element - Add role=separator, aria-orientation, aria-valuenow/min/max for a11y - Add tabIndex=0 for keyboard focusability - Support Arrow keys (resize by 10px steps), Home (collapse), End (expand to max) - Add focus-visible styling on the visual line
…ontent BREAKING CHANGE: Remove three unused subcomponents to simplify the API surface. - Remove SidebarInput (search trigger button) — consumers should build their own search trigger inline or use CommandPalette - Remove SidebarMenuAction (right-aligned action overlay) — unused in practice - Remove SidebarGroupContent and group-level collapsible props (collapsible, defaultOpen, open, onOpenChange) from SidebarGroup — all collapse behavior is now exclusively at the item level via Sidebar.Collapsible - Remove SidebarGroupCollapsibleContext - Simplify SidebarGroup to a plain flex container - Rework SidebarGroupLabel: collapsed state now shows a thin divider between icon groups (hidden for first group); uses grid-rows animation - Update all JSDoc to remove MenuAction references - Clean up index.ts and src/index.ts exports
…ementation - Remove @base-ui/react/collapsible dependency from sidebar - New SidebarCollapseContext provides isOpen, toggle, contentId to children - SidebarCollapsible: custom forwardRef component with controlled/uncontrolled state, keyboard auto-expand on focus-visible, auto-collapse on blur - SidebarCollapsibleTrigger: uses React.cloneElement to merge aria-expanded, aria-controls, data-open, and onClick onto the render element - SidebarCollapsibleContent: CSS grid-rows-[0fr]/[1fr] animation (no DOM height measurement needed), always mounted, proper aria-hidden + inert - SidebarMenuChevron: reads isOpen from context instead of relying on Base UI data-panel-open attribute; uses size=12 weight=bold for consistency
…Trigger New features: - contained prop: uses absolute (not fixed) positioning for collapsed sidebar, scoped inside bounded parent for demos/embedded sidebars - peekable prop: hovering/focusing collapsed sidebar temporarily expands it; state becomes 'peeking'; moving away collapses back - SidebarState type: 'expanded' | 'collapsed' | 'peeking' - animationDuration prop: configurable animation duration (default 250ms) Architecture: - Two-layer rail/content design: <aside> (rail) width drives layout, inner content-container can overlay when peeking - Footer rendered outside peek zone so hover doesn't trigger peek - Footer now sticky bottom with width tracking via CSS custom properties UI: - SidebarPanelIcon: animated custom SVG replacing Phosphor SidebarSimpleIcon; vertical divider line slides based on open/closed state - SidebarTrigger: now aria-expanded, dynamic aria-label, size-8.5 grid layout - Remove @phosphor-icons/react SidebarSimpleIcon dependency
New subcomponents: - SidebarSlidingViews: container managing activeKey-based horizontal slide transitions between navigation surfaces (e.g., account ↔ zone) - SidebarSlidingView: individual panel; inactive views get aria-hidden, inert, invisible, and pointer-events-none Uses CSS transforms (no motion/react dependency needed) with prefers-reduced-motion support. Animation uses the same --sidebar-easing and --sidebar-animation-duration custom properties as the rest of the sidebar.
- Remove CollapsibleGroupDemo (group-level collapsible removed) - Remove references to SidebarInput, MenuAction, GroupContent - Add PeekingDemo, SlidingViewsDemo showcasing new features - Rewrite FullDemo as kitchen sink with sliding views, nested collapsibles, custom search button, and footer trigger - All demos now use contained prop for bounded demo containers - Update tokens: hairline→line, duration-250→CSS custom properties - Update icon imports: CubeIcon, StackIcon etc. replacing Cloud/Rocket/Flask - Update docs page: remove deprecated sections, add peeking + sliding views
- Inset focus rings (ring-2 ring-inset ring-kumo-brand) on all interactive items - Remove background-color transitions to prevent flash on theme switch - Footer inside content container with mt-auto (fixes collapse float) - MenuSubButton restored to original sizing, flex content span for chevron - No collapsed padding hacks — overflow-hidden clips naturally - Vertical scroll-fade CSS (data-overflowing-y) in kumo-binding.css - SidebarContent overflow detection via ResizeObserver + MutationObserver - Demo: search uses MenuButton with ring class, inside SlidingView
Closed
commit: |
Contributor
Docs PreviewCommit: |
Contributor
Covers: - Export structure (compound component, removed components, variants, displayNames) - Toggle: defaultOpen, trigger click, aria-expanded, controlled onOpenChange - Collapsible: default closed, defaultOpen, toggle click, aria-controls linking, role=region, inert on closed content - Peeking: peekable=false does nothing, mouseEnter triggers peek, mouseLeave stops peek, no peek when expanded - SlidingViews: active view visible, inactive aria-hidden + inert, switching activeKey - ResizeHandle: ARIA separator role, orientation, label, valuemin/max, tabindex - MenuButton: auto-wrap in li, data-active, link rendering - Contained mode: min-h-svh presence/absence
- Remove MutationObserver from SidebarContent — ResizeObserver already fires when content rect changes (collapsible open/close), the MutationObserver on childList+subtree was a performance hazard - Extract inline onBlur handler in SidebarRoot to useCallback - Remove will-change-[width] from rail and content container — animating width triggers layout every frame regardless, will-change just wastes GPU memory by pre-promoting to a compositing layer - Simplify useMemo dep array in Provider — only reactive state values (state, open, openMobile, isMobile, width, isResizing, isPeeking) need to be deps; props and useCallback refs are stable - Fix TS error in CollapsibleTrigger onClick prop typing
- Trigger uses same px-3 padding as MenuButton for icon alignment - Trigger is natural width (not w-full) so it doesn't stretch when expanded - Footer px-2 matches content area padding - Drop MutationObserver (ResizeObserver sufficient for overflow detection) - Extract inline onBlur to useCallback - Remove will-change-[width] (pure GPU memory overhead) - Simplify useMemo dep array to only reactive state values - Fix TS error in CollapsibleTrigger onClick typing
- Replace manual ResizeObserver overflow detection with Base UI ScrollArea - Slim custom scrollbar (w-1.5, rounded thumb, bg-kumo-line) that fades in on scroll/hover - Scroll fade via CSS mask-image driven by Base UI's --scroll-area-overflow-y CSS variables — no JS needed, works with SSR - Remove custom scroll-fade-y keyframes from kumo-binding.css - Stub getAnimations in test for happy-dom compatibility
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
























Summary
Comprehensive sidebar modernization that supersedes #340 and #450. Grafts the valuable changes from both aborted PRs into a clean branch from main, with additional fixes and improvements discovered along the way.
Breaking Changes
Sidebar.Input— consumers should build custom search triggers (demo shows using aMenuButtonwith a ring class)Sidebar.MenuAction— unused in practiceSidebar.GroupContentand group-level collapsible props (collapsible,defaultOpen,open,onOpenChange) fromSidebar.Group— all collapse is now at the item level viaSidebar.Collapsiblegrid-rowsimplementation — same API (Collapsible,CollapsibleTrigger,CollapsibleContent) but no longer re-exports from@base-ui/react/collapsibleSidebarStatetype is now"expanded" | "collapsed" | "peeking"(was"expanded" | "collapsed")New Features
containedproppeekableprop"peeking"animationDurationpropSlidingViews+SlidingViewaria-hidden/inerton inactive viewsSidebarPanelIconSidebarSimpleIcon; vertical divider line slides on open/closeTriggeraria-expanded, dynamicaria-label,size-8.5grid layoutrole="separator",aria-valuenow/min/maxgrid-rowsanimation, keyboard auto-expand onfocus-visible, auto-collapse on blur, properaria-hidden/inertmask-imageanimation on sidebar content (same pattern as tabs)Token & Styling Fixes
border-kumo-hairline→border-kumo-linethroughoutduration-250/ease-[cubic-bezier(...)]→--sidebar-animation-duration/--sidebar-easingCSS custom propertiesbg-kumo-base→bg-(--sidebar-bg)for theme overridabilityisolateon sidebar root withz-1/z-2instead ofz-20/z-50focus-visible:ring-2 ring-inset ring-kumo-brandon all interactive items (inset so overflow-hidden ancestors don't clip)transition-colorson buttons — prevents flash on theme switchdata-state/data-side/data-variant/data-collapsible/group/sidebar16rem→16.25rem,3rem→57pxArchitecture
<aside>(rail) width drives layout; innercontent-containercan overlay when peekingmt-auto— simpler than the split approach in Kumo Sidebar Support #340overflow-hiddenon the container clips text naturally as width transitionsDemo & Docs
CollapsibleGroupDemo(feature removed)PeekingDemo,SlidingViewsDemoFullDemoas kitchen sink with sliding views, nested collapsibles, search button, footer triggercontainedpropCloses
Supersedes #340 and #450.