✨ feat: responsive collapse and drawer behavior for Sidebar#626
Merged
✨ feat: responsive collapse and drawer behavior for Sidebar#626
Conversation
Adapts the Sidebar to three viewport-driven modes: - `>= 1024px`: expanded (current behavior). - `640-1023px`: collapsed icon-only with auto-injected NavigationSeparator between groups and group titles hidden. - `< 640px`: the aside is replaced by a hamburger trigger that opens the sidebar content inside a left Drawer in expanded form. Introduces: - useMediaQuery hook (SSR-safe) exported from the library. - useSidebarMode + SidebarContext to distribute the active mode to descendants. - Sidebar.Label - auto-hides text labels when collapsed, replacing the manual hidden md:block pattern consumers had to repeat. - HamburgerTrigger button for the drawer mode. - Optional mode prop on Sidebar (auto | expanded | collapsed | drawer) for manual override, tests and stories. Fixes an alignment bug where md: padding on Logo and NavigationOption was driven by the viewport and clashed with the collapsed mode in the 768-1023px range: the paddings are now driven by group-data-[mode=expanded]/sidebar:* selectors so they stay tied to the sidebar mode instead of the viewport.
…n hover reveal - Drag-to-resize: disable transition during drag, set body cursor and prevent text selection, clear inline width when leaving expanded mode. - Expose minWith / maxWith / initialWidth as optional props (defaults match prior hardcoded values: 240 / 300 / 256). - Add expandOnHover (default true) and animateOnHover (default true) props. In collapsed mode, hovering a NavigationOption reveals its Label as an absolute-positioned overlay anchored to the right edge, animating max-width and opacity. The icon stays in place and the auto-injected separator between groups remains unobstructed. - Update Sidebar.test.tsx: split the collapsed-mode label test into a strict "expandOnHover disabled" case and a clipped-in-DOM case for the new default.
…classNames - drawerMaxWidth: cap the drawer panel width on mobile (default 280; width is min(viewport, drawerMaxWidth) so it shrinks on narrow phones). - drawerBreakpoint / expandedBreakpoint: configurable viewport thresholds for auto mode (defaults 640 / 1024). - triggerClassName: forward custom classes to the HamburgerTrigger button rendered in drawer mode. - separatorClassName: applied to the auto-injected NavigationSeparator between groups in collapsed mode (passed via SidebarContext). - Drop the slate-200 border-r the Drawer renders in left position by forcing border-r-0 on the panel — that border was the visible "white line" along the right edge of the mobile drawer.
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
Makes the
Sidebarfully responsive with three viewport-driven modes:>= 1024pxexpanded640 – 1023pxcollapsedNavigationSeparatorauto-injected between groups.< 640pxdrawer<aside>is replaced by a hamburger trigger that opens the sidebar content inside a leftDrawerin expanded form.Design reference (Figma):
What changed
New public API
useMediaQuery(query)— SSR-safe hook, exported from the library.<Sidebar.Label>— auto-hides in collapsed mode, replacing the manualhidden md:blockpattern consumers had to repeat on every label.mode?: 'auto' | 'expanded' | 'collapsed' | 'drawer'prop onSidebar(defaultauto= viewport-driven; the others are manual overrides for tests, stories, or forced layouts).Internal additions
SidebarContext+useSidebarContextfor distributing the active mode to descendants.useSidebarModehook that resolves the mode from the viewport or themodeprop.HamburgerTriggercomponent for the drawer mode.Behavior changes on existing components
NavigationGrouphides its title when collapsed.Navigationauto-injects aNavigationSeparatorbetweenNavigationGroupsiblings when collapsed (without duplicating separators the consumer added manually).Wrappernow resolves the mode and renders either<aside>orHamburgerTrigger+Draweraccordingly.Styling (minimal and targeted)
NavigationSeparator.variants.ts: updated to match the thin horizontal line in the Figma designs (only stylistic change in this PR).Logo.variants.tsandNavigationOption.variants.ts: the padding rules previously tied to themd:viewport breakpoint now usegroup-data-[mode=expanded]/sidebar:*selectors. Values are unchanged — they just follow the sidebar's mode instead of the viewport, which fixes an alignment bug where the768–1023pxrange applied desktop padding inside the collapsed (w-18) aside.Sidebar.variants.ts: width moved fromw-18 md:w-64to amodeCVA variant (w-18/w-64).Story
Sidebar.stories.tsxrefactored to use<Sidebar.Label>andgroup-data-[mode=expanded]/sidebar:*selectors. AddedCollapsedModeandDrawerModestories for manual inspection.Tests
tests/setup.ts: added a defaultwindow.matchMediamock (required by the new hook).Sidebar.test.tsx: new coverage for the three modes — labels/titles visibility, auto-injected separators, hamburger trigger, drawer opening with expanded content.Test plan
npm run check:typespasses.npm run lintpasses.npm testpasses (426 tests).npm run buildsucceeds.In Review/Sidebar→Sidebar:>= 1024pxviewport: sidebar is expanded, labels and group titles visible.640–1023px: sidebar collapses to icons only, a thin line separates groups, no group titles.< 640px: the<aside>disappears, a hamburger button shows in the top-left.Sidebarat< 640px: click the hamburger → aDraweropens from the left with labels and group titles visible. Escape / overlay click / X close it.CollapsedModestory: icons only, separators between groups.DrawerModestory: only the hamburger trigger is visible; clicking it opens the drawer.