Polish typography, button states, and login screen

Applies the display typeface (already used for small section labels)
consistently to every major heading — page titles, hero titles on
album/playlist/mix pages, the home hero, the sidebar/mobile wordmark, and
empty-state titles — instead of only the smaller ones, fixing an
inconsistent visual hierarchy. Also fixes a couple of real bugs found along
the way: Input had a focus border color with no border width to show it,
Button/IconButton had no focus-visible ring at all, and SectionHeading's
letter-spacing only applied when it had an actions slot.

Button's secondary/subtle variants drop their harsh border and flat grey
hover for a softer accent-tinted wash. The accent (and the per-track
extracted accent) is desaturated for a calmer, more pastel feel without
touching lightness, so on-accent text contrast is unaffected.

The login screen gets the same treatment plus a subtle ambient glow (reusing
the home page's existing accent-gradient technique) and a fluid,
viewport-scaled headline instead of a few fixed breakpoints, so it fills the
available space on wide screens instead of staying pinned to one size.
This commit is contained in:
2026-09-10 09:56:16 +02:00
parent 92a06a3c15
commit ca04a81e81
18 changed files with 42 additions and 30 deletions
@@ -22,7 +22,7 @@
<header <header
class="sticky top-0 z-40 flex h-14 items-center justify-between border-b border-line bg-bg/85 page-x backdrop-blur-md md:hidden" class="sticky top-0 z-40 flex h-14 items-center justify-between border-b border-line bg-bg/85 page-x backdrop-blur-md md:hidden"
> >
<a href="/" class="text-lg font-semibold tracking-wide">Musify</a> <a href="/" class="font-display text-lg font-semibold tracking-tight text-fg">Musify</a>
<div class="relative" bind:this={menuRef}> <div class="relative" bind:this={menuRef}>
<button <button
+1 -1
View File
@@ -82,7 +82,7 @@
class="sticky top-0 hidden h-screen shrink-0 flex-col gap-7 border-r border-line px-4.5 py-6.5 md:flex" class="sticky top-0 hidden h-screen shrink-0 flex-col gap-7 border-r border-line px-4.5 py-6.5 md:flex"
style="width:var(--mf-sidebar-w)" style="width:var(--mf-sidebar-w)"
> >
<a href="/" class="px-3 text-xl font-medium tracking-wide">Musify</a> <a href="/" class="px-3 font-display text-xl font-semibold tracking-tight text-fg">Musify</a>
<div class="relative flex flex-col gap-7" bind:this={navBox}> <div class="relative flex flex-col gap-7" bind:this={navBox}>
<div <div
@@ -36,13 +36,13 @@
const variants = { const variants = {
primary: 'bg-accent-soft text-on-accent active:scale-[0.97]', primary: 'bg-accent-soft text-on-accent active:scale-[0.97]',
secondary: 'border border-line text-fg hover:bg-hover', secondary: 'bg-surface-2 text-fg hover:bg-accent/12',
subtle: 'bg-surface-2 text-fg hover:bg-surface-hover', subtle: 'bg-white/10 text-fg hover:bg-accent/18',
danger: 'bg-danger text-on-accent hover:brightness-110' danger: 'bg-danger text-on-accent hover:brightness-110'
}; };
const classes = $derived( const classes = $derived(
`inline-flex items-center justify-center gap-2 rounded-control transition disabled:opacity-40 ${sizes[size]} ${variants[variant]} ${klass}` `inline-flex items-center justify-center gap-2 rounded-control font-medium transition focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg focus-visible:outline-none disabled:opacity-40 ${sizes[size]} ${variants[variant]} ${klass}`
); );
</script> </script>
@@ -20,7 +20,7 @@
</span> </span>
{/if} {/if}
{#if title} {#if title}
<h3 class="mt-5 text-2xl font-semibold tracking-tight text-fg">{title}</h3> <h3 class="mt-5 font-display text-2xl font-semibold tracking-tight text-fg">{title}</h3>
{/if} {/if}
{#if description} {#if description}
<p class="mt-2 text-fg-2">{description}</p> <p class="mt-2 text-fg-2">{description}</p>
@@ -29,7 +29,7 @@
{onclick} {onclick}
aria-label={label} aria-label={label}
title={label} title={label}
class="grid place-items-center rounded-control text-muted transition hover:bg-hover hover:text-fg {sizes[ class="grid place-items-center rounded-control text-muted transition hover:bg-hover hover:text-fg focus-visible:bg-hover focus-visible:text-fg focus-visible:ring-2 focus-visible:ring-accent focus-visible:outline-none {sizes[
size size
]} {revealOnHover ]} {revealOnHover
? 'sm:opacity-0 sm:group-hover:opacity-100 sm:focus-visible:opacity-100' ? 'sm:opacity-0 sm:group-hover:opacity-100 sm:focus-visible:opacity-100'
@@ -52,7 +52,7 @@
{autocomplete} {autocomplete}
{oninput} {oninput}
bind:value bind:value
class="w-full rounded-control bg-surface py-3 text-base text-fg transition placeholder:text-muted focus:border-accent/50 focus:outline-none {Icon class="w-full rounded-control border border-line bg-surface py-3 text-base text-fg transition placeholder:text-muted focus:border-accent/60 focus:outline-none {Icon
? 'pr-4 pl-11' ? 'pr-4 pl-11'
: 'px-4'} {klass}" : 'px-4'} {klass}"
/> />
@@ -13,7 +13,7 @@
<div class="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between {klass}"> <div class="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between {klass}">
<div class="min-w-0"> <div class="min-w-0">
<h1 class="text-2xl font-semibold tracking-tight text-fg sm:text-4xl">{title}</h1> <h1 class="font-display text-2xl font-semibold tracking-tight text-fg sm:text-4xl">{title}</h1>
{#if subtitle} {#if subtitle}
<p class="mt-2 text-sm text-fg-2 sm:text-base">{subtitle}</p> <p class="mt-2 text-sm text-fg-2 sm:text-base">{subtitle}</p>
{/if} {/if}
@@ -11,14 +11,14 @@
</script> </script>
{#if actions} {#if actions}
<div class="mb-3 flex items-baseline justify-between gap-4 sm:mb-4 {klass} tracking-widest"> <div class="mb-3 flex items-baseline justify-between gap-4 sm:mb-4 {klass}">
<h2 class="font-display font-medium text-fg sm:text-xl"> <h2 class="font-display font-medium tracking-tight text-fg sm:text-xl">
{title} {title}
</h2> </h2>
{@render actions()} {@render actions()}
</div> </div>
{:else} {:else}
<h2 class="mb-3 font-display font-medium text-fg sm:mb-4 sm:text-xl {klass}"> <h2 class="mb-3 font-display font-medium tracking-tight text-fg sm:mb-4 sm:text-xl {klass}">
{title} {title}
</h2> </h2>
{/if} {/if}
+1 -1
View File
@@ -1,5 +1,5 @@
export const ACCENT_LIGHTNESS = 60; export const ACCENT_LIGHTNESS = 60;
export const ACCENT_CHROMA = 0.17; export const ACCENT_CHROMA = 0.1;
export const ACCENT_HUE = 145; export const ACCENT_HUE = 145;
export const DEFAULT_ACCENT = `oklch(${ACCENT_LIGHTNESS}% ${ACCENT_CHROMA} ${ACCENT_HUE})`; export const DEFAULT_ACCENT = `oklch(${ACCENT_LIGHTNESS}% ${ACCENT_CHROMA} ${ACCENT_HUE})`;
+1 -1
View File
@@ -100,7 +100,7 @@ export async function extractAccent(url: string): Promise<Accent | null> {
if (best < 0) return null; if (best < 0) return null;
const hue = ((Math.atan2(hueSin[best], hueCos[best]) * 180) / Math.PI + 360) % 360; const hue = ((Math.atan2(hueSin[best], hueCos[best]) * 180) / Math.PI + 360) % 360;
const chroma = Math.min(0.19, Math.max(0.1, chromaSum[best] / weight[best])); const chroma = Math.min(0.11, Math.max(0.045, chromaSum[best] / weight[best]));
const h = hue.toFixed(1); const h = hue.toFixed(1);
const c = chroma.toFixed(3); const c = chroma.toFixed(3);
+1 -1
View File
@@ -15,7 +15,7 @@
--mf-border-strong: rgba(255, 255, 255, 0.12); --mf-border-strong: rgba(255, 255, 255, 0.12);
--mf-track: rgba(255, 255, 255, 0.12); --mf-track: rgba(255, 255, 255, 0.12);
--mf-accent: oklch(60% 0.17 145); --mf-accent: oklch(60% 0.1 145);
--mf-accent-soft: color-mix(in oklch, var(--mf-accent) 80%, transparent); --mf-accent-soft: color-mix(in oklch, var(--mf-accent) 80%, transparent);
--mf-scrim: #000000; --mf-scrim: #000000;
@@ -55,7 +55,7 @@
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
<p class="text-sm tracking-[0.14em] text-fg-3 uppercase">Álbum</p> <p class="text-sm tracking-[0.14em] text-fg-3 uppercase">Álbum</p>
<h1 <h1
class="mt-1.5 text-3xl font-semibold tracking-tight break-words text-fg sm:text-5xl md:text-7xl" class="mt-1.5 font-display text-3xl font-semibold tracking-tight break-words text-fg sm:text-5xl md:text-7xl"
> >
{title} {title}
</h1> </h1>
@@ -93,7 +93,7 @@
{album.isSingle ? 'Single' : album.isEp ? 'EP' : 'Álbum'} {album.isSingle ? 'Single' : album.isEp ? 'EP' : 'Álbum'}
</p> </p>
<h1 <h1
class="mt-1.5 text-3xl font-semibold tracking-tight break-words text-fg sm:text-5xl md:text-7xl" class="mt-1.5 font-display text-3xl font-semibold tracking-tight break-words text-fg sm:text-5xl md:text-7xl"
> >
{album.title} {album.title}
</h1> </h1>
@@ -72,7 +72,7 @@
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
<p class="text-sm tracking-[0.14em] text-fg-3 uppercase">Mezcla</p> <p class="text-sm tracking-[0.14em] text-fg-3 uppercase">Mezcla</p>
<h1 <h1
class="mt-1.5 text-3xl font-semibold tracking-tight break-words text-fg sm:text-5xl md:text-7xl" class="mt-1.5 font-display text-3xl font-semibold tracking-tight break-words text-fg sm:text-5xl md:text-7xl"
> >
{mix.title} {mix.title}
</h1> </h1>
@@ -36,7 +36,7 @@
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
<p class="text-sm tracking-[0.14em] text-fg-3 uppercase">Lista</p> <p class="text-sm tracking-[0.14em] text-fg-3 uppercase">Lista</p>
<h1 <h1
class="mt-1.5 text-3xl font-semibold tracking-tight break-words text-fg sm:text-5xl md:text-7xl" class="mt-1.5 font-display text-3xl font-semibold tracking-tight break-words text-fg sm:text-5xl md:text-7xl"
> >
{name} {name}
</h1> </h1>
+1 -1
View File
@@ -155,7 +155,7 @@
></div> ></div>
<div class="animate-enter relative flex flex-col gap-2 sm:gap-2.5"> <div class="animate-enter relative flex flex-col gap-2 sm:gap-2.5">
<p class="text-sm tracking-[0.14em] text-fg-2 uppercase sm:text-base">{data.greeting}</p> <p class="text-sm tracking-[0.14em] text-fg-2 uppercase sm:text-base">{data.greeting}</p>
<h1 class="text-3xl leading-[1.05] font-semibold text-fg sm:text-6xl"> <h1 class="font-display text-3xl leading-[1.05] font-semibold tracking-tight text-fg sm:text-6xl">
Tu música. Sin límites. Tu música. Sin límites.
</h1> </h1>
</div> </div>
+1 -1
View File
@@ -22,7 +22,7 @@
--color-on-accent: var(--mf-on-art); --color-on-accent: var(--mf-on-art);
--color-scrim: var(--mf-scrim); --color-scrim: var(--mf-scrim);
--color-on-art: var(--mf-on-art); --color-on-art: var(--mf-on-art);
--color-danger: oklch(63.7% 0.237 25.331); --color-danger: oklch(63.7% 0.15 25.331);
--color-danger-fg: oklch(88.5% 0.062 18.334); --color-danger-fg: oklch(88.5% 0.062 18.334);
--radius-control: 0.625rem; --radius-control: 0.625rem;
+21 -9
View File
@@ -11,18 +11,30 @@
<meta name="description" content="Inicia sesión o crea una cuenta en Musify." /> <meta name="description" content="Inicia sesión o crea una cuenta en Musify." />
</svelte:head> </svelte:head>
<div class="grid min-h-screen place-items-center bg-bg px-6 text-fg"> <div class="relative grid min-h-screen place-items-center overflow-hidden bg-bg px-6 text-fg">
<div class="flex w-full max-w-110 flex-col items-center gap-9 text-center"> <div
<div class="text-3xl font-semibold tracking-tight">Musify</div> class="pointer-events-none absolute inset-0"
style="background:radial-gradient(ellipse 46rem 28rem at 50% 36%, color-mix(in oklch, var(--color-accent), transparent 82%), transparent 70%)"
></div>
<div> <div class="relative flex w-full max-w-4xl flex-col items-center gap-12 text-center">
<h1 class="text-4xl font-semibold tracking-tight">Tu música. Sin límites.</h1> <div class="font-display text-[clamp(1.5rem,3vw,2.5rem)] font-semibold tracking-tight">
<p class="mt-3 text-base text-fg-2">Inicia sesión para continuar.</p> Musify
</div> </div>
<div class="flex w-full flex-col gap-5"> <div class="animate-enter flex flex-col items-center gap-5">
<h1
class="font-display text-[clamp(2.75rem,9vw,8rem)] leading-[1.05] font-semibold tracking-tight"
>
<span class="block">Tu música.</span>
<span class="block">Sin límites.</span>
</h1>
<p class="text-lg text-fg-2">Inicia sesión para continuar.</p>
</div>
<div class="flex w-full max-w-80 flex-col gap-3">
<Button href={`/auth/login?${returnParam}`} reload size="lg" class="w-full"> <Button href={`/auth/login?${returnParam}`} reload size="lg" class="w-full">
Iniciar Sesión Iniciar sesión
</Button> </Button>
<Button <Button
href={`/auth/login?mode=register&${returnParam}`} href={`/auth/login?mode=register&${returnParam}`}
@@ -31,7 +43,7 @@
size="lg" size="lg"
class="w-full" class="w-full"
> >
Crear Cuenta Crear cuenta
</Button> </Button>
</div> </div>
</div> </div>