refactor(overlay): single overlay-root convention via Overlay helper

Add components/Overlay.tsx (portals to <body>) and route the in-tree full-screen/
floating holdouts through it — ChatDock, OnboardingWizard, GlassTuner — so they
escape any ancestor stacking context (mask/transform/backdrop-filter) instead of
relying on a bare high z-index. Toaster stays in-tree (it's content-column-scoped
by design, outside the masked scroller); Welcome is a page, not an overlay.
This commit is contained in:
2026-07-18 19:11:38 +02:00
parent c91caffa5e
commit 6075e96bd0
4 changed files with 31 additions and 8 deletions
+9 -6
View File
@@ -7,6 +7,7 @@ import { onMessage, onUnread } from "../lib/messagesSocket";
import * as e2ee from "../lib/e2ee";
import Avatar from "./Avatar";
import ChatThread from "./ChatThread";
import Overlay from "./Overlay";
// The Messenger-style floating chat dock: open conversations live here, bottom-right, across
// page navigation (state persisted per user). Always mounted (for human users) so it also owns
@@ -51,11 +52,13 @@ export default function ChatDock({ meId }: { meId: number }) {
if (chats.length === 0) return null;
return (
<div className="fixed bottom-0 right-0 z-40 flex flex-row-reverse items-end gap-3 p-4 pointer-events-none">
{chats.map((c) => (
<ChatWindow key={c.partnerId} chat={c} meId={meId} />
))}
</div>
<Overlay>
<div className="fixed bottom-0 right-0 z-rail flex flex-row-reverse items-end gap-3 p-4 pointer-events-none">
{chats.map((c) => (
<ChatWindow key={c.partnerId} chat={c} meId={meId} />
))}
</div>
</Overlay>
);
}
@@ -65,7 +68,7 @@ function ChatWindow({ chat, meId }: { chat: DockChat; meId: number }) {
<div className="pointer-events-auto relative w-80 max-w-[calc(100vw-2rem)] glass rounded-2xl shadow-2xl flex flex-col overflow-hidden">
{/* One-shot attention flash on a new message; keyed by the counter so it replays each time. */}
{chat.flash > 0 && (
<div key={chat.flash} className="chat-flash pointer-events-none absolute inset-0 rounded-2xl z-10" />
<div key={chat.flash} className="chat-flash pointer-events-none absolute inset-0 rounded-2xl z-base" />
)}
<div className="flex items-center gap-2 px-3 py-2 border-b border-border bg-card/50">
<button