Checkpoint web-player design-system refactor and backend track genre tagging
Advances the ongoing token/componentization cleanup (anchored player dock, border-based panels instead of floating shadows, corrected light-mode accent/gradient tokens) alongside in-progress backend support for track genre tags.
This commit is contained in:
@@ -70,7 +70,7 @@
|
||||
required={requireCover}
|
||||
icon={Disc3}
|
||||
gradient
|
||||
class="h-36 w-36 rounded-[16px]"
|
||||
class="h-36 w-36 rounded-2xl"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
|
||||
@@ -15,10 +15,6 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<p
|
||||
class="rounded-control border px-4 py-3 text-sm shadow-menu backdrop-blur-md {tones[
|
||||
tone
|
||||
]} {klass}"
|
||||
>
|
||||
<p class="rounded-control border px-4 py-3 text-sm backdrop-blur-md {tones[tone]} {klass}">
|
||||
{@render children()}
|
||||
</p>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<div class="flex flex-col items-center gap-2.5 text-center" style="width:{size}px">
|
||||
<div
|
||||
class="relative grid shrink-0 place-items-center overflow-hidden rounded-full bg-surface shadow-art ring-1 ring-line transition duration-300 ease-out ring-inset group-hover/artist:shadow-art-lg"
|
||||
class="relative grid shrink-0 place-items-center overflow-hidden rounded-full bg-surface ring-1 ring-line transition duration-300 ease-out ring-inset"
|
||||
style="width:{size}px;height:{size}px"
|
||||
>
|
||||
{#if imageUrl}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
};
|
||||
|
||||
const classes = $derived(
|
||||
`inline-flex items-center justify-center gap-2 rounded-xl font-semibold 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}`
|
||||
`inline-flex items-center justify-center gap-2 rounded-xl 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>
|
||||
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
|
||||
<div class="flex flex-col items-center rounded-panel px-6 py-16 text-center {klass}">
|
||||
{#if Icon}
|
||||
<Icon class="h-9 w-9 text-fg-2" strokeWidth={1.3} />
|
||||
<Icon class="h-9 w-9 text-fg-2" strokeWidth={1.25} />
|
||||
{/if}
|
||||
{#if title}
|
||||
<h3 class="mt-6 font-display text-xl font-semibold tracking-[-0.025em] text-fg">
|
||||
<h3 class="mt-6 font-display text-xl font-medium tracking-tight text-fg">
|
||||
{title}
|
||||
</h3>
|
||||
{/if}
|
||||
{#if description}
|
||||
<p class="mt-2.5 max-w-sm text-sm leading-[1.65] text-fg-2">{description}</p>
|
||||
<p class="mt-2.5 max-w-sm text-sm leading-relaxed 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>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
const delays = [0, 0.3, 0.6];
|
||||
</script>
|
||||
|
||||
<div class="flex items-end justify-center gap-[2px] {klass}" style="height:{size}px">
|
||||
<div class="flex items-end justify-center gap-0.5 {klass}" style="height:{size}px">
|
||||
{#each delays as delay (delay)}
|
||||
<span
|
||||
class="rounded-full bg-accent"
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
let { class: klass = '', style = '', children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="glass-panel animate-pop rounded-panel p-1.5 shadow-menu {klass}" {style}>
|
||||
<div class="glass-panel animate-pop rounded-panel border border-hairline p-1.5 {klass}" {style}>
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts" module>
|
||||
export const mediaCardArt =
|
||||
'aspect-square w-full rounded-art shadow-art transition duration-300 ease-out group-hover:shadow-art-lg';
|
||||
'aspect-square w-full rounded-art transition duration-300 ease-out';
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -25,7 +25,7 @@
|
||||
{oncontextmenu}
|
||||
>
|
||||
{@render art(mediaCardArt)}
|
||||
<div class="mt-2.5 truncate text-sm font-medium text-fg">{title}</div>
|
||||
<div class="mt-2.5 truncate text-fg">{title}</div>
|
||||
{#if subtitle}
|
||||
<div class="mt-0.5 truncate text-xs text-fg-3">{subtitle}</div>
|
||||
{/if}
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
{onclick}
|
||||
class={classes}
|
||||
>
|
||||
<Icon class="h-[18px] w-[18px] shrink-0" strokeWidth={1.8} />
|
||||
<Icon class="h-4.5 w-4.5 shrink-0" strokeWidth={1.8} />
|
||||
{label}
|
||||
</a>
|
||||
{:else}
|
||||
<button {type} {onclick} class={classes}>
|
||||
<Icon class="h-[18px] w-[18px] shrink-0" strokeWidth={1.8} />
|
||||
<Icon class="h-4.5 w-4.5 shrink-0" strokeWidth={1.8} />
|
||||
{label}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
class="group/tile animate-enter block cursor-pointer {klass}"
|
||||
style="animation-delay:{Math.min(index, 10) * 45}ms"
|
||||
>
|
||||
<div class="relative aspect-square overflow-hidden rounded-art shadow-art">
|
||||
<div class="relative aspect-square overflow-hidden rounded-art">
|
||||
<MixArt
|
||||
items={mix.items}
|
||||
class="h-full w-full scale-100 transition duration-300 group-hover/tile:scale-105"
|
||||
@@ -37,7 +37,7 @@
|
||||
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-on-art/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 transition-opacity duration-150 group-hover/tile:opacity-100"
|
||||
>
|
||||
<Play class="h-3.5 w-3.5" fill="currentColor" />
|
||||
</button>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
>
|
||||
<div
|
||||
bind:this={panel}
|
||||
class="animate-pop max-h-[85dvh] w-full overflow-y-auto {maxWidth} rounded-panel p-5 shadow-2xl backdrop-blur-[22px] transition-[background] duration-[600ms] outline-none sm:p-6 {panelClass}"
|
||||
class="animate-pop max-h-[85dvh] w-full overflow-y-auto {maxWidth} rounded-panel border border-hairline p-5 backdrop-blur-xl transition-[background] duration-500 outline-none sm:p-6 {panelClass}"
|
||||
style="background-color:var(--mf-panel-bg); background-image:var(--mf-modal-glow)"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
@@ -87,7 +87,7 @@
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div class="min-w-0">
|
||||
{#if eyebrow}
|
||||
<p class="mb-2.5 text-xs font-semibold tracking-[0.1em] text-muted uppercase">
|
||||
<p class="mb-2.5 text-xs font-semibold tracking-widest text-muted uppercase">
|
||||
{eyebrow}
|
||||
</p>
|
||||
{/if}
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
const delays = [0, 0.3, 0.6];
|
||||
</script>
|
||||
|
||||
<div class="absolute inset-0 flex items-end justify-center gap-[3px] bg-scrim/45 pb-2.5">
|
||||
<div class="absolute inset-0 flex items-end justify-center gap-0.75 bg-scrim/45 pb-2.5">
|
||||
{#each delays as delay (delay)}
|
||||
<span
|
||||
class="w-[3px] origin-bottom rounded-full bg-on-art"
|
||||
style="height:14px; animation:equalize .9s ease-in-out {delay}s infinite; animation-play-state:{paused
|
||||
class="h-3.5 w-0.75 origin-bottom rounded-full bg-on-art"
|
||||
style="animation:equalize .9s ease-in-out {delay}s infinite; animation-play-state:{paused
|
||||
? 'paused'
|
||||
: 'running'}"
|
||||
></span>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
let { id, name, trackCount, trackIds, updatedAt, index = 0 }: Props = $props();
|
||||
|
||||
const subtitle = $derived(`${trackCount} ${trackCount === 1 ? 'canción' : 'canciones'}`);
|
||||
const subtitle = $derived(`${trackCount} ${trackCount === 1 ? 'Canción' : 'Canciones'}`);
|
||||
</script>
|
||||
|
||||
<MediaCard href="/playlists/{id}" title={name} {subtitle} {index}>
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
fallbackUrl={coverFallbackUrl}
|
||||
icon={Music}
|
||||
gradient
|
||||
class="h-36 w-36 rounded-[16px]"
|
||||
class="h-36 w-36 rounded-2xl"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-4 rounded-[14px] bg-surface px-4 py-3.5">
|
||||
<div class="flex items-center justify-between gap-4 rounded-art bg-surface px-4 py-3.5">
|
||||
<div class="min-w-0">
|
||||
<div class="text-sm font-medium text-fg-2">Visibilidad</div>
|
||||
<div class="mt-0.5 text-xs text-muted">
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
const RADIUS: Record<SearchResultKind, string> = {
|
||||
track: 'rounded-control',
|
||||
album: 'rounded-[6px]',
|
||||
playlist: 'rounded-[13px]',
|
||||
album: 'rounded-md',
|
||||
playlist: 'rounded-art',
|
||||
user: 'rounded-full'
|
||||
};
|
||||
|
||||
@@ -37,9 +37,7 @@
|
||||
art
|
||||
}: Props = $props();
|
||||
|
||||
const artClass = $derived(
|
||||
`relative h-13 w-13 shrink-0 overflow-hidden shadow-art ${RADIUS[kind]}`
|
||||
);
|
||||
const artClass = $derived(`relative h-13 w-13 shrink-0 overflow-hidden ${RADIUS[kind]}`);
|
||||
</script>
|
||||
|
||||
{#snippet content()}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</script>
|
||||
|
||||
{#snippet heading()}
|
||||
<h2 class="font-display text-lg font-medium tracking-[-0.025em] text-fg">
|
||||
<h2 class="font-display text-lg font-medium tracking-tight text-fg">
|
||||
{title}
|
||||
</h2>
|
||||
{#if subtitle}
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Hash } from '@lucide/svelte';
|
||||
|
||||
import { setContext } from 'svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
@@ -31,7 +33,7 @@
|
||||
index ? '32px' : '',
|
||||
'minmax(180px,1fr)',
|
||||
...meta.map((column) => column.width),
|
||||
'64px',
|
||||
'104px',
|
||||
action ? '36px' : ''
|
||||
]
|
||||
.filter((width) => width !== '')
|
||||
@@ -56,17 +58,17 @@
|
||||
|
||||
<div class="overflow-x-auto {klass}">
|
||||
<div
|
||||
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"
|
||||
class="grid track-grid items-center gap-3 border-b border-line pr-4 pb-3 pl-2 text-sm text-fg-3 sm:gap-8 sm:pr-6 sm:pl-3"
|
||||
style="--mf-track-cols:{columns}; --mf-track-cols-mobile:{columnsMobile}"
|
||||
>
|
||||
{#if index}
|
||||
<span class="text-center">#</span>
|
||||
<Hash size={16} strokeWidth={1.5} />
|
||||
{/if}
|
||||
<span>Título</span>
|
||||
<span class="transition-colors">Título</span>
|
||||
{#each meta as column (column.label)}
|
||||
<span class="hidden text-center sm:block">{column.label}</span>
|
||||
<span class="hidden text-center transition-colors sm:block">{column.label}</span>
|
||||
{/each}
|
||||
<span class="hidden text-center sm:block">Duración</span>
|
||||
<span class="hidden text-center transition-colors sm:block">Duración</span>
|
||||
{#if action}
|
||||
<span></span>
|
||||
{/if}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
artist,
|
||||
ownerUserId,
|
||||
coverSrc,
|
||||
coverSize = 'h-[42px] w-[42px]',
|
||||
coverSize = 'h-10.5 w-10.5',
|
||||
explicit = false,
|
||||
active = false,
|
||||
titleClass = '',
|
||||
@@ -39,12 +39,17 @@
|
||||
event.preventDefault();
|
||||
onClick();
|
||||
}
|
||||
|
||||
function onclick(event: MouseEvent) {
|
||||
event.stopPropagation();
|
||||
onClick();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick={onClick}
|
||||
{onclick}
|
||||
onkeydown={onKeydown}
|
||||
class="flex min-w-0 items-center gap-3.5 text-left"
|
||||
>
|
||||
@@ -62,10 +67,8 @@
|
||||
{#if explicit}
|
||||
<ExplicitBadge />
|
||||
{/if}
|
||||
<span
|
||||
class="min-w-0 truncate text-sm font-medium {active
|
||||
? 'text-accent-soft'
|
||||
: 'text-fg'} {titleClass}">{title}</span
|
||||
<span class="min-w-0 truncate text-sm {active ? 'text-accent-soft' : 'text-fg'} {titleClass}"
|
||||
>{title}</span
|
||||
>
|
||||
{@render badge?.()}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user