web-player: single source of truth for theme, no light-mode flash
Remove the duplication of the ~18 per-hue dynamic tokens between
theme.css and buildThemeTokens(): theme.css now only defines fixed
neutrals, surfaces, layout and z-index; hue/mode-dependent values
come solely from tokens.ts, injected via SSR as an inline <style> in
+layout.svelte (tokensToCss helper) and reapplied on every hue
change. app.html sets data-theme before first paint by reading
localStorage, removing the dark flash when light mode was saved.
The two inline-style backgrounds become @utility
app-backdrop/app-vignette; the main's bottom padding becomes
@utility app-main, driven by --mf-dock-h via [data-has-track].
Note: <style>{expr}</style> doesn't interpolate in Svelte (it
compiles as component-scoped CSS) — the SSR block is injected with
{@html `<style>${css}</style>`} since the content is fully generated
(no user input).
Verified in the browser: --mf-accent/--mf-bg resolve correctly during
SSR, data-theme='light' applies before the first render with no
flash.
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
import { liked } from '$lib/player/liked.svelte';
|
||||
import { createPlaylistModal } from '$lib/playlists.svelte';
|
||||
import { ACCENT_HUE } from '$lib/theme/color';
|
||||
import { buildThemeTokens, applyThemeTokens } from '$lib/theme/tokens';
|
||||
import { buildThemeTokens, applyThemeTokens, tokensToCss } from '$lib/theme/tokens';
|
||||
import { themeMode } from '$lib/theme/mode.svelte';
|
||||
import { trackNavigation } from '$lib/navigation.svelte';
|
||||
import { page } from '$app/state';
|
||||
@@ -32,6 +32,8 @@
|
||||
const isAuthPage = $derived(page.url.pathname === '/login');
|
||||
const hasTrack = $derived(player.currentId !== null);
|
||||
|
||||
const initialThemeCss = `:root{${tokensToCss(buildThemeTokens(ACCENT_HUE, 'dark'))}}:root[data-theme='light']{${tokensToCss(buildThemeTokens(ACCENT_HUE, 'light'))}}`;
|
||||
|
||||
let hadTrack = false;
|
||||
$effect(() => {
|
||||
if (hasTrack && !hadTrack) queuePanel.show();
|
||||
@@ -83,7 +85,10 @@
|
||||
|
||||
<svelte:window onkeydown={onWindowKeydown} />
|
||||
|
||||
<svelte:head><link rel="icon" href={favicon} /></svelte:head>
|
||||
<svelte:head>
|
||||
<link rel="icon" href={favicon} />
|
||||
{@html `<style>${initialThemeCss}</style>`}
|
||||
</svelte:head>
|
||||
|
||||
{#if isAuthPage || (!data.user && !data.allowAnonymousListening)}
|
||||
<div class="min-h-screen bg-bg text-fg antialiased">
|
||||
@@ -91,14 +96,8 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="relative flex min-h-screen text-fg antialiased">
|
||||
<div
|
||||
class="pointer-events-none fixed inset-0 -z-10 bg-bg"
|
||||
style="background-image:var(--mf-ambient)"
|
||||
></div>
|
||||
<div
|
||||
class="pointer-events-none fixed inset-0 -z-10"
|
||||
style="background:radial-gradient(130% 85% at 50% 118%, color-mix(in oklch, var(--mf-scrim) 80%, transparent), transparent 62%)"
|
||||
></div>
|
||||
<div class="app-backdrop"></div>
|
||||
<div class="app-vignette"></div>
|
||||
|
||||
<Sidebar user={data.user} playlists={data.sidebarPlaylists ?? []} />
|
||||
|
||||
@@ -106,12 +105,7 @@
|
||||
<div class="flex min-w-0 flex-1 flex-col">
|
||||
<MobileHeader user={data.user} accountUrl={data.accountUrl} />
|
||||
<TopBar user={data.user} accountUrl={data.accountUrl} />
|
||||
<main
|
||||
class="flex-1 transition-[padding]"
|
||||
style="padding-bottom:calc(var(--mf-nav-h) + var(--mf-safe-b) + {hasTrack
|
||||
? 'var(--mf-player-h)'
|
||||
: '0px'})"
|
||||
>
|
||||
<main class="flex-1 app-main" data-has-track={hasTrack ? '' : undefined}>
|
||||
{#key page.url.pathname}
|
||||
<div class="animate-fade">{@render children()}</div>
|
||||
{/key}
|
||||
|
||||
Reference in New Issue
Block a user