@@ -249,6 +249,10 @@ function Sidebar({
249249 side === 'left'
250250 ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
251251 : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]' ,
252+ // Hover line: 2px pseudo-element overlaying the border edge (no layout shift).
253+ 'after:pointer-events-none after:absolute after:inset-y-0 after:z-10 after:w-0.5 after:bg-stroke after:opacity-0 after:transition-opacity after:duration-150 after:content-[""]' ,
254+ 'group-data-[side=left]:after:-right-px group-data-[side=right]:after:-left-px' ,
255+ 'has-[[data-sidebar=rail]:hover]:after:opacity-100' ,
252256 // Adjust the padding for floating and inset variants.
253257 variant === 'floating' || variant === 'inset'
254258 ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
@@ -299,29 +303,51 @@ function SidebarTrigger({
299303 )
300304}
301305
302- function SidebarRail ( { className, ...props } : React . ComponentProps < 'button' > ) {
306+ function SidebarRail ( {
307+ className,
308+ tooltip,
309+ ...props
310+ } : React . ComponentProps < 'button' > & { tooltip ?: React . ReactNode } ) {
303311 const { toggleSidebar } = useSidebar ( )
304312
305- return (
313+ const rail = (
306314 < button
307315 data-sidebar = "rail"
308316 data-slot = "sidebar-rail"
309317 aria-label = "Toggle Sidebar"
310318 tabIndex = { - 1 }
311319 onClick = { toggleSidebar }
312- title = "Toggle Sidebar"
313320 className = { cn (
314- 'hover:after:bg-stroke absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] sm:flex' ,
321+ 'absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex' ,
322+ // Hover line is drawn on the Sidebar container (has-[rail:hover]); the rail
323+ // itself is just the hit area.
315324 'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize' ,
316325 '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize' ,
317- 'hover:group-data-[collapsible=offcanvas]:bg-bg group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full ' ,
326+ 'hover:group-data-[collapsible=offcanvas]:bg-bg group-data-[collapsible=offcanvas]:translate-x-0' ,
318327 '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2' ,
319328 '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2' ,
320329 className
321330 ) }
322331 { ...props }
323332 />
324333 )
334+
335+ if ( ! tooltip ) {
336+ return rail
337+ }
338+
339+ return (
340+ < Tooltip delayDuration = { 300 } >
341+ < TooltipTrigger asChild > { rail } </ TooltipTrigger >
342+ < TooltipContent
343+ side = "right"
344+ align = "center"
345+ className = "flex items-center gap-2 py-1.5 pr-2 normal-case"
346+ >
347+ { tooltip }
348+ </ TooltipContent >
349+ </ Tooltip >
350+ )
325351}
326352
327353function SidebarInset ( { className, ...props } : React . ComponentProps < 'main' > ) {
0 commit comments