From 56966838597ff69fdad53d860360ad97729bf4cb Mon Sep 17 00:00:00 2001 From: XShine555 Date: Tue, 15 Sep 2026 18:06:37 +0200 Subject: [PATCH] Fix light-mode regression and finish design-system cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A code-review pass on the previous commit found a real bug: the explore genre tiles used var(--mf-ink) as a decorative gradient endpoint, but that token is meant for text-on-accent and flips to white in light mode, turning the tiles solid white. Replaces it with a new, deliberately theme-invariant --mf-tile-shade token (same category as --mf-cover-grad). Also: raises CollectionHeader's meta-line contrast in light mode (text-muted, ~3:1, to text-fg-2), makes its actions prop optional so pages without conditional actions don't render an empty wrapper, moves AccountMenu.svelte into $lib/components/ui/ per the repo's own componentization rule, adds the aria-expanded TopBar's trigger was missing relative to MobileHeader's, and migrates the profile page (u/[id]/+page.svelte) onto CollectionHeader — a sixth duplicate of the same header pattern that the previous commit touched but missed. --- .../docs/design-system-refactor-plan.md | 34 +++++--- .../src/lib/components/MobileHeader.svelte | 2 +- web-player/src/lib/components/TopBar.svelte | 5 +- .../components/{ => ui}/AccountMenu.svelte | 4 +- .../lib/components/ui/CollectionHeader.svelte | 12 +-- web-player/src/lib/theme/theme.css | 1 + .../src/routes/(app)/explore/+page.svelte | 2 +- .../src/routes/(app)/u/[id]/+page.svelte | 78 +++++++++---------- 8 files changed, 78 insertions(+), 60 deletions(-) rename web-player/src/lib/components/{ => ui}/AccountMenu.svelte (95%) diff --git a/web-player/docs/design-system-refactor-plan.md b/web-player/docs/design-system-refactor-plan.md index 6ada010..ce3d046 100644 --- a/web-player/docs/design-system-refactor-plan.md +++ b/web-player/docs/design-system-refactor-plan.md @@ -20,15 +20,31 @@ > sobre el "chrome" de la app o sobre arte/gradiente saturado, se extrajeron > `lib/components/ui/CollectionHeader.svelte` (portada + título + meta + acciones, usado > en `AlbumHeader`, `PlaylistHeader`, `liked/+page.svelte`, `mixes/[id]/+page.svelte`, -> `albums/external/.../+page.svelte`) y `lib/components/AccountMenu.svelte` (estado de -> apertura + panel del menú de cuenta, usado en `TopBar`/`MobileHeader`), y se corrigió -> `--color-hover` en `layout.css`, que estaba hardcodeado en vez de apuntar a -> `--mf-surface-2` (rompía el modo claro para cualquier `hover:bg-hover`). Pendiente de -> quien retome esto: verificación visual en el navegador con una sesión autenticada real -> (esta sesión no tenía credenciales del IdP de Zitadel ni acceso para activar -> `allowAnonymousListening`), y `lib/components/ui/Rail.svelte`, que el grep de -> huérfanos de §5 marca sin importadores — no se tocó porque no forma parte de este -> barrido, pero conviene confirmarlo a mano y borrarlo si procede. +> `albums/external/.../+page.svelte` y `u/[id]/+page.svelte`) y +> `lib/components/ui/AccountMenu.svelte` (estado de apertura + panel del menú de cuenta, +> usado en `TopBar`/`MobileHeader`), y se corrigió `--color-hover` en `layout.css`, que +> estaba hardcodeado en vez de apuntar a `--mf-surface-2` (rompía el modo claro para +> cualquier `hover:bg-hover`). +> +> **Repaso posterior a la verificación visual** (ya con sesión autenticada): se detectó +> que el fix de §2 anterior para la tarjeta de género de `explore/+page.svelte` estaba +> mal — usaba `var(--mf-ink)` como extremo oscuro del degradado, pero `--mf-ink` es el +> token de "texto sobre botón de acento" y se invierte a blanco en modo claro, rompiendo +> la tarjeta (fondo blanco en vez de degradado oscuro). Se creó un token nuevo, +> `--mf-tile-shade` (deliberadamente **sin** variante de modo claro, igual que +> `--mf-cover-grad`/`--mf-liked-grad`, porque es "arte" decorativo, no "chrome"), y se usa +> ahí en su lugar. También se cambió la línea de meta de `CollectionHeader` de +> `text-muted` a `text-fg-2` (mejor contraste en modo claro; `text-muted` rondaba ~3:1, +> por debajo de WCAG AA para texto normal) y se hizo `actions` opcional para poder migrar +> páginas sin acciones condicionales (como `u/[id]/+page.svelte`) sin renderizar un +> `
` vacío. +> +> Pendiente de quien retome esto: `lib/components/ui/Rail.svelte` no tiene importadores +> (confirmado con el mismo método de §5, no porque §5 lo listara — ese hallazgo es nuevo) +> — no se tocó porque no forma parte de este barrido, pero conviene confirmarlo a mano y +> borrarlo si procede. También queda pendiente repetir la verificación visual (§6) sobre +> los cambios de este repaso: tarjetas de género de Explorar, cabeceras de colección +> (contraste de la línea de meta) y `u/[id]/+page.svelte`. ## 0. Objetivo diff --git a/web-player/src/lib/components/MobileHeader.svelte b/web-player/src/lib/components/MobileHeader.svelte index e3e6b64..8f29005 100644 --- a/web-player/src/lib/components/MobileHeader.svelte +++ b/web-player/src/lib/components/MobileHeader.svelte @@ -4,7 +4,7 @@ import Folder from '@lucide/svelte/icons/folder'; import LogIn from '@lucide/svelte/icons/log-in'; import MenuItem from './ui/MenuItem.svelte'; - import AccountMenu from './AccountMenu.svelte'; + import AccountMenu from './ui/AccountMenu.svelte'; interface Props { user: SessionUser | null; diff --git a/web-player/src/lib/components/TopBar.svelte b/web-player/src/lib/components/TopBar.svelte index ec162db..ccf1ee5 100644 --- a/web-player/src/lib/components/TopBar.svelte +++ b/web-player/src/lib/components/TopBar.svelte @@ -7,7 +7,7 @@ import X from '@lucide/svelte/icons/x'; import ChevronLeft from '@lucide/svelte/icons/chevron-left'; import ChevronRight from '@lucide/svelte/icons/chevron-right'; - import AccountMenu from './AccountMenu.svelte'; + import AccountMenu from './ui/AccountMenu.svelte'; interface Props { user: SessionUser | null; @@ -107,11 +107,12 @@ {accountUrl} panelClass="absolute top-full right-0 z-20 mt-2 w-52 overflow-hidden" > - {#snippet trigger({ toggle })} + {#snippet trigger({ toggle, open })}
-
- {@render actions()} -
+{#if actions} +
+ {@render actions()} +
+{/if} diff --git a/web-player/src/lib/theme/theme.css b/web-player/src/lib/theme/theme.css index c52730f..a585420 100644 --- a/web-player/src/lib/theme/theme.css +++ b/web-player/src/lib/theme/theme.css @@ -36,6 +36,7 @@ --mf-logo-glow: 0 0 20px oklch(0.74 0.145 145 / 0.3); --mf-cover-grad: linear-gradient(150deg, oklch(0.5 0.13 145), oklch(0.16 0.035 145)); --mf-liked-grad: linear-gradient(150deg, oklch(0.4 0.09 330), oklch(0.18 0.04 10)); + --mf-tile-shade: rgba(9, 9, 12, 0.95); --mf-accent: oklch(0.74 0.145 145); --mf-accent-title: oklch(0.87 0.07 145); diff --git a/web-player/src/routes/(app)/explore/+page.svelte b/web-player/src/routes/(app)/explore/+page.svelte index 1a068fe..6c335ab 100644 --- a/web-player/src/routes/(app)/explore/+page.svelte +++ b/web-player/src/routes/(app)/explore/+page.svelte @@ -477,7 +477,7 @@ href={buildHref(genre.query)} class="animate-enter group relative block h-28 overflow-hidden rounded-2xl p-4.25 transition-[filter] hover:brightness-125" style="animation-delay:{Math.min(i, 10) * - 40}ms; background:linear-gradient(140deg, oklch(0.24 0.07 {genre.hue}), var(--mf-ink))" + 40}ms; background:linear-gradient(140deg, oklch(0.24 0.07 {genre.hue}), var(--mf-tile-shade))" >
{genre.label} diff --git a/web-player/src/routes/(app)/u/[id]/+page.svelte b/web-player/src/routes/(app)/u/[id]/+page.svelte index b355f5b..373de0c 100644 --- a/web-player/src/routes/(app)/u/[id]/+page.svelte +++ b/web-player/src/routes/(app)/u/[id]/+page.svelte @@ -8,11 +8,13 @@ import EmptyState from '$lib/components/ui/EmptyState.svelte'; import Button from '$lib/components/ui/Button.svelte'; import ArtistAvatar from '$lib/components/ui/ArtistAvatar.svelte'; + import CollectionHeader from '$lib/components/ui/CollectionHeader.svelte'; let { data, form } = $props(); const profile = $derived(data.profile); const playlists = $derived(data.playlists); + const canFollow = $derived(!data.isOwnProfile && !data.isAnonymous); let following = $state(untrack(() => data.isFollowing)); let submitting = $state(false); @@ -28,47 +30,43 @@ - -
- -
-

Perfil

-

- {profile.name} -

-

- {profile.followersCount} - {profile.followersCount === 1 ? 'seguidor' : 'seguidores'} · {profile.followingCount} siguiendo -

-
-
+{#snippet followAction()} +
{ + submitting = true; + return async ({ update }) => { + await update(); + submitting = false; + }; + }} + > + +
+{/snippet} - {#if !data.isOwnProfile && !data.isAnonymous} -
-
{ - submitting = true; - return async ({ update }) => { - await update(); - submitting = false; - }; - }} - > - -
-
- {/if} + + + {#snippet cover()} + + {/snippet} +