Skip to content

feat(sidebar): comprehensive modernization#527

Open
mattrothenberg wants to merge 14 commits into
mainfrom
sidebar-modernization
Open

feat(sidebar): comprehensive modernization#527
mattrothenberg wants to merge 14 commits into
mainfrom
sidebar-modernization

Conversation

@mattrothenberg
Copy link
Copy Markdown
Collaborator

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

  • Removed Sidebar.Input — consumers should build custom search triggers (demo shows using a MenuButton with a ring class)
  • Removed Sidebar.MenuAction — unused in practice
  • Removed Sidebar.GroupContent and group-level collapsible props (collapsible, defaultOpen, open, onOpenChange) from Sidebar.Group — all collapse is now at the item level via Sidebar.Collapsible
  • Replaced Base UI Collapsible with custom CSS grid-rows implementation — same API (Collapsible, CollapsibleTrigger, CollapsibleContent) but no longer re-exports from @base-ui/react/collapsible
  • SidebarState type is now "expanded" | "collapsed" | "peeking" (was "expanded" | "collapsed")

New Features

Feature Description
contained prop Absolute (not fixed) positioning for collapsed sidebar — scoped inside bounded parent for demos/embedded sidebars
peekable prop Hover/focus collapsed sidebar to temporarily expand; state becomes "peeking"
animationDuration prop Configurable animation timing (default 250ms)
SlidingViews + SlidingView Animated horizontal transitions between navigation surfaces (e.g., account ↔ zone) with aria-hidden/inert on inactive views
Animated SidebarPanelIcon Custom SVG replacing Phosphor SidebarSimpleIcon; vertical divider line slides on open/close
Enhanced Trigger aria-expanded, dynamic aria-label, size-8.5 grid layout
Keyboard resize handle Arrow keys (10px steps), Home (collapse), End (expand to max); role="separator", aria-valuenow/min/max
Custom Collapsible CSS grid-rows animation, keyboard auto-expand on focus-visible, auto-collapse on blur, proper aria-hidden/inert
Vertical scroll fade CSS scroll-driven mask-image animation on sidebar content (same pattern as tabs)

Token & Styling Fixes

  • border-kumo-hairlineborder-kumo-line throughout
  • Hardcoded duration-250 / ease-[cubic-bezier(...)]--sidebar-animation-duration / --sidebar-easing CSS custom properties
  • bg-kumo-basebg-(--sidebar-bg) for theme overridability
  • isolate on sidebar root with z-1/z-2 instead of z-20/z-50
  • Focus: focus-visible:ring-2 ring-inset ring-kumo-brand on all interactive items (inset so overflow-hidden ancestors don't clip)
  • No transition-colors on buttons — prevents flash on theme switch
  • No padding transitions — width clip handles collapse, zero visual jank
  • Mobile sidebar gets correct data-state/data-side/data-variant/data-collapsible/group/sidebar
  • Updated width constants: 16rem16.25rem, 3rem57px

Architecture

  • Two-layer rail/content: <aside> (rail) width drives layout; inner content-container can overlay when peeking
  • Footer stays inside content container with mt-auto — simpler than the split approach in Kumo Sidebar Support #340
  • Collapsed state: no special button styles — overflow-hidden on the container clips text naturally as width transitions

Demo & Docs

  • Removed CollapsibleGroupDemo (feature removed)
  • Added PeekingDemo, SlidingViewsDemo
  • Rewrote FullDemo as kitchen sink with sliding views, nested collapsibles, search button, footer trigger
  • All demos use contained prop
  • Updated docs page: removed deprecated sections, added peeking + sliding views

Closes

Supersedes #340 and #450.


  • Reviews
  • bonk has reviewed the change
  • automated review not possible because: comprehensive sidebar rewrite touching tokens, architecture, and API surface
  • Tests
  • Tests included/updated
  • Additional testing not necessary because: manual visual testing completed across all demo variants, light/dark mode, collapsed/expanded/peeking states, keyboard navigation, and resize handle

- 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
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 20, 2026

npm i https://pkg.pr.new/@cloudflare/kumo@527

commit: 9739cc0

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

Docs Preview

View docs preview

Commit: 1832ff4

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

Visual Regression Report — 8 changed, 30 unchanged

8 screenshot(s) with visual changes:

Button / Variant: Secondary

314 px (0.31%) changed

Before After Diff
Before After Diff

Button / Loading State

4 px (0%) changed

Before After Diff
Before After Diff

Dialog / Dialog Alert

354 px (0.35%) changed

Before After Diff
Before After Diff

Dialog / Dialog Confirmation

707 px (0.7%) changed

Before After Diff
Before After Diff

Dialog (Open)

0 px (0%) changed

Before After Diff
Before After Diff

Select / Select With Tooltip

294 px (0.29%) changed

Before After Diff
Before After Diff

Select / Select Loading

0 px (0%) changed

Before After Diff
Before After Diff

Select (Open)

1,506 px (0.01%) changed

Before After Diff
Before After Diff
30 screenshot(s) unchanged
  • Button / Basic
  • Button / Variant: Primary
  • Button / Variant: Ghost
  • Button / Variant: Destructive
  • Button / Variant: Outline
  • Button / Variant: Secondary Destructive
  • Button / Sizes
  • Button / With Icon
  • Button / Icon Only
  • Button / Disabled State
  • Button / Title
  • Button / Link as Button
  • Dialog / Dialog With Actions
  • Dialog / Dialog Basic
  • Dialog / Dialog With Select
  • Dialog / Dialog With Combobox
  • Dialog / Dialog With Dropdown
  • Select / Select Basic
  • Select / Select Sizes
  • Select / Select Without Label
  • Select / Select With Field
  • Select / Select Placeholder
  • Select / Select Custom Rendering
  • Select / Select Multiple
  • Select / Select Complex
  • Select / Select Disabled Options
  • Select / Select Disabled Items
  • Select / Select Grouped
  • Select / Select Grouped With Disabled
  • Select / Select Long List

Generated by Kumo Visual Regression

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant