Fix light-mode regression and finish design-system cleanup

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.
This commit is contained in:
2026-09-15 18:06:37 +02:00
parent f1756468bc
commit 5696683859
8 changed files with 78 additions and 60 deletions
@@ -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))"
>
<div class="font-display text-base font-semibold tracking-[-0.02em] text-fg">
{genre.label}
+38 -40
View File
@@ -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 @@
<meta name="description" content="Perfil de {profile.name} en Musify." />
</svelte:head>
<Page>
<div class="flex flex-col gap-5 sm:flex-row sm:items-center sm:gap-6">
<ArtistAvatar name={profile.name} imageUrl={profile.profilePictureUrl} size={144} />
<div class="min-w-0 flex-1">
<p class="text-xs font-semibold tracking-[0.16em] text-fg-2 uppercase">Perfil</p>
<h1
class="mt-3 font-display text-3xl font-semibold tracking-[-0.035em] break-words text-fg sm:text-4xl"
>
{profile.name}
</h1>
<p class="mt-3.25 text-sm text-fg-2">
{profile.followersCount}
{profile.followersCount === 1 ? 'seguidor' : 'seguidores'} · {profile.followingCount} siguiendo
</p>
</div>
</div>
{#snippet followAction()}
<form
method="POST"
action={following ? '?/unfollow' : '?/follow'}
use:enhance={() => {
submitting = true;
return async ({ update }) => {
await update();
submitting = false;
};
}}
>
<Button
type="submit"
size="sm"
variant={following ? 'secondary' : 'primary'}
disabled={submitting}
>
{following ? 'Dejar de seguir' : 'Seguir'}
</Button>
</form>
{/snippet}
{#if !data.isOwnProfile && !data.isAnonymous}
<div class="mt-6 flex items-center gap-2.5 sm:mt-7">
<form
method="POST"
action={following ? '?/unfollow' : '?/follow'}
use:enhance={() => {
submitting = true;
return async ({ update }) => {
await update();
submitting = false;
};
}}
>
<Button
type="submit"
size="sm"
variant={following ? 'secondary' : 'primary'}
disabled={submitting}
>
{following ? 'Dejar de seguir' : 'Seguir'}
</Button>
</form>
</div>
{/if}
<Page>
<CollectionHeader
eyebrow="Perfil"
title={profile.name}
meta="{profile.followersCount} {profile.followersCount === 1
? 'seguidor'
: 'seguidores'} · {profile.followingCount} siguiendo"
align="center"
actions={canFollow ? followAction : undefined}
>
{#snippet cover()}
<ArtistAvatar name={profile.name} imageUrl={profile.profilePictureUrl} size={144} />
{/snippet}
</CollectionHeader>
<div class="mt-9">
<h2 class="font-display text-lg font-semibold tracking-[-0.02em] text-fg">