style(TrackListRow): elevate visual fidelity to premium standards

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
senke 2026-02-07 09:13:34 +01:00
parent 6a8ef31582
commit aeec29caa1
2 changed files with 43 additions and 21 deletions

View file

@ -24,7 +24,15 @@ const mockTrack = {
const meta = {
title: 'Components/Features/Tracks/TrackListRow',
component: TrackListRow,
parameters: { layout: 'centered' },
tags: ['autodocs'],
decorators: [
(Story) => (
<div className="w-full max-w-2xl p-4 bg-background border border-border rounded-[var(--radius-xl)] min-h-layout-story">
<Story />
</div>
),
],
argTypes: {
onTrackClick: { action: 'track clicked' },
onTrackPlay: { action: 'play clicked' },
@ -72,3 +80,16 @@ export const Liked: Story = {
isLiked: true,
},
};
/** List + selection + metadata to validate tokens. */
export const VisualStressTest: Story = {
args: {
track: mockTrack,
format: 'list',
showMetadata: true,
showCover: true,
showDuration: true,
showSelection: true,
isSelected: true,
},
};

View file

@ -67,8 +67,8 @@ export function TrackListRow({
<tr
role="row"
className={cn(
'hover:bg-muted/50 transition-colors',
isSelected && 'bg-kodo-cyan/10 dark:bg-kodo-cyan/20',
'hover:bg-muted/50 transition-colors duration-[var(--duration-normal)]',
isSelected && 'bg-primary/10',
className,
)}
onClick={() => onTrackClick?.(track)}
@ -92,12 +92,12 @@ export function TrackListRow({
<img
src={track.cover}
alt={`Cover de ${track.title}`}
className="w-full h-full object-cover rounded"
className="w-full h-full object-cover rounded-[var(--radius-md)]"
/>
) : (
<div className="w-full h-full bg-muted flex items-center justify-center rounded">
<div className="w-full h-full bg-muted flex items-center justify-center rounded-[var(--radius-md)]">
<svg
className="w-4 h-4 text-muted-foreground"
className="w-4 h-4 text-muted-foreground/90"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@ -112,7 +112,7 @@ export function TrackListRow({
</div>
)}
{showActions && (
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 bg-black/40 transition-opacity rounded">
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 bg-black/40 transition-opacity duration-[var(--duration-normal)] rounded-[var(--radius-md)]">
<button
onClick={handlePlay}
aria-label={isPlaying ? 'Mettre en pause' : 'Lire'}
@ -124,15 +124,15 @@ export function TrackListRow({
)}
</div>
)}
<span>{track.title}</span>
<span className="tracking-tight">{track.title}</span>
</div>
</td>
<td className="p-2">{track.artist}</td>
<td className="p-2 text-muted-foreground/90 tracking-tight">{track.artist}</td>
{showMetadata && (
<td className="p-2 text-muted-foreground">{track.album}</td>
<td className="p-2 text-muted-foreground/90 tracking-tight">{track.album}</td>
)}
{showDuration && (
<td className="p-2 text-right text-muted-foreground text-xs">
<td className="p-2 text-right text-muted-foreground/90 text-xs tabular-nums tracking-tight">
{Math.floor(track.duration / 60)}:
{String(track.duration % 60).padStart(2, '0')}
</td>
@ -145,8 +145,9 @@ export function TrackListRow({
<li
role="listitem"
className={cn(
'flex items-center gap-4 p-2 rounded-md hover:bg-muted/50 group h-14 transition-colors',
isSelected && 'bg-kodo-cyan/10 dark:bg-kodo-cyan/20',
'flex items-center gap-4 p-2 rounded-[var(--radius-md)] hover:bg-muted/50 group h-14',
'transition-colors duration-[var(--duration-normal)]',
isSelected && 'bg-primary/10',
className,
)}
onClick={() => onTrackClick?.(track)}
@ -169,12 +170,12 @@ export function TrackListRow({
<img
src={track.cover}
alt={`Cover de ${track.title}`}
className="w-full h-full object-cover rounded"
className="w-full h-full object-cover rounded-[var(--radius-md)]"
/>
) : (
<div className="w-full h-full bg-muted flex items-center justify-center rounded">
<div className="w-full h-full bg-muted flex items-center justify-center rounded-[var(--radius-md)]">
<svg
className="w-5 h-5 text-muted-foreground"
className="w-5 h-5 text-muted-foreground/90"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
@ -192,7 +193,7 @@ export function TrackListRow({
)}
{showActions && (
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 bg-black/40 transition-opacity rounded">
<div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 bg-black/40 transition-opacity duration-[var(--duration-normal)] rounded-[var(--radius-md)]">
<button
aria-label={isPlaying ? 'Mettre en pause' : 'Lire'}
onClick={handlePlay}
@ -205,16 +206,16 @@ export function TrackListRow({
</div>
<div className="flex-1 min-w-0">
<div className="font-medium truncate">{track.title}</div>
<div className="font-medium truncate tracking-tight">{track.title}</div>
{showMetadata && (
<div className="text-xs text-muted-foreground truncate">
<div className="text-xs text-muted-foreground/90 truncate tracking-tight">
{track.artist} {track.album && `${track.album}`}
</div>
)}
</div>
{showDuration && (
<div className="text-xs text-muted-foreground">
<div className="text-xs text-muted-foreground/90 tabular-nums tracking-tight">
{Math.floor(track.duration / 60)}:
{String(track.duration % 60).padStart(2, '0')}
</div>
@ -226,14 +227,14 @@ export function TrackListRow({
<button
aria-label={isLiked ? 'Retirer des favoris' : 'Ajouter aux favoris'}
onClick={handleLike}
className="hover:text-primary transition-colors"
className="hover:text-primary transition-colors duration-[var(--duration-normal)]"
>
{LikeIcon}
</button>
<button
aria-label="Plus d'options"
onClick={handleMore}
className="hover:text-primary transition-colors"
className="hover:text-primary transition-colors duration-[var(--duration-normal)]"
>
...
</button>