Files
minstrel/web/src/lib/components/CardActionCluster.svelte
T
bvandeusen c9afd4f584
test-web / test (push) Failing after 33s
feat(web): card hover-reveal + drop year + accent rule on section headers
First wave of Home visual polish (UI tasks 78/79/81/85):

- CardActionCluster like/queue/menu cluster is now opacity-0 and fades
  in on group-hover or focus-within. The group class moves from the
  inner anchor to the outer card wrapper so the cluster (positioned
  outside the anchor) participates in the same hover scope. PlaylistCard
  refresh kebab gets the same treatment.
- AlbumCard drops the year line. Title plus artist is enough on Home
  tiles; the album detail page still shows the year.
- HorizontalScrollRow h2 picks up a flex-baseline layout with a trailing
  12-wide accent rule (after:bg-accent/60), so section headers read as
  chapter breaks instead of identical plain text.
- AlbumCard art-wrap is shadow-sm by default and lifts to shadow-lg
  plus ring-accent/40 on hover. Pairs with the existing
  group-hover:scale-1.03 for a clean lift-on-mouseover feel.
2026-06-01 19:54:16 -04:00

68 lines
2.4 KiB
Svelte

<script lang="ts">
import type { Snippet } from 'svelte';
import { Plus } from 'lucide-svelte';
import LikeButton from './LikeButton.svelte';
// Shared chrome for AlbumCard / ArtistCard / CompactTrackCard. Owns
// the top-right Like + add-to-queue cluster and the bottom-right
// menu slot — the bits that were drifting between the three cards
// (focus-ring, play-overlay-adjacent positioning, button styling).
//
// Per-card data fetching + onPlay/onAdd logic stays in the calling
// component; the wrapping element (a vs button) and art container
// also stay there since their differences are load-bearing.
//
// stopPropagation is applied to both wrapper divs so clicks on the
// action elements never bubble to the parent — works for both
// <a>-wrapped cards (AlbumCard/ArtistCard) and <button>-wrapped
// ones (CompactTrackCard).
let {
likeEntityType,
likeEntityId,
onAdd,
addLabel,
addDisabled = false,
menu
}: {
likeEntityType: 'track' | 'album' | 'artist';
likeEntityId: string;
onAdd: (e: MouseEvent) => void;
addLabel: string;
addDisabled?: boolean;
menu: Snippet;
} = $props();
</script>
<!-- Like + Add to queue + Menu cluster. Hover-revealed so the
default tile state is just the cover artwork — controls fade in
when the user hovers the card or focuses any of the buttons
(focus-within keeps it visible during keyboard navigation). -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="absolute right-2 top-2 z-10 flex gap-1 opacity-0 transition-opacity duration-150
group-hover:opacity-100 focus-within:opacity-100"
onclick={(e) => e.stopPropagation()}
>
<LikeButton entityType={likeEntityType} entityId={likeEntityId} />
<button
type="button"
aria-label={addLabel}
onclick={onAdd}
disabled={addDisabled}
class="rounded-full bg-surface px-2 py-1 text-sm text-text-secondary hover:text-text-primary focus-visible:ring-2 focus-visible:ring-accent disabled:opacity-50"
>
<Plus size={16} strokeWidth={1} />
</button>
</div>
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="absolute right-2 bottom-2 z-10 opacity-0 transition-opacity duration-150
group-hover:opacity-100 focus-within:opacity-100"
onclick={(e) => e.stopPropagation()}
>
{@render menu()}
</div>