fix(redesign): portal overlays + solid popup surfaces for legibility

Portal EntitySelect/MultiEntitySelect/Modal/Snackbar/EventChart/Hint to
<body> so they escape backdrop-filter ancestors. Replace translucent
glass on popups (IconPicker, IconGridSelect, SearchPalette, Snackbar)
with solid backgrounds and theme-aware light-mode override.
This commit is contained in:
2026-04-25 11:41:25 +03:00
parent 9643fe519e
commit d356e5a3ac
9 changed files with 396 additions and 229 deletions
+93 -39
View File
@@ -2,6 +2,7 @@
import { onMount } from 'svelte';
import MdiIcon from './MdiIcon.svelte';
import { t } from '$lib/i18n';
import { portal } from '$lib/portal';
let { open = false, title = '', onclose, children } = $props<{
open: boolean;
@@ -74,86 +75,139 @@
<svelte:window onkeydown={open ? handleKeydown : undefined} />
{#if open}
<div
class="modal-backdrop"
class:visible
style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 9999; display: flex; align-items: center; justify-content: center;"
onclick={onclose}
onkeydown={handleBackdropKeydown}
role="presentation"
>
<div use:portal class="modal-portal-root">
<div
bind:this={panelEl}
class="modal-panel"
class="modal-backdrop"
class:visible
role="dialog"
aria-modal="true"
aria-labelledby="modal-title-{uniqueId}"
style="background: var(--color-card); border: 1px solid var(--color-border); border-radius: 1rem; width: 100%; max-width: 32rem; max-height: 80vh; margin: 1rem; display: flex; flex-direction: column;"
onclick={(e) => e.stopPropagation()}
onclick={onclose}
onkeydown={handleBackdropKeydown}
role="presentation"
>
<div style="display: flex; align-items: center; justify-content: space-between; padding: 1.5rem 1.5rem 1rem;">
<h3 id="modal-title-{uniqueId}" style="font-size: 1.125rem; font-weight: 600;">{title}</h3>
<button class="modal-close" onclick={onclose} aria-label={t('common.close')}>
<MdiIcon name="mdiClose" size={18} />
</button>
</div>
<div style="padding: 0 1.5rem 1.5rem; overflow-y: auto;">
{@render children()}
<div
bind:this={panelEl}
class="modal-panel"
class:visible
role="dialog"
aria-modal="true"
aria-labelledby="modal-title-{uniqueId}"
onclick={(e) => e.stopPropagation()}
>
<div class="modal-head">
<h3 id="modal-title-{uniqueId}" class="modal-title">{title}</h3>
<button class="modal-close" onclick={onclose} aria-label={t('common.close')}>
<MdiIcon name="mdiClose" size={18} />
</button>
</div>
<div class="modal-body">
{@render children()}
</div>
</div>
</div>
</div>
{/if}
<style>
.modal-portal-root {
position: fixed;
inset: 0;
z-index: 9999;
}
.modal-backdrop {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0);
backdrop-filter: blur(0px);
transition: background 0.25s ease, backdrop-filter 0.25s ease;
}
.modal-backdrop.visible {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
background: rgba(0, 0, 0, 0.55);
backdrop-filter: blur(8px) saturate(120%);
-webkit-backdrop-filter: blur(8px) saturate(120%);
}
.modal-panel {
--modal-solid-bg: #131520;
background: var(--modal-solid-bg);
border: 1px solid var(--color-rule-strong);
border-radius: 18px;
width: 100%;
max-width: 32rem;
max-height: 80vh;
margin: 1rem;
display: flex;
flex-direction: column;
opacity: 0;
transform: translateY(12px) scale(0.97);
transition: opacity 0.25s ease, transform 0.25s ease;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.12),
0 0 0 1px rgba(255, 255, 255, 0.05) inset;
var(--shadow-card),
0 30px 80px -20px rgba(0, 0, 0, 0.6);
position: relative;
overflow: hidden;
}
.modal-panel::after {
content: '';
position: absolute; inset: 0;
border-radius: inherit;
pointer-events: none;
background: linear-gradient(180deg, var(--color-highlight), transparent 30%);
opacity: 0.4;
}
:global([data-theme="dark"]) .modal-panel {
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
0 0 48px var(--color-glow),
0 0 0 1px rgba(255, 255, 255, 0.03) inset;
}
:global([data-theme="light"]) .modal-panel { --modal-solid-bg: #fafafe; }
.modal-panel.visible {
opacity: 1;
transform: translateY(0) scale(1);
}
.modal-head {
position: relative;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.4rem 1.5rem 1rem;
}
.modal-title {
font-family: var(--font-display);
font-weight: 400;
font-size: 1.4rem;
letter-spacing: -0.02em;
color: var(--color-foreground);
margin: 0;
}
.modal-body {
position: relative;
z-index: 1;
padding: 0 1.5rem 1.5rem;
overflow-y: auto;
}
.modal-close {
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
border-radius: 0.5rem;
border: none;
width: 2.1rem;
height: 2.1rem;
border-radius: 10px;
border: 1px solid transparent;
background: transparent;
color: var(--color-muted-foreground);
cursor: pointer;
transition: all 0.2s ease;
transition: all 0.15s ease;
}
.modal-close:hover {
background: var(--color-muted);
background: var(--color-glass-strong);
border-color: var(--color-border);
color: var(--color-foreground);
}
</style>