Handle edge cases invisibly
Pause unseen timers, keep hover steady, never cover the UI in use.
Emil Kowalskiemilkowal.ski
Emil Kowalski@emilkowalskiThat's why there's a `useIsDocumentHidden` hook that checks if the document (tab) is hidden, and if so, it will pause the timer. This can be done with the "visibilitychange" event listener on the document. A small detail that improves the experience.
Emil Kowalski@emilkowalskiIntroducing Sonner - an opinionated toast component for React.
Emil Kowalski@emilkowalskiWhen you were using the Linear Agent and a toast was render, it covered the chat. Now the toast renders next to it and changes position when the chat is open/closed.
From emil-design-eng, Sonner principles.
Fill the gaps between toasts
Hover a stack, then move slowly up across the gaps.
Gaps are dead spaceBad
no :aftercollapsed 0×:after fills each gapGood
::after { height: var(--gap) }collapsed 0×.toast::after {
content: "";
position: absolute;
left: 0;
right: 0;
top: 100%;
height: var(--gap);
}
Pause when nobody can read
Send, then hover a toast or tick Tab hidden.
Timer always runsBad
idle · 0 missedPauses when unseenGood
idle · 0 missedlet remaining = 4000;
let last = performance.now();
const paused = () => hovered || focused || document.hidden;
document.addEventListener("visibilitychange", () => {
last = performance.now();
});
function tick(now) {
if (!paused()) remaining -= now - last;
last = now;
if (remaining <= 0) dismiss();
else requestAnimationFrame(tick);
}
Don't cover the UI in use
Send a few chat messages, then toggle Agent.
Fixed bottom-rightBad
right: 8pxMoves beside the chatGood
translateX(-chat-width).app { container-type: inline-size; --chat-width: 56cqi; }
.toaster { transition: transform 300ms var(--ease-in-out); }
.app[data-chat="open"] .toaster {
transform: translateX(calc(-1 * var(--chat-width)));
}
Why it matters, pros and cons
Why it matters
A toast that expires unseen or hides your input has failed silently.
Pros
- The 4 seconds count only when visible.
- The stack stays open under the pointer.
- Nothing to configure, nothing to get wrong.
Cons
- Paused toasts pile up; cap visible toasts at 3.
- The gap fill can swallow clicks below the stack.
- Critical errors should not be toasts at all.