Design Prot Merge #1
@@ -20,15 +20,31 @@
|
|||||||
> sobre el "chrome" de la app o sobre arte/gradiente saturado, se extrajeron
|
> sobre el "chrome" de la app o sobre arte/gradiente saturado, se extrajeron
|
||||||
> `lib/components/ui/CollectionHeader.svelte` (portada + título + meta + acciones, usado
|
> `lib/components/ui/CollectionHeader.svelte` (portada + título + meta + acciones, usado
|
||||||
> en `AlbumHeader`, `PlaylistHeader`, `liked/+page.svelte`, `mixes/[id]/+page.svelte`,
|
> en `AlbumHeader`, `PlaylistHeader`, `liked/+page.svelte`, `mixes/[id]/+page.svelte`,
|
||||||
> `albums/external/.../+page.svelte`) y `lib/components/AccountMenu.svelte` (estado de
|
> `albums/external/.../+page.svelte` y `u/[id]/+page.svelte`) y
|
||||||
> apertura + panel del menú de cuenta, usado en `TopBar`/`MobileHeader`), y se corrigió
|
> `lib/components/ui/AccountMenu.svelte` (estado de apertura + panel del menú de cuenta,
|
||||||
> `--color-hover` en `layout.css`, que estaba hardcodeado en vez de apuntar a
|
> usado en `TopBar`/`MobileHeader`), y se corrigió `--color-hover` en `layout.css`, que
|
||||||
> `--mf-surface-2` (rompía el modo claro para cualquier `hover:bg-hover`). Pendiente de
|
> estaba hardcodeado en vez de apuntar a `--mf-surface-2` (rompía el modo claro para
|
||||||
> quien retome esto: verificación visual en el navegador con una sesión autenticada real
|
> cualquier `hover:bg-hover`).
|
||||||
> (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
|
> **Repaso posterior a la verificación visual** (ya con sesión autenticada): se detectó
|
||||||
> huérfanos de §5 marca sin importadores — no se tocó porque no forma parte de este
|
> que el fix de §2 anterior para la tarjeta de género de `explore/+page.svelte` estaba
|
||||||
> barrido, pero conviene confirmarlo a mano y borrarlo si procede.
|
> 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
|
||||||
|
> `<div>` 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
|
## 0. Objetivo
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import Folder from '@lucide/svelte/icons/folder';
|
import Folder from '@lucide/svelte/icons/folder';
|
||||||
import LogIn from '@lucide/svelte/icons/log-in';
|
import LogIn from '@lucide/svelte/icons/log-in';
|
||||||
import MenuItem from './ui/MenuItem.svelte';
|
import MenuItem from './ui/MenuItem.svelte';
|
||||||
import AccountMenu from './AccountMenu.svelte';
|
import AccountMenu from './ui/AccountMenu.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: SessionUser | null;
|
user: SessionUser | null;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import X from '@lucide/svelte/icons/x';
|
import X from '@lucide/svelte/icons/x';
|
||||||
import ChevronLeft from '@lucide/svelte/icons/chevron-left';
|
import ChevronLeft from '@lucide/svelte/icons/chevron-left';
|
||||||
import ChevronRight from '@lucide/svelte/icons/chevron-right';
|
import ChevronRight from '@lucide/svelte/icons/chevron-right';
|
||||||
import AccountMenu from './AccountMenu.svelte';
|
import AccountMenu from './ui/AccountMenu.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: SessionUser | null;
|
user: SessionUser | null;
|
||||||
@@ -107,11 +107,12 @@
|
|||||||
{accountUrl}
|
{accountUrl}
|
||||||
panelClass="absolute top-full right-0 z-20 mt-2 w-52 overflow-hidden"
|
panelClass="absolute top-full right-0 z-20 mt-2 w-52 overflow-hidden"
|
||||||
>
|
>
|
||||||
{#snippet trigger({ toggle })}
|
{#snippet trigger({ toggle, open })}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={toggle}
|
onclick={toggle}
|
||||||
aria-label="Tu cuenta"
|
aria-label="Tu cuenta"
|
||||||
|
aria-expanded={open}
|
||||||
class="block h-8.25 w-8.25 shrink-0 overflow-hidden rounded-full border border-line-strong transition active:scale-95"
|
class="block h-8.25 w-8.25 shrink-0 overflow-hidden rounded-full border border-line-strong transition active:scale-95"
|
||||||
>
|
>
|
||||||
{#if user.picture}
|
{#if user.picture}
|
||||||
|
|||||||
+2
-2
@@ -6,8 +6,8 @@
|
|||||||
import Settings from '@lucide/svelte/icons/settings';
|
import Settings from '@lucide/svelte/icons/settings';
|
||||||
import Sun from '@lucide/svelte/icons/sun';
|
import Sun from '@lucide/svelte/icons/sun';
|
||||||
import Moon from '@lucide/svelte/icons/moon';
|
import Moon from '@lucide/svelte/icons/moon';
|
||||||
import MenuItem from './ui/MenuItem.svelte';
|
import MenuItem from './MenuItem.svelte';
|
||||||
import GlassMenu from './ui/GlassMenu.svelte';
|
import GlassMenu from './GlassMenu.svelte';
|
||||||
import { themeMode } from '$lib/theme/mode.svelte';
|
import { themeMode } from '$lib/theme/mode.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
meta?: string;
|
meta?: string;
|
||||||
align?: 'center' | 'end';
|
align?: 'center' | 'end';
|
||||||
cover: Snippet;
|
cover: Snippet;
|
||||||
actions: Snippet;
|
actions?: Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { eyebrow, title, description, meta, align = 'end', cover, actions }: Props = $props();
|
let { eyebrow, title, description, meta, align = 'end', cover, actions }: Props = $props();
|
||||||
@@ -33,11 +33,13 @@
|
|||||||
<p class="mt-3 max-w-2xl text-sm text-fg-2 sm:text-base">{description}</p>
|
<p class="mt-3 max-w-2xl text-sm text-fg-2 sm:text-base">{description}</p>
|
||||||
{/if}
|
{/if}
|
||||||
{#if meta}
|
{#if meta}
|
||||||
<p class="mt-3 text-sm text-muted">{meta}</p>
|
<p class="mt-3 text-sm text-fg-2">{meta}</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if actions}
|
||||||
<div class="mt-6 flex flex-wrap items-center gap-2.5 sm:mt-7">
|
<div class="mt-6 flex flex-wrap items-center gap-2.5 sm:mt-7">
|
||||||
{@render actions()}
|
{@render actions()}
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
--mf-logo-glow: 0 0 20px oklch(0.74 0.145 145 / 0.3);
|
--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-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-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: oklch(0.74 0.145 145);
|
||||||
--mf-accent-title: oklch(0.87 0.07 145);
|
--mf-accent-title: oklch(0.87 0.07 145);
|
||||||
|
|||||||
@@ -477,7 +477,7 @@
|
|||||||
href={buildHref(genre.query)}
|
href={buildHref(genre.query)}
|
||||||
class="animate-enter group relative block h-28 overflow-hidden rounded-2xl p-4.25 transition-[filter] hover:brightness-125"
|
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) *
|
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">
|
<div class="font-display text-base font-semibold tracking-[-0.02em] text-fg">
|
||||||
{genre.label}
|
{genre.label}
|
||||||
|
|||||||
@@ -8,11 +8,13 @@
|
|||||||
import EmptyState from '$lib/components/ui/EmptyState.svelte';
|
import EmptyState from '$lib/components/ui/EmptyState.svelte';
|
||||||
import Button from '$lib/components/ui/Button.svelte';
|
import Button from '$lib/components/ui/Button.svelte';
|
||||||
import ArtistAvatar from '$lib/components/ui/ArtistAvatar.svelte';
|
import ArtistAvatar from '$lib/components/ui/ArtistAvatar.svelte';
|
||||||
|
import CollectionHeader from '$lib/components/ui/CollectionHeader.svelte';
|
||||||
|
|
||||||
let { data, form } = $props();
|
let { data, form } = $props();
|
||||||
|
|
||||||
const profile = $derived(data.profile);
|
const profile = $derived(data.profile);
|
||||||
const playlists = $derived(data.playlists);
|
const playlists = $derived(data.playlists);
|
||||||
|
const canFollow = $derived(!data.isOwnProfile && !data.isAnonymous);
|
||||||
|
|
||||||
let following = $state(untrack(() => data.isFollowing));
|
let following = $state(untrack(() => data.isFollowing));
|
||||||
let submitting = $state(false);
|
let submitting = $state(false);
|
||||||
@@ -28,25 +30,7 @@
|
|||||||
<meta name="description" content="Perfil de {profile.name} en Musify." />
|
<meta name="description" content="Perfil de {profile.name} en Musify." />
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Page>
|
{#snippet followAction()}
|
||||||
<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>
|
|
||||||
|
|
||||||
{#if !data.isOwnProfile && !data.isAnonymous}
|
|
||||||
<div class="mt-6 flex items-center gap-2.5 sm:mt-7">
|
|
||||||
<form
|
<form
|
||||||
method="POST"
|
method="POST"
|
||||||
action={following ? '?/unfollow' : '?/follow'}
|
action={following ? '?/unfollow' : '?/follow'}
|
||||||
@@ -67,8 +51,22 @@
|
|||||||
{following ? 'Dejar de seguir' : 'Seguir'}
|
{following ? 'Dejar de seguir' : 'Seguir'}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
{/snippet}
|
||||||
{/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">
|
<div class="mt-9">
|
||||||
<h2 class="font-display text-lg font-semibold tracking-[-0.02em] text-fg">
|
<h2 class="font-display text-lg font-semibold tracking-[-0.02em] text-fg">
|
||||||
|
|||||||
Reference in New Issue
Block a user