From cbd95350bbeec3216bc255ec7e7334e84a97bea2 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Tue, 2 Jun 2026 21:38:48 +0300 Subject: [PATCH] fix(popover): stop animating left/top so first open doesn't slide from corner --- src/shared/ui/Popover/Popover.svelte | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/shared/ui/Popover/Popover.svelte b/src/shared/ui/Popover/Popover.svelte index 27e7efc..c1443d1 100644 --- a/src/shared/ui/Popover/Popover.svelte +++ b/src/shared/ui/Popover/Popover.svelte @@ -88,6 +88,14 @@ let resolvedSide = $state(side); */ let positioned = $state(false); +/** + * Resolved fixed-position coordinates. Applied through the reactive `style` + * attribute (not imperatively) so they can't be wiped when the attribute + * re-renders — mixing the two caused a one-frame top-left flash. + */ +let x = $state(0); +let y = $state(0); + /** * Actual DOM open state, driven by the `toggle` event. Source of truth for * whether the browser currently shows the popover; `open` is the public binding. @@ -132,8 +140,8 @@ function updatePosition(): void { sideOffset, }); resolvedSide = result.side; - contentEl.style.left = `${result.x}px`; - contentEl.style.top = `${result.y}px`; + x = result.x; + y = result.y; positioned = true; } @@ -204,9 +212,9 @@ $effect(() => { data-side={resolvedSide} data-state={shown ? 'open' : 'closed'} ontoggle={onToggle} - style={`position: fixed; inset: auto; margin: 0;${positioned ? '' : ' visibility: hidden;'}`} + style={`position: fixed; inset: auto; left: ${x}px; top: ${y}px; margin: 0;${positioned ? '' : ' visibility: hidden;'}`} class={cn( - 'opacity-0 scale-95 transition-discrete transition-all duration-fast', + 'opacity-0 scale-95 transition-discrete transition-[opacity,transform] duration-fast', 'starting:opacity-0 starting:scale-95', '[&:popover-open]:opacity-100 [&:popover-open]:scale-100', 'data-[side=top]:origin-bottom data-[side=bottom]:origin-top',