Skip to content

Commit 581f3e3

Browse files
Brett Lamyclaude
authored andcommitted
fix(side-drawer): a pushed page takes focus and returns it on pop
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 6239379 commit 581f3e3

1 file changed

Lines changed: 15 additions & 2 deletions

File tree

‎packages/ui/src/components/side-drawer.tsx‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,19 @@ function SideDrawerPush({ open, onClose, title, backLabel, host, hostWidth, chil
171171
const el = host.current;
172172
return () => pageOf(el).forEach((p) => { p.style.transition = ''; p.style.translate = ''; });
173173
}, [host]);
174+
// A pushed page takes focus (so Escape and the keyboard land in it); popping hands it back to where it came from.
175+
const returnTo = useRef<HTMLElement | null>(null);
176+
const focusedOpen = useRef(open);
177+
useEffect(() => {
178+
if (focusedOpen.current === open) return;
179+
focusedOpen.current = open;
180+
if (open) {
181+
returnTo.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
182+
panel.current?.focus({ preventScroll: true });
183+
} else if (panel.current?.contains(document.activeElement)) {
184+
returnTo.current?.focus({ preventScroll: true });
185+
}
186+
}, [open]);
174187

175188
const down = (e: React.PointerEvent) => {
176189
if (e.button || !open) return;
@@ -231,12 +244,12 @@ function SideDrawerPush({ open, onClose, title, backLabel, host, hostWidth, chil
231244
'absolute inset-0 bg-black transition-opacity duration-spring-smooth ease-spring-smooth motion-reduce:transition-none',
232245
open ? 'pointer-events-auto opacity-12' : 'pointer-events-none opacity-0',
233246
)} />
234-
<div ref={panel} data-slot="side-drawer-panel" role="dialog" aria-label={typeof title === 'string' ? title : undefined}
247+
<div ref={panel} data-slot="side-drawer-panel" role="dialog" tabIndex={-1} aria-label={typeof title === 'string' ? title : undefined}
235248
aria-hidden={!open} inert={!open || undefined}
236249
onPointerDownCapture={down} onPointerMove={move} onPointerUp={up} onPointerCancel={up}
237250
onKeyDown={(e) => { if (e.key === 'Escape' && open) { e.stopPropagation(); onClose?.(); } }}
238251
className={cn(
239-
'absolute inset-0 flex touch-pan-y flex-col overflow-hidden bg-background will-change-transform [transform:translateX(var(--side-drawer-x))] transition-transform duration-spring-smooth ease-spring-smooth motion-reduce:transition-none',
252+
'absolute inset-0 flex touch-pan-y flex-col overflow-hidden bg-background outline-none will-change-transform [transform:translateX(var(--side-drawer-x))] transition-transform duration-spring-smooth ease-spring-smooth motion-reduce:transition-none',
240253
open ? 'pointer-events-auto' : 'pointer-events-none',
241254
(open || shown) && 'shadow-[-10px_0_30px_black] shadow-black/16',
242255
!open && !shown && 'invisible',

0 commit comments

Comments
 (0)