From 85bd428df7d7d9b46703e84718ce0b1ceb0fa248 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Wed, 3 Jun 2026 11:42:26 +0200 Subject: [PATCH] fix: modal stole input focus on every keystroke The focus/scroll-lock effect depended on onClose (a new function each render), so it re-ran on every keystroke and refocused the panel. Split focus-on-open from the Escape listener so inputs in modals keep focus while typing. Co-Authored-By: Claude Opus 4.8 --- src/components/ui/Modal.tsx | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index ca88e0a..9503626 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -17,19 +17,26 @@ const sizes = { sm: 'max-w-sm', md: 'max-w-lg', lg: 'max-w-2xl' } export function Modal({ open, onClose, title, children, footer, size = 'md' }: Props) { const panelRef = useRef(null) + // Lock scroll + focus the panel only when the modal opens (not on every render, + // otherwise typing in an input would keep stealing focus back to the panel). + useEffect(() => { + if (!open) return + const prev = document.body.style.overflow + document.body.style.overflow = 'hidden' + panelRef.current?.focus() + return () => { + document.body.style.overflow = prev + } + }, [open]) + + // Escape-to-close listener kept separate so its onClose dependency can change freely. useEffect(() => { if (!open) return function onKey(e: KeyboardEvent) { if (e.key === 'Escape') onClose() } document.addEventListener('keydown', onKey) - const prev = document.body.style.overflow - document.body.style.overflow = 'hidden' - panelRef.current?.focus() - return () => { - document.removeEventListener('keydown', onKey) - document.body.style.overflow = prev - } + return () => document.removeEventListener('keydown', onKey) }, [open, onClose]) if (!open) return null