Live Demo
Source
Add showRail and an optional
railWidthPx (defaults to 64). Read
isLeftRail from context to render compact
content while in rail mode:
RailExample.tsx
import {
SwipeBarProvider,
SwipeBarLeft,
useSwipeBarContext,
} from "@luciodale/swipe-bar";
function App() {
return (
<SwipeBarProvider>
<Layout />
</SwipeBarProvider>
);
}
function Layout() {
const { openSidebar, closeSidebar, isLeftOpen, isLeftRail } =
useSwipeBarContext();
const showLabels = isLeftOpen;
return (
<div className="flex h-full w-full">
<SwipeBarLeft showRail railWidthPx={64} sidebarWidthPx={240}>
<div className="flex flex-col p-2">
<button
onClick={() => (isLeftOpen ? closeSidebar("left") : openSidebar("left"))}
aria-label={isLeftOpen ? "Collapse" : "Expand"}
>
{isLeftOpen ? "←" : "→"}
</button>
<nav>
<button>
<span aria-hidden="true">▤</span>
{showLabels && <span>Dashboard</span>}
</button>
{/* ...more items */}
</nav>
</div>
</SwipeBarLeft>
<main>{/* page content */}</main>
</div>
);
}How it behaves
-
Mounting with
showRailand nodefaultOpenon desktop renders the rail immediately atrailWidthPx. -
closeSidebaron desktop transitions to the rail; on small viewports it fully hides. -
openSidebaralways lands in the open mode (fullsidebarWidthPx) and clears the rail flag. - The built-in toggle is hidden when the rail is showing on desktop — the rail itself is the affordance. On small viewports, the toggle reappears since the rail is suppressed.
-
Rail items are interactive (
inertis cleared); the focus trap only activates in the open mode. -
Crossing
mediaQueryWidthreconciles automatically. Any open sidebar is closed first (no animation), then the new viewport's close-state takes over: rail on desktop, fully hidden on mobile. Open does not survive a viewport cross — it always resets to the appropriate close-state.