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.
This commit is contained in:
2026-09-15 17:53:47 +02:00
parent 7861600081
commit f1756468bc
40 changed files with 463 additions and 448 deletions
@@ -82,7 +82,7 @@
<h2 class="font-display text-xl font-semibold tracking-[-0.02em] text-fg">
¿Eliminar «{playlist.name}»?
</h2>
<p class="mt-2.5 text-[13px] leading-[1.65] text-fg-2">
<p class="mt-2.5 text-sm leading-[1.65] text-fg-2">
Esta acción no se puede deshacer, la playlist se eliminará de tu biblioteca y de todos los
dispositivos donde la tengas guardada.
</p>
@@ -1,10 +1,7 @@
<script lang="ts">
import Play from '@lucide/svelte/icons/play';
import Pause from '@lucide/svelte/icons/pause';
import SquarePencil from '@lucide/svelte/icons/square-pen';
import Trash from '@lucide/svelte/icons/trash';
import PlaylistArt from '$lib/components/ui/PlaylistArt.svelte';
import Button from '$lib/components/ui/Button.svelte';
import CollectionHeader from '$lib/components/ui/CollectionHeader.svelte';
interface Props {
id: string;
@@ -31,47 +28,39 @@
onEdit,
onDelete
}: Props = $props();
const meta = $derived(
`${trackIds.length} ${trackIds.length === 1 ? 'canción' : 'canciones'} · ${
visibility === 'Public' ? 'Pública' : 'Privada'
}`
);
</script>
<div class="flex flex-col gap-5 sm:flex-row sm:items-center sm:gap-6">
<PlaylistArt
playlistId={id}
{trackIds}
size="large"
version={updatedAt}
class="h-36 w-36 shrink-0 rounded-[18px] shadow-[0_24px_56px_rgba(0,0,0,.55)] sm:h-41 sm:w-41"
/>
<div class="min-w-0 flex-1">
<p class="text-[10.5px] font-semibold tracking-[0.16em] text-fg-2 uppercase">Lista</p>
<h1
class="mt-3 font-display text-[28px] font-semibold tracking-[-0.035em] break-words text-fg sm:text-[33px]"
>
{name}
</h1>
{#if description}
<p class="mt-3 max-w-2xl text-sm text-fg-2 sm:text-base">{description}</p>
{/if}
<p class="mt-3.25 text-[12.5px] text-fg-2">
{trackIds.length}
{trackIds.length === 1 ? 'canción' : 'canciones'} · {visibility === 'Public'
? 'Pública'
: 'Privada'}
</p>
</div>
</div>
<div class="mt-6 flex flex-wrap items-center gap-2.5 sm:mt-7">
<Button size="sm" onclick={onPlayAll} disabled={trackIds.length === 0}>
{#if playing}
Pausar
{:else}
Reproducir
{/if}
</Button>
<Button size="sm" variant="secondary" onclick={onEdit}>
Editar
</Button>
<Button size="sm" variant="secondary" onclick={onDelete}>
Eliminar
</Button>
</div>
<CollectionHeader
eyebrow="Lista"
title={name}
description={description ?? undefined}
{meta}
align="center"
>
{#snippet cover()}
<PlaylistArt
playlistId={id}
{trackIds}
size="large"
version={updatedAt}
class="h-36 w-36 shrink-0 rounded-[18px] shadow-cover-lg sm:h-41 sm:w-41"
/>
{/snippet}
{#snippet actions()}
<Button size="sm" onclick={onPlayAll} disabled={trackIds.length === 0}>
{#if playing}
Pausar
{:else}
Reproducir
{/if}
</Button>
<Button size="sm" variant="secondary" onclick={onEdit}>Editar</Button>
<Button size="sm" variant="secondary" onclick={onDelete}>Eliminar</Button>
{/snippet}
</CollectionHeader>