add mobile swipe for product card images and fix dev asset caching

Product cards now use CSS scroll-snap on touch devices (mobile) for
swiping between images, with dot indicators and a JS hook for active
state. Desktop keeps the existing hover crossfade via @media (hover:
hover). Dots use size differentiation (WCAG 2.2 AA compliant) with
outline rings for contrast on any background.

Also fixes: no-image placeholder (SVG icon instead of broken img),
unnecessary wrapper div for single-image cards, and dev static asset
caching (was immutable for all envs, now only prod).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-10 12:24:52 +00:00
parent 19b4a5bd59
commit 1a69736734
6 changed files with 222 additions and 42 deletions

View File

@@ -329,24 +329,87 @@
color: #ffffff;
}
/* Product Hover Image */
/* Product Card Images — mobile: swipe, desktop: hover crossfade */
.product-image-container {
position: relative;
}
.product-image-hover {
/* Mobile default: horizontal scroll-snap for swiping between images */
.product-image-scroll {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
scrollbar-width: none;
-webkit-overflow-scrolling: touch;
height: 100%;
&::-webkit-scrollbar {
display: none;
}
}
.product-image-scroll > img,
.product-image-scroll > picture {
flex: 0 0 100%;
width: 100%;
height: 100%;
scroll-snap-align: start;
}
/* Dot indicators for swipeable images (mobile only) */
.product-image-dots {
position: absolute;
inset: 0;
opacity: 0;
transition: opacity 0.3s ease;
bottom: 0.5rem;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 0.375rem;
z-index: 5;
}
.product-card:hover .product-image-hover {
opacity: 1;
.product-image-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.5);
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.42);
border: none;
padding: 0;
transition: all 0.2s ease;
}
.product-card:hover .product-image-primary:has(+ .product-image-hover) {
opacity: 0;
.product-image-dot-active {
width: 8px;
height: 8px;
background: rgba(255, 255, 255, 0.95);
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.42);
}
/* Desktop: hover crossfade instead of scroll */
@media (hover: hover) {
.product-image-scroll {
display: contents;
}
.product-image-dots {
display: none;
}
.product-image-hover {
position: absolute;
inset: 0;
opacity: 0;
transition: opacity 0.3s ease;
}
.product-card:hover .product-image-hover {
opacity: 1;
}
.product-card:hover .product-image-primary:has(+ .product-image-hover) {
opacity: 0;
}
}
/* Social Links */