style: improve thumbnail indicator + selection contrast
- Bump primary from #3b6ed8 to #3b82f6 (Tailwind blue-500). Higher saturation reads better against both the dark surface and varied photo content. Contrast against bg-bg goes from ~5.7:1 to ~6.6:1. - Selection ring: add ring-offset-2 ring-offset-bg so the bright blue has a dark gap separating it from the photo edge — pops on light and dark photos alike. Hover ring gets the same treatment. - Selection check badge: white ring + shadow + thicker stroke so the badge is legible against any photo (was disappearing on bright scenes). - Rating stars: wrap in a translucent dark pill with backdrop-blur so yellow stars don't vanish on yellow / sandy photos. - Heap / duplicate / discarded badges: matching white ring + thicker icon stroke so they all read consistently and don't blend in. - Timeline date headers: text-text instead of text-text-muted so the group labels actually pop above the grid. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -118,8 +118,11 @@ export function PhotoThumbnail({
|
||||
<div
|
||||
className={clsx(
|
||||
'group relative cursor-pointer overflow-hidden rounded-sm transition-all duration-200',
|
||||
'hover:ring-2 hover:ring-primary/50',
|
||||
isSelected && 'ring-2 ring-primary shadow-lg',
|
||||
// Two-tone hover ring: bright primary inner + dark offset so it
|
||||
// pops on light AND dark photos.
|
||||
'hover:ring-2 hover:ring-primary/60 hover:ring-offset-1 hover:ring-offset-bg',
|
||||
isSelected &&
|
||||
'ring-2 ring-primary ring-offset-2 ring-offset-bg shadow-lg',
|
||||
!imageLoaded && 'bg-surface animate-pulse'
|
||||
)}
|
||||
style={{
|
||||
@@ -181,49 +184,52 @@ export function PhotoThumbnail({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Selection Indicator */}
|
||||
{/* Selection Indicator — white ring around the primary fill so the
|
||||
* badge stays visible against any photo (light or dark). */}
|
||||
{isSelected && (
|
||||
<div className="absolute left-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-primary text-white">
|
||||
<Check className="h-4 w-4" />
|
||||
<div className="absolute left-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-primary text-white shadow-md ring-2 ring-white/90">
|
||||
<Check className="h-4 w-4" strokeWidth={3} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Rating Stars */}
|
||||
|
||||
{/* Rating Stars — sit in a translucent dark pill so yellow stars
|
||||
* don't disappear against a yellow / sandy photo. */}
|
||||
{photo.rating > 0 && (
|
||||
<div className="absolute bottom-1 left-1 flex gap-0.5">
|
||||
<div className="absolute bottom-1 left-1 flex items-center gap-0.5 rounded bg-black/55 px-1 py-0.5 shadow-sm backdrop-blur-[2px]">
|
||||
{Array.from({ length: photo.rating }).map((_, i) => (
|
||||
<Star
|
||||
key={i}
|
||||
className="h-3 w-3 fill-star text-star"
|
||||
className="h-3 w-3 fill-star text-star drop-shadow"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Flag Indicators */}
|
||||
|
||||
{/* Flag Indicators — every badge gets a white ring so it survives
|
||||
* any photo content. */}
|
||||
<div className="absolute bottom-1 right-1 flex items-center gap-1">
|
||||
{isInActiveHeap && (
|
||||
<div
|
||||
className="flex h-5 w-5 items-center justify-center rounded-full bg-pick text-white shadow-md"
|
||||
className="flex h-5 w-5 items-center justify-center rounded-full bg-pick text-white shadow-md ring-2 ring-white/90"
|
||||
title="In active heap"
|
||||
>
|
||||
<ShoppingBasket className="h-3 w-3" />
|
||||
<ShoppingBasket className="h-3 w-3" strokeWidth={2.5} />
|
||||
</div>
|
||||
)}
|
||||
{photo.is_duplicate && (
|
||||
<div
|
||||
className="flex h-5 w-5 items-center justify-center rounded-full bg-black/60 text-white shadow-md"
|
||||
className="flex h-5 w-5 items-center justify-center rounded-full bg-black/70 text-white shadow-md ring-2 ring-white/90"
|
||||
title="Duplicate (matches another photo's hash)"
|
||||
>
|
||||
<Copy className="h-3 w-3" />
|
||||
<Copy className="h-3 w-3" strokeWidth={2.5} />
|
||||
</div>
|
||||
)}
|
||||
{photo.is_discarded && (
|
||||
<div
|
||||
className="flex h-5 w-5 items-center justify-center rounded-full bg-reject text-white shadow-md"
|
||||
className="flex h-5 w-5 items-center justify-center rounded-full bg-reject text-white shadow-md ring-2 ring-white/90"
|
||||
title="Discarded"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
<Trash2 className="h-3 w-3" strokeWidth={2.5} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -482,7 +482,7 @@ export function Timeline() {
|
||||
}}
|
||||
className="flex items-end pb-1"
|
||||
>
|
||||
<h3 className="text-sm font-semibold uppercase tracking-wide text-text-muted">
|
||||
<h3 className="text-sm font-semibold uppercase tracking-wide text-text">
|
||||
{item.label}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user