Skip to content

Commit 7401701

Browse files
authored
fix(sidebar): smooth collapse transitions, mobile rewrite, and new props (#567)
1 parent f831482 commit 7401701

5 files changed

Lines changed: 840 additions & 168 deletions

File tree

.changeset/every-meals-switch.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
"@cloudflare/kumo": minor
3+
---
4+
5+
Sidebar: mobile rewrite, smooth collapse transitions, and new props
6+
7+
**New features:**
8+
9+
- `mobileBreakpoint` prop on Provider — configurable viewport width for mobile detection
10+
- `contentClassName` prop on Sidebar root — pass-through class for the inner content container
11+
- Controlled mobile state — `open` prop now controls the mobile sidebar too, not just desktop
12+
13+
**Fixes:**
14+
15+
- Replaced Base UI Dialog mobile sidebar with a plain `<nav>` + backdrop for simpler, more predictable transitions
16+
- Collapsible sections now animate closed smoothly when the sidebar collapses instead of snapping shut
17+
- Removed `hidden` class from `Sidebar.MenuSub` so sub-menus participate in collapse animations
18+
- Removed `inertValue` React-version helper — `SidebarSlidingView` now sets `inert` imperatively for React 18 compatibility
19+
- Restored `inert` on closed `SidebarCollapsibleContent` while removing its `data-open` attribute
20+
21+
**Styling:**
22+
23+
- `bg-kumo-tint``bg-(--sidebar-active-bg)` CSS variable for active/hover/focus backgrounds
24+
- Icon opacity `0.5``0.4`; chevron gains hover opacity transition
25+
- Header gains `shrink-0` and animated padding on collapse
26+
- Content scroll area gains animated `gap` transition and `tabIndex={-1}` on viewport
27+
- Sliding views container gains `max-w-(--sidebar-width)` to prevent overflow
28+
- Mobile sidebar uses `--sidebar-animation-duration` CSS variable for slide transition

packages/kumo-docs-astro/src/components/demos/SidebarDemo.tsx

Lines changed: 163 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ function AccountSwitcher() {
6767
render={
6868
<button
6969
type="button"
70-
className="cursor-pointer flex w-full min-w-0 items-center gap-2 rounded-lg px-3 group-data-[state=collapsed]/sidebar:px-1.5 py-2 text-left text-sm font-medium text-kumo-default hover:bg-kumo-tint focus-visible:ring-1 focus-visible:ring-kumo-line outline-none transition-[padding] duration-(--sidebar-animation-duration) ease-(--sidebar-easing)"
70+
className="cursor-pointer flex w-full min-w-0 items-center gap-2 rounded-lg px-3 py-2 text-left text-sm font-medium text-kumo-default hover:bg-kumo-tint focus-visible:ring-1 focus-visible:ring-kumo-line outline-none transition-[padding] duration-(--sidebar-animation-duration) ease-(--sidebar-easing)"
7171
>
7272
<active.icon
7373
className="size-4 shrink-0 text-kumo-brand"
@@ -386,7 +386,96 @@ export function SidebarPeekingDemo() {
386386
}
387387

388388
// ---------------------------------------------------------------------------
389-
// 6. Sliding Views — animated horizontal transitions between surfaces
389+
// 6. Auto Scroll — keep long collapsible content in view
390+
// ---------------------------------------------------------------------------
391+
392+
/** Long sidebar where opening a lower collapsible scrolls its revealed content into view. */
393+
export function SidebarAutoScrollDemo() {
394+
return (
395+
<div className="relative h-[420px] w-full overflow-hidden rounded-lg border border-kumo-line bg-kumo-base">
396+
<Sidebar.Provider contained defaultOpen className="min-h-0! h-full">
397+
<Sidebar>
398+
<Sidebar.Header>
399+
<BrandLogo />
400+
</Sidebar.Header>
401+
<Sidebar.Content>
402+
<Sidebar.Group>
403+
<Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>
404+
<Sidebar.Menu>
405+
<Sidebar.MenuButton icon={HouseIcon} active>
406+
Home
407+
</Sidebar.MenuButton>
408+
<Sidebar.MenuButton icon={ChartBarIcon}>
409+
Analytics
410+
</Sidebar.MenuButton>
411+
<Sidebar.MenuButton icon={GlobeIcon}>
412+
Domains
413+
</Sidebar.MenuButton>
414+
</Sidebar.Menu>
415+
</Sidebar.Group>
416+
417+
<Sidebar.Group>
418+
<Sidebar.GroupLabel>Platform</Sidebar.GroupLabel>
419+
<Sidebar.Menu>
420+
<Sidebar.MenuButton icon={DatabaseIcon}>
421+
Storage
422+
</Sidebar.MenuButton>
423+
<Sidebar.MenuButton icon={ShieldCheckIcon}>
424+
Security
425+
</Sidebar.MenuButton>
426+
<Sidebar.MenuButton icon={LockIcon}>
427+
Zero Trust
428+
</Sidebar.MenuButton>
429+
<Sidebar.MenuButton icon={GearIcon}>
430+
Settings
431+
</Sidebar.MenuButton>
432+
</Sidebar.Menu>
433+
</Sidebar.Group>
434+
435+
<Sidebar.Group>
436+
<Sidebar.GroupLabel>Build</Sidebar.GroupLabel>
437+
<Sidebar.Menu>
438+
<Sidebar.MenuItem>
439+
<Sidebar.Collapsible autoScrollOnOpen>
440+
<Sidebar.CollapsibleTrigger
441+
render={
442+
<Sidebar.MenuButton icon={CodeIcon}>
443+
Workers
444+
<Sidebar.MenuChevron />
445+
</Sidebar.MenuButton>
446+
}
447+
/>
448+
<Sidebar.CollapsibleContent>
449+
<Sidebar.MenuSub>
450+
<Sidebar.MenuSubButton>Overview</Sidebar.MenuSubButton>
451+
<Sidebar.MenuSubButton>Deployments</Sidebar.MenuSubButton>
452+
<Sidebar.MenuSubButton>Observability</Sidebar.MenuSubButton>
453+
<Sidebar.MenuSubButton>Settings</Sidebar.MenuSubButton>
454+
</Sidebar.MenuSub>
455+
</Sidebar.CollapsibleContent>
456+
</Sidebar.Collapsible>
457+
</Sidebar.MenuItem>
458+
<Sidebar.MenuButton icon={CubeIcon}>
459+
Containers
460+
<Sidebar.MenuBadge>Beta</Sidebar.MenuBadge>
461+
</Sidebar.MenuButton>
462+
</Sidebar.Menu>
463+
</Sidebar.Group>
464+
</Sidebar.Content>
465+
<Sidebar.Footer>
466+
<Sidebar.Trigger />
467+
</Sidebar.Footer>
468+
</Sidebar>
469+
<DemoMain>
470+
<p>Open Workers near the bottom of the list</p>
471+
</DemoMain>
472+
</Sidebar.Provider>
473+
</div>
474+
);
475+
}
476+
477+
// ---------------------------------------------------------------------------
478+
// 7. Sliding Views — animated horizontal transitions between surfaces
390479
// ---------------------------------------------------------------------------
391480

392481
/** Sidebar with animated sliding views between Account and Zone navigation. */
@@ -477,7 +566,7 @@ export function SidebarSlidingViewsDemo() {
477566
}
478567

479568
// ---------------------------------------------------------------------------
480-
// 7. Full — kitchen sink showcasing every subcomponent
569+
// 8. Full — kitchen sink showcasing every subcomponent
481570
// ---------------------------------------------------------------------------
482571

483572
/** Kitchen sink sidebar showcasing every subcomponent: header with account switcher, groups with labels, collapsible sections with nested expandable, badges, sliding views via Domains, and a footer trigger. */
@@ -502,7 +591,7 @@ export function SidebarFullDemo() {
502591
<Sidebar.MenuButton
503592
icon={MagnifyingGlassIcon}
504593
tooltip="Search"
505-
className="ring ring-kumo-line group-data-[state=collapsed]/sidebar:ring-transparent"
594+
className="ring ring-kumo-line group-data-[state=collapsed]/sidebar:ring-transparent mb-3 group-data-[state=collapsed]/sidebar:mb-0 transition-[margin] duration-(--sidebar-animation-duration)"
506595
>
507596
Quick search&hellip;
508597
</Sidebar.MenuButton>
@@ -642,3 +731,73 @@ export function SidebarFullDemo() {
642731
</DemoContainer>
643732
);
644733
}
734+
735+
// ---------------------------------------------------------------------------
736+
// 9. Mobile — navigation drawer with Escape to close
737+
// ---------------------------------------------------------------------------
738+
739+
function MobileToggleButton() {
740+
const { toggleSidebar, openMobile } = useSidebar();
741+
return (
742+
<button
743+
type="button"
744+
onClick={toggleSidebar}
745+
className="cursor-pointer rounded-lg border border-kumo-line bg-kumo-base px-3 py-1.5 text-base text-kumo-default transition-colors hover:bg-kumo-tint"
746+
>
747+
{openMobile ? "Close sidebar" : "Open sidebar"}
748+
</button>
749+
);
750+
}
751+
752+
/** Mobile sidebar demo. Uses a high `mobileBreakpoint` to force mobile mode at any viewport width. */
753+
export function SidebarMobileDemo() {
754+
return (
755+
<div className="relative h-[540px] w-full overflow-hidden rounded-lg border border-kumo-line bg-kumo-base">
756+
<Sidebar.Provider contained mobileBreakpoint={9999} className="h-full">
757+
<Sidebar>
758+
<Sidebar.Header>
759+
<BrandLogo />
760+
</Sidebar.Header>
761+
<Sidebar.Content>
762+
<Sidebar.Group>
763+
<Sidebar.GroupLabel>Overview</Sidebar.GroupLabel>
764+
<Sidebar.Menu>
765+
<Sidebar.MenuButton icon={HouseIcon} active>
766+
Home
767+
</Sidebar.MenuButton>
768+
<Sidebar.MenuButton icon={ChartBarIcon}>
769+
Analytics
770+
</Sidebar.MenuButton>
771+
<Sidebar.MenuButton icon={GlobeIcon}>
772+
Domains
773+
</Sidebar.MenuButton>
774+
</Sidebar.Menu>
775+
</Sidebar.Group>
776+
777+
<Sidebar.Group>
778+
<Sidebar.GroupLabel>Build</Sidebar.GroupLabel>
779+
<Sidebar.Menu>
780+
<Sidebar.MenuButton icon={CodeIcon}>
781+
Compute
782+
</Sidebar.MenuButton>
783+
<Sidebar.MenuButton icon={DatabaseIcon}>
784+
Storage
785+
</Sidebar.MenuButton>
786+
</Sidebar.Menu>
787+
</Sidebar.Group>
788+
</Sidebar.Content>
789+
<Sidebar.Footer>
790+
<Sidebar.Trigger />
791+
</Sidebar.Footer>
792+
</Sidebar>
793+
<DemoMain>
794+
<MobileToggleButton />
795+
<p>Click the button to open the mobile sidebar</p>
796+
<p className="text-sm text-kumo-subtle">
797+
Press Escape or click the backdrop to close
798+
</p>
799+
</DemoMain>
800+
</Sidebar.Provider>
801+
</div>
802+
);
803+
}

packages/kumo-docs-astro/src/pages/components/sidebar.astro

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import {
1212
SidebarResizableDemo,
1313
SidebarRightDemo,
1414
SidebarPeekingDemo,
15+
SidebarAutoScrollDemo,
1516
SidebarSlidingViewsDemo,
17+
SidebarMobileDemo,
1618
} from "../../components/demos/SidebarDemo";
1719
---
1820

@@ -210,6 +212,28 @@ const { state, isPeeking } = useSidebar();
210212
</ComponentExample>
211213
</div>
212214

215+
<div>
216+
<Heading level={3}>Auto Scroll</Heading>
217+
<p>
218+
Use <code class="rounded bg-kumo-control px-1 py-0.5 text-xs">autoScrollOnOpen</code> on long collapsible sections to keep newly revealed content in view.
219+
This is useful when a group near the bottom of a scrollable sidebar expands below the visible area.
220+
</p>
221+
<ComponentExample code={`<Sidebar.Collapsible autoScrollOnOpen>
222+
<Sidebar.CollapsibleTrigger
223+
render={
224+
<Sidebar.MenuButton icon={CodeIcon}>
225+
Workers <Sidebar.MenuChevron />
226+
</Sidebar.MenuButton>
227+
}
228+
/>
229+
<Sidebar.CollapsibleContent>
230+
<Sidebar.MenuSub>...</Sidebar.MenuSub>
231+
</Sidebar.CollapsibleContent>
232+
</Sidebar.Collapsible>`}>
233+
<SidebarAutoScrollDemo client:load />
234+
</ComponentExample>
235+
</div>
236+
213237
<div>
214238
<Heading level={3}>Sliding Views</Heading>
215239
<p>
@@ -279,6 +303,22 @@ const { state, isPeeking } = useSidebar();
279303
</ComponentExample>
280304
</div>
281305

306+
<div>
307+
<Heading level={3}>Mobile</Heading>
308+
<p>
309+
On narrow viewports the sidebar renders as a navigation drawer. Use <code class="rounded bg-kumo-control px-1 py-0.5 text-xs">mobileBreakpoint</code> to control the threshold.
310+
The drawer uses <code class="rounded bg-kumo-control px-1 py-0.5 text-xs">inert</code> and <code class="rounded bg-kumo-control px-1 py-0.5 text-xs">aria-hidden</code> while closed, moves focus in on open, and supports Escape-to-close.
311+
This demo forces mobile mode via a high breakpoint.
312+
</p>
313+
<ComponentExample code={`<Sidebar.Provider mobileBreakpoint={9999}>
314+
<Sidebar>
315+
<Sidebar.Content>...</Sidebar.Content>
316+
</Sidebar>
317+
</Sidebar.Provider>`}>
318+
<SidebarMobileDemo client:load />
319+
</ComponentExample>
320+
</div>
321+
282322
</div>
283323
</ComponentSection>
284324

@@ -289,7 +329,7 @@ const { state, isPeeking } = useSidebar();
289329
<div>
290330
<h3 class="mb-3 text-lg font-semibold font-mono">Sidebar</h3>
291331
<p>
292-
The main sidebar container. Renders as <code class="rounded bg-kumo-control px-1 py-0.5 text-xs">&lt;aside&gt;</code> on desktop, Dialog sheet on mobile.
332+
The main sidebar container. Renders as <code class="rounded bg-kumo-control px-1 py-0.5 text-xs">&lt;aside&gt;</code> on desktop and a navigation drawer on mobile.
293333
</p>
294334
<PropsTable component="Sidebar" />
295335
</div>

0 commit comments

Comments
 (0)