From a60230b161a520b83fa5fddfcd444a1bf7ed9977 Mon Sep 17 00:00:00 2001 From: Ethan Graf Date: Tue, 2 Jun 2026 21:46:32 -0400 Subject: [PATCH] Esc key always closes modals --- .opencode/skills/textile-design/components.md | 3 ++- src/components/Modal.tsx | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.opencode/skills/textile-design/components.md b/.opencode/skills/textile-design/components.md index c6dd3d5..b16010a 100644 --- a/.opencode/skills/textile-design/components.md +++ b/.opencode/skills/textile-design/components.md @@ -6,9 +6,10 @@ Reference implementations live under `src/components/` unless noted. **Reference:** `Modal.tsx` -- Overlay: dim + optional blur; `role="dialog"`, `aria-modal="true"`, `aria-label`. +- Overlay: dim + optional blur; `role="dialog"`, `aria-modal="true"`, `aria-label`. Pressing `Escape` closes the modal. - Panel: `rounded-lg`, `border-border`, `shadow-xl`, `max-w-[92vw]`. - Header row: `h-11`, `border-b`, title centered, close control `aria-label="Close modal"`, icon `aria-hidden`. +- `CommandPalette` is intentionally independent (not a real modal) and implements its own overlay markup. ## Settings layout diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index f75e90f..31137d9 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,4 +1,4 @@ -import type { ReactNode } from 'react'; +import { useEffect, type ReactNode } from 'react'; type ModalProps = { open: boolean; @@ -19,6 +19,18 @@ export function Modal({ bodyClassName, children, }: ModalProps) { + useEffect(() => { + if (!open) return; + const handleKeyDown = (e: globalThis.KeyboardEvent) => { + if (e.key === 'Escape') { + e.preventDefault(); + onClose(); + } + }; + document.addEventListener('keydown', handleKeyDown); + return () => document.removeEventListener('keydown', handleKeyDown); + }, [open, onClose]); + if (!open) return null; return ( -- 2.51.2