fix(mana/web): SessionWarning + SuggestionToast into bottom-stack

Two more centred bottom-anchored toasts had the same problem the
EncryptionIntroBanner had in 2a437a586: their own position: fixed
with hardcoded bottom + transform centring put them in stacking
contexts that the QuickInputBar (z-index 90) either covered up
(SessionWarning, z-index 45 → hidden) or sat under (SuggestionToast,
z-index 9999 → covered the input bar instead).

Both moved into .bottom-stack as .bottom-stack-notification children
in (app)/+layout.svelte, with the parent handling positioning and
the components themselves stripped down to in-flow flex items.

- SessionWarning: was a free-floating element inside (app)/+layout
  but outside the bottom-stack — moved into the stack, kept the
  authStore.isAuthenticated gate so it only renders for logged-in
  users
- SuggestionToast: was mounted in the ROOT layout, but its only
  consumer (automationsStore) is an (app)-only module so the toast
  never made sense on auth/landing pages. Moved into (app) bottom-
  stack, removed from root layout

CSS cleanup in both: dropped position: fixed, bottom, left,
transform, max-width, z-index. Slide-up keyframes rewritten to use
translateY only (no more parent-transform-X to fight with).

Stack order in (app)/+layout.svelte from top to bottom now:
  1. EncryptionIntroBanner  (one-time)
  2. NotificationBar         (guest nudge, conditional)
  3. SessionWarning          (auth-only, conditional)
  4. SuggestionToast         (auto-dismissing, conditional)
  5. QuickInputBar
  6. TagStrip
  7. PillNav

Corner-anchored toasts (PwaUpdatePrompt right-12px, SyncConflictToast
right-1rem) intentionally NOT moved — they live in different visual
real estate and don't compete with the centred stack column.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-08 18:16:55 +02:00
parent 2a437a5861
commit 94ab125fbb
4 changed files with 32 additions and 20 deletions

View file

@ -64,12 +64,10 @@
{/if}
<style>
/* Positioning is the parent's job — this lives inside .bottom-stack
in (app)/+layout.svelte. Used to be position: fixed at bottom 16px
with z-index 45, which the bottom-stack (z-index 90) hid. */
.session-warning {
position: fixed;
bottom: 16px;
left: 50%;
transform: translateX(-50%);
z-index: 45;
display: flex;
align-items: center;
gap: 12px;
@ -85,11 +83,11 @@
@keyframes slideUp {
from {
transform: translateX(-50%) translateY(20px);
transform: translateY(20px);
opacity: 0;
}
to {
transform: translateX(-50%) translateY(0);
transform: translateY(0);
opacity: 1;
}
}

View file

@ -62,12 +62,11 @@
{/if}
<style>
/* Positioning is the parent's job — this lives inside .bottom-stack
in (app)/+layout.svelte. Used to be position: fixed at bottom 1rem
centred with z-index 9999, which sat ON TOP of the QuickInputBar
and covered it. */
.toast {
position: fixed;
bottom: 1rem;
left: 50%;
transform: translateX(-50%);
z-index: 9999;
display: flex;
align-items: center;
gap: 0.75rem;
@ -139,11 +138,11 @@
@keyframes slide-up {
from {
opacity: 0;
transform: translateX(-50%) translateY(1rem);
transform: translateY(1rem);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);
transform: translateY(0);
}
}
</style>

View file

@ -8,6 +8,7 @@
import KeyboardShortcutsModal from '$lib/components/KeyboardShortcutsModal.svelte';
import SessionWarning from '$lib/components/SessionWarning.svelte';
import EncryptionIntroBanner from '$lib/components/EncryptionIntroBanner.svelte';
import SuggestionToast from '$lib/components/SuggestionToast.svelte';
import { locale, _ } from 'svelte-i18n';
import {
PillNavigation,
@ -460,6 +461,23 @@
</div>
{/if}
<!-- Session expiry warning (auth only). Self-gates on the
secondsLeft countdown and only renders inside the stack
when actually warning, so the wrapper is no-op otherwise. -->
{#if authStore.isAuthenticated}
<div class="bottom-stack-notification">
<SessionWarning />
</div>
{/if}
<!-- Cross-module automation suggestions. Lives in the (app)
stack because automationsStore is an (app)-only module
and the toast doesn't make sense on auth/landing pages
anyway. Self-gates on visible state. -->
<div class="bottom-stack-notification">
<SuggestionToast />
</div>
<!-- QuickInputBar with inline nav toggle -->
<QuickInputBar
onSearch={inputBarAdapter.onSearch}
@ -557,10 +575,9 @@
</div>
</main>
<!-- Session expiry warning (auth only) -->
{#if authStore.isAuthenticated}
<SessionWarning />
{/if}
<!-- Session expiry warning lives inside .bottom-stack now (see above)
so it doesn't end up obscured by the QuickInputBar like
EncryptionIntroBanner used to be. -->
<!-- Keyboard shortcuts modal -->
<KeyboardShortcutsModal open={showShortcuts} onclose={() => (showShortcuts = false)} />

View file

@ -9,7 +9,6 @@
import { migrateGuestDataToUser } from '$lib/data/guest-migration';
import { installDataLayerListeners } from '$lib/data/data-layer-listeners';
import { getVaultClient, hasAnyEncryption } from '$lib/data/crypto';
import SuggestionToast from '$lib/components/SuggestionToast.svelte';
import RecoveryCodeUnlockModal from '$lib/components/RecoveryCodeUnlockModal.svelte';
import SyncConflictToast from '$lib/components/SyncConflictToast.svelte';
import OfflineIndicator from '$lib/components/OfflineIndicator.svelte';
@ -106,7 +105,6 @@
</script>
{@render children()}
<SuggestionToast />
<SyncConflictToast />
<OfflineIndicator />
<PwaUpdatePrompt />