feat(web): dominant-color accent strip on PlayerBar + bigger Home tiles
test-web / test (push) Successful in 36s
test-web / test (push) Successful in 36s
#9 — new lib/media/dominantColor.ts: load cover image, downsample to 1x1 canvas, read pixel. Approximates the dominant tone via the browser's bilinear mean — close enough for an ambient accent without the 5KB ColorThief dependency. Same-origin cover URLs so no CORS dance. Result cached by URL so revisits are free. PlayerBar samples the current track's cover and pipes the resulting rgb into a 2px accent strip above both the compact and desktop variants. Transparent until the first resolve; 300ms transition on colour change so track-skips fade rather than snap. #10 — slight bump to Home tile widths so a typical viewport shows roughly 5-6 across instead of cramming 8-9: Playlists w-56, all remaining AlbumCard rows w-48 (Recently Added + both Rediscover album scrollers). Replaces the wave-2 sizes that were still showing 7-8 across on wider screens.
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
} from '$lib/player/store.svelte';
|
||||
import { formatDuration } from '$lib/media/duration';
|
||||
import { FALLBACK_COVER, coverUrl } from '$lib/media/covers';
|
||||
import { dominantColorFromUrl, rgbToCssString } from '$lib/media/dominantColor';
|
||||
import LikeButton from './LikeButton.svelte';
|
||||
import TrackMenu from './TrackMenu.svelte';
|
||||
|
||||
@@ -44,6 +45,24 @@
|
||||
function onCoverError(e: Event) {
|
||||
(e.currentTarget as HTMLImageElement).src = FALLBACK_COVER;
|
||||
}
|
||||
|
||||
// #9 — dominant-color accent. Sample the current track's cover and
|
||||
// pipe it into a CSS custom property; a 2px strip above the bar
|
||||
// takes on the colour, so the page subtly tracks what's playing.
|
||||
// Cache-backed in dominantColorFromUrl so revisits are free.
|
||||
// Default ('') keeps the strip transparent until the first cover
|
||||
// resolves.
|
||||
let accentRgb = $state('');
|
||||
$effect(() => {
|
||||
const cover = current?.album_id ? coverUrl(current.album_id) : null;
|
||||
if (!cover) {
|
||||
accentRgb = '';
|
||||
return;
|
||||
}
|
||||
dominantColorFromUrl(cover).then((rgb) => {
|
||||
if (rgb) accentRgb = rgbToCssString(rgb);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if current}
|
||||
@@ -59,6 +78,16 @@
|
||||
Play controls maintain 40/48/40 size; like+kebab and column-3 sub-rows
|
||||
are shorter so column 2's bottom edge aligns with column 3's bottom.
|
||||
-->
|
||||
<!-- Dominant-color accent strip — same colour for both compact
|
||||
and desktop variants, only one renders at a time due to the
|
||||
md: breakpoint switch below. Transparent until the first
|
||||
cover resolves; 300ms tween on colour change so track-skips
|
||||
fade rather than snap. -->
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="md:hidden h-[2px] w-full transition-colors duration-300"
|
||||
style={accentRgb ? `background-color: ${accentRgb};` : ''}
|
||||
></div>
|
||||
<div
|
||||
data-testid="player-bar-compact"
|
||||
class="md:hidden flex flex-col gap-1.5 border-t border-border bg-surface px-3 py-2"
|
||||
@@ -224,6 +253,13 @@
|
||||
the redundant PlayerOverflowMenu kebab is no longer mounted (volume,
|
||||
shuffle, repeat, queue all live inline in the right cluster).
|
||||
-->
|
||||
<!-- See compact-variant accent strip above; desktop has its own
|
||||
so both responsive sizes get the same treatment. -->
|
||||
<div
|
||||
aria-hidden="true"
|
||||
class="hidden md:block h-[2px] w-full transition-colors duration-300"
|
||||
style={accentRgb ? `background-color: ${accentRgb};` : ''}
|
||||
></div>
|
||||
<div
|
||||
data-testid="player-bar-desktop"
|
||||
class="hidden md:flex md:flex-row md:min-h-[108px] md:items-center md:gap-4 border-t border-border bg-surface md:px-4 md:py-1.5"
|
||||
|
||||
Reference in New Issue
Block a user