Unify web-player design tokens and componentize collection headers

Migrates remaining one-off colors (bg-white/[N%], border-white/N, hardcoded
shadows/gradients) to the existing --mf-* tokens so light mode reacts
consistently everywhere, including a fix to --color-hover which was
hardcoded instead of pointing at --mf-surface-2. Extracts CollectionHeader
(album/playlist/mix/liked headers) and AccountMenu (TopBar/MobileHeader)
to remove five and two duplicated implementations respectively, and
normalizes inconsistent empty-state copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 17:53:47 +02:00
co-authored by Claude Sonnet 5
parent 33a9e886be
commit c947cdac1c
40 changed files with 463 additions and 448 deletions
@@ -27,9 +27,9 @@
{:else}
<div
class="pointer-events-none absolute inset-0"
style="background:repeating-linear-gradient(135deg, rgba(255,255,255,0.12) 0 2px, transparent 2px 10px)"
style="background:repeating-linear-gradient(135deg, color-mix(in oklch, var(--mf-text) 12%, transparent) 0 2px, transparent 2px 10px)"
></div>
<span class="relative text-lg font-semibold text-white/90">{initials}</span>
<span class="relative text-lg font-semibold text-fg">{initials}</span>
{/if}
</div>
<div class="w-full truncate text-sm text-fg">{name}</div>
@@ -29,12 +29,12 @@
}: Props = $props();
const sizes = {
sm: 'px-4 py-2.5 text-[12.5px]',
md: 'px-5 py-3 text-[13px]',
sm: 'px-4 py-2.5 text-xs',
md: 'px-5 py-3 text-sm',
lg: 'px-6 py-3.5 text-sm'
};
const subtleButton = 'bg-white/[6%] text-fg-2 hover:bg-white/[9%] hover:text-fg';
const subtleButton = 'bg-surface-2 text-fg-2 hover:bg-surface-hover hover:text-fg';
const variants = {
primary: subtleButton,
@@ -0,0 +1,43 @@
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
eyebrow: string;
title: string;
description?: string;
meta?: string;
align?: 'center' | 'end';
cover: Snippet;
actions: Snippet;
}
let { eyebrow, title, description, meta, align = 'end', cover, actions }: Props = $props();
</script>
<div
class="flex flex-col gap-5 sm:flex-row sm:gap-6 {align === 'end'
? 'mt-3 sm:mt-4 sm:items-end'
: 'sm:items-center'}"
>
{@render cover()}
<div class="min-w-0 flex-1">
<p class="text-xs font-semibold tracking-[0.16em] text-fg-2 uppercase">{eyebrow}</p>
<h1
class="{align === 'end'
? 'mt-1.5'
: 'mt-3'} font-display text-3xl font-semibold tracking-[-0.035em] break-words text-fg sm:text-4xl"
>
{title}
</h1>
{#if description}
<p class="mt-3 max-w-2xl text-sm text-fg-2 sm:text-base">{description}</p>
{/if}
{#if meta}
<p class="mt-3 text-sm text-muted">{meta}</p>
{/if}
</div>
</div>
<div class="mt-6 flex flex-wrap items-center gap-2.5 sm:mt-7">
{@render actions()}
</div>
@@ -23,7 +23,7 @@
</h3>
{/if}
{#if description}
<p class="mt-2.5 max-w-sm text-[13px] leading-[1.65] text-fg-2">{description}</p>
<p class="mt-2.5 max-w-sm text-sm leading-[1.65] text-fg-2">{description}</p>
{/if}
{#if actions}
<div class="mt-6 flex flex-wrap items-center justify-center gap-2.5">{@render actions()}</div>
@@ -7,7 +7,7 @@
</script>
<span
class="grid h-4 w-4 shrink-0 place-items-center rounded-sm bg-fg-3 text-[11px] leading-4 font-bold text-bg {klass}"
class="grid h-4 w-4 shrink-0 place-items-center rounded-sm bg-fg-3 text-xs leading-4 font-bold text-bg {klass}"
title="Contenido explícito"
aria-label="Contenido explícito">E</span
>
@@ -57,6 +57,6 @@
class="h-full w-full object-cover"
/>
{:else}
<Icon class="h-9 w-9 {gradient ? 'text-white/70' : 'text-muted'}" strokeWidth={1.3} />
<Icon class="h-9 w-9 {gradient ? 'text-on-art/70' : 'text-muted'}" strokeWidth={1.3} />
{/if}
</label>
@@ -27,6 +27,6 @@
{@render art(mediaCardArt)}
<div class="mt-2.5 truncate text-sm font-medium text-fg">{title}</div>
{#if subtitle}
<div class="mt-0.5 truncate text-[12.5px] text-fg-3">{subtitle}</div>
<div class="mt-0.5 truncate text-xs text-fg-3">{subtitle}</div>
{/if}
</a>
@@ -24,7 +24,7 @@
}: Props = $props();
const classes = $derived(
`flex w-full items-center gap-3 rounded-control px-2 py-2.5 text-left text-[13.5px] font-medium text-fg-2 transition hover:bg-hover ${klass}`
`flex w-full items-center gap-3 rounded-control px-2 py-2.5 text-left text-sm font-medium text-fg-2 transition hover:bg-hover ${klass}`
);
</script>
@@ -37,15 +37,15 @@
type="button"
onclick={play}
aria-label="Reproducir {mix.title}"
class="absolute right-2.5 bottom-2.5 grid h-9.5 w-9.5 place-items-center rounded-full border border-white/12 bg-ink/76 text-fg opacity-0 backdrop-blur-[8px] transition-opacity duration-150 group-hover/tile:opacity-100"
class="absolute right-2.5 bottom-2.5 grid h-9.5 w-9.5 place-items-center rounded-full border border-on-art/12 bg-ink/76 text-fg opacity-0 backdrop-blur-[8px] transition-opacity duration-150 group-hover/tile:opacity-100"
>
<Play class="h-3.5 w-3.5" fill="currentColor" />
</button>
</div>
<div class="mt-3.25 truncate text-[13px] font-semibold tracking-[-0.01em] text-fg">
<div class="mt-3.25 truncate text-sm font-semibold tracking-[-0.01em] text-fg">
{mix.title}
</div>
{#if mix.subtitle}
<div class="mt-1 truncate text-[11.5px] text-fg-3">{mix.subtitle}</div>
<div class="mt-1 truncate text-xs text-fg-3">{mix.subtitle}</div>
{/if}
</a>
@@ -13,11 +13,11 @@
<div class="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between {klass}">
<div class="min-w-0">
<h1 class="font-display text-[28px] font-semibold tracking-[-0.03em] text-fg sm:text-[32px]">
<h1 class="font-display text-3xl font-semibold tracking-[-0.03em] text-fg sm:text-4xl">
{title}
</h1>
{#if subtitle}
<p class="mt-2 text-[13px] text-fg-2">{subtitle}</p>
<p class="mt-2 text-sm text-fg-2">{subtitle}</p>
{/if}
</div>
{#if actions}
@@ -49,14 +49,14 @@
{#if explicit}
<ExplicitBadge />
{/if}
<span class="truncate text-[13px] font-medium text-fg">{title}</span>
<span class="truncate text-sm font-medium text-fg">{title}</span>
</div>
{#if subtitle}
<div class="mt-0.75 truncate text-[11.5px] text-fg-3">{subtitle}</div>
<div class="mt-0.75 truncate text-xs text-fg-3">{subtitle}</div>
{/if}
</div>
{#if meta}
<span class="shrink-0 text-[11.5px] text-muted tabular-nums">{meta}</span>
<span class="shrink-0 text-xs text-muted tabular-nums">{meta}</span>
{/if}
{/snippet}
@@ -12,7 +12,7 @@
</script>
{#snippet heading()}
<h2 class="font-display text-[17.5px] font-medium tracking-[-0.025em] text-fg">
<h2 class="font-display text-lg font-medium tracking-[-0.025em] text-fg">
{title}
</h2>
{#if subtitle}
@@ -9,6 +9,6 @@
let { class: klass = '', children }: Props = $props();
</script>
<span class="truncate text-center text-[12.5px] text-muted tabular-nums {klass}">
<span class="truncate text-center text-xs text-muted tabular-nums {klass}">
{@render children()}
</span>
@@ -19,7 +19,7 @@
<div
class="group grid track-grid items-center gap-3 rounded-control py-3 pr-4 pl-2 transition-colors sm:gap-8 sm:pr-6 sm:pl-3 {active
? 'bg-accent-tint'
: 'hover:bg-white/[3.2%]'} {klass}"
: 'hover:bg-hover'} {klass}"
style="--mf-track-cols:{table.columns}; --mf-track-cols-mobile:{table.columnsMobile}"
{oncontextmenu}
>
@@ -56,7 +56,7 @@
<div class="overflow-x-auto {klass}">
<div
class="grid track-grid items-center gap-3 border-b border-white/5 pr-4 pb-3 pl-2 text-[10.5px] font-semibold tracking-[0.12em] text-muted uppercase sm:gap-8 sm:pr-6 sm:pl-3"
class="grid track-grid items-center gap-3 border-b border-line pr-4 pb-3 pl-2 text-xs font-semibold tracking-[0.12em] text-muted uppercase sm:gap-8 sm:pr-6 sm:pl-3"
style="--mf-track-cols:{columns}; --mf-track-cols-mobile:{columnsMobile}"
>
{#if index}
@@ -69,6 +69,6 @@
>
{@render badge?.()}
</div>
<ArtistLink name={artist} {ownerUserId} class="mt-0.5 text-[12.5px] text-fg-3" />
<ArtistLink name={artist} {ownerUserId} class="mt-0.5 text-xs text-fg-3" />
</div>
</div>