This commit is contained in:
2026-07-24 18:54:31 +02:00
parent 9132e67371
commit 6412a657e7
23 changed files with 187 additions and 175 deletions
@@ -21,7 +21,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 shadow-art ring-1 ring-line transition duration-300 ease-out ring-inset group-hover/artist:-translate-y-1 group-hover/artist:shadow-art-lg"
class="relative grid shrink-0 place-items-center overflow-hidden rounded-full shadow-art ring-1 ring-line transition duration-300 ease-out ring-inset group-hover/artist:shadow-art-lg"
style="width:{size}px;height:{size}px;background:{gradientForHue(hueFor(id))}"
>
<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:-translate-y-1 group-hover:shadow-art-lg';
'aspect-square w-full rounded-art shadow-art transition duration-300 ease-out group-hover:shadow-art-lg';
</script>
<script lang="ts">
@@ -1,7 +1,7 @@
<script lang="ts">
import MixArt from './MixArt.svelte';
import { hueFor } from '$lib/theme/color';
import type { Mix } from '$lib/mixes';
import Cover from './Cover.svelte';
import { gradientForHue, hueFor } from '$lib/theme/color';
import { mixItemKey, type Mix } from '$lib/mixes';
interface Props {
mix: Mix;
@@ -12,35 +12,53 @@
}
let { mix, featured = false, banner = false, class: klass = '', index = 0 }: Props = $props();
const fillCount = $derived(banner ? 40 : 12);
const tiles = $derived.by(() => {
if (mix.items.length === 0) return [];
const out: Mix['items'] = [];
while (out.length < fillCount) out.push(...mix.items);
return out.slice(0, fillCount);
});
</script>
<a
href="/mixes/{mix.id}"
class="group/tile animate-enter relative block overflow-hidden rounded-art shadow-art ring-1 ring-line transition duration-300 ease-out ring-inset hover:shadow-art-lg focus-visible:ring-2 focus-visible:ring-accent focus-visible:outline-none {klass}"
style="animation-delay:{Math.min(index, 10) * 45}ms"
style="animation-delay:{Math.min(index, 10) * 45}ms;background:{gradientForHue(hueFor(mix.id))}"
>
<MixArt
items={mix.items}
hue={hueFor(mix.id)}
class="absolute inset-0 h-full w-full transition duration-500 ease-out group-hover/tile:scale-[1.04]"
/>
{#if tiles.length > 0}
<div
class="pointer-events-none absolute inset-0"
style="background:linear-gradient(to top, rgba(0,0,0,0.78) 0%, rgba(0,0,0,0.32) 42%, rgba(0,0,0,0.05) 100%)"
></div>
class="absolute inset-0 flex transition duration-300 ease-out group-hover/tile:scale-[1.01]"
>
{#each tiles as item, i (i)}
<Cover
trackId={item.trackId ?? item.videoId ?? ''}
src={item.thumbnailUrl}
hue={hueFor(mixItemKey(item))}
size="medium"
class="aspect-square h-full shrink-0"
/>
{/each}
</div>
{/if}
<div
class="pointer-events-none absolute inset-0 bg-black/10 transition-colors duration-300 group-hover/tile:bg-black/20"
></div>
<div
class="relative flex h-full flex-col justify-end p-3 sm:p-4 {banner
class="group relative flex h-full flex-col justify-end p-3 sm:p-4 {banner
? 'sm:flex-row sm:items-center sm:justify-between sm:gap-4'
: ''}"
>
<div class="min-w-0">
<div
class="truncate font-semibold text-white {featured ? 'text-xl sm:text-2xl' : 'text-base'}"
class="truncate text-white {featured ? 'text-xl sm:text-2xl' : 'text-base'} group-hover:text-accent-soft transition-colors"
>
{mix.title}
</div>
{#if mix.subtitle}
<div class="mt-0.5 truncate text-xs text-white/70 sm:text-sm">{mix.subtitle}</div>
<div class="mt-0.5 truncate text-xs text-muted sm:text-sm">{mix.subtitle}</div>
{/if}
</div>
</div>
@@ -11,9 +11,8 @@
</script>
<span
class="absolute right-2.5 bottom-2.5 grid h-11 w-11 translate-y-2 place-items-center rounded-full bg-accent-soft text-on-accent opacity-0 shadow-lg transition-all group-hover:translate-y-0 group-hover:opacity-100"
class="absolute right-2.5 bottom-2.5 grid h-11 w-11 place-items-center rounded-full bg-accent-soft text-on-accent opacity-0 shadow-lg transition-opacity group-hover:opacity-100"
class:!opacity-100={active}
class:!translate-y-0={active}
>
{#if active && playing}
<Pause class="h-5 w-5" fill="currentColor" />
@@ -11,12 +11,16 @@
</script>
{#if actions}
<div class="mb-3 flex items-baseline justify-between gap-4 sm:mb-4 {klass}">
<h2 class="text-lg tracking-tight text-fg sm:text-[28px]">{title}</h2>
<div class="mb-3 flex items-baseline justify-between gap-4 sm:mb-4 {klass} tracking-widest">
<h2 class="font-display font-medium text-fg sm:text-xl">
{title}
</h2>
{@render actions()}
</div>
{:else}
<h2 class="mb-3 text-lg tracking-tight text-fg sm:mb-4 sm:text-[28px] {klass}">
<h2
class="mb-3 font-display font-medium text-fg sm:mb-4 sm:text-xl {klass}"
>
{title}
</h2>
{/if}