mirror of
https://github.com/layer-systems/website.git
synced 2026-09-12 05:33:12 +02:00
fix: keep a window in place when focus switches (#16)
Raising a window made it visibly jump to the top-left corner and animate back. Two things combined: the enter keyframes animated `transform`, which overrode the inline `translate3d` a window's position is applied with, and the window layer rendered the z-sorted list, so focusing a window moved its DOM node and made the browser replay that animation. Animate the standalone `scale` property instead — it composes with the inline transform rather than replacing it — scope the animation to `.os-window` (it was hitting every child of the layer, including the snap preview, which is positioned the same way), and render windows in their stable creation order, since the inline `z-index` already handles stacking. Fixes #13 Claude-Session: https://claude.ai/code/session_01Trku191Ww2a2YmDWQF3fTS Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,11 +24,17 @@ function SnapPreview({ zone }: { zone: Exclude<SnapZone, null> }) {
|
||||
}
|
||||
|
||||
export function WindowLayer() {
|
||||
const { windows, focusedId } = useWindowManager();
|
||||
// Deliberately the unsorted list. `windows` from the context is sorted by
|
||||
// z-order, so raising a window reorders the DOM nodes; the browser then
|
||||
// replays the enter animation on the moved element. Stacking is already
|
||||
// handled by the inline `z-index` each frame sets, so render order is free
|
||||
// to stay stable.
|
||||
const { state, focusedId } = useWindowManager();
|
||||
const windows = state.windows;
|
||||
const [snap, setSnap] = useState<SnapZone>(null);
|
||||
|
||||
return (
|
||||
<div className="os-window-enter pointer-events-none absolute inset-0">
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
{snap && <SnapPreview zone={snap} />}
|
||||
|
||||
{windows.map((win) => {
|
||||
|
||||
@@ -308,14 +308,20 @@ body.os-dragging .os-window-content {
|
||||
|
||||
/* ------------------------------------------------------------- Animations */
|
||||
|
||||
/*
|
||||
* Windows carry their position in an inline `transform: translate3d(...)`, so
|
||||
* the opening animation must not touch `transform` — animating it would pin
|
||||
* the window at 0,0 for the duration. The standalone `scale` property
|
||||
* composes with the inline transform instead of replacing it.
|
||||
*/
|
||||
@keyframes os-window-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.96);
|
||||
scale: 0.96;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
scale: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +334,7 @@ body.os-dragging .os-window-content {
|
||||
}
|
||||
}
|
||||
|
||||
.os-window-enter > * {
|
||||
.os-window {
|
||||
animation: os-window-in 160ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
@@ -339,9 +345,6 @@ body.os-dragging .os-window-content {
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.os-window {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.os-window-enter > * {
|
||||
animation: os-fade-in 120ms ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user