Add collection filters and sort, out-of-stock options, gallery drag

- Collections: filters and sort via the Storefront API — the products
  connection now takes `filters`/`after` and returns its facets, with
  cursor-based load more on the collection page
- Extract ProductFilters (renamed from SearchFilters) and a shared
  ProductToolbar so search and collections use the same chrome
- PDP: mark option values with no in-stock variant using a diagonal
  strike, computed against the other selected options
- PDP: pointer drag-scrolling for the mobile image carousel, since a
  scroll container doesn't drag with a mouse
- Header/cart: circular icon buttons, tighter spacing, and icon sizes
  set by class (Button's base CSS clamps un-classed svgs to size-4)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jWbNNJLksC1QG8z8845FX
This commit is contained in:
Rami Bitar
2026-08-01 12:27:05 -04:00
co-authored by Claude Opus 5
parent cc901b9ce8
commit 2ef5639a2e
13 changed files with 515 additions and 144 deletions
+8 -4
View File
@@ -20,9 +20,9 @@ const CartIcon: React.FC = () => {
variant="ghost"
size="icon"
aria-label={`Open bag (${itemCount})`}
className="relative"
className="relative rounded-full"
>
<RiShoppingBagLine size={20} />
<RiShoppingBagLine className="size-5" />
{itemCount > 0 && (
<span className="absolute top-0 right-0 bg-primary text-primary-foreground text-[10px] font-mono rounded-full w-4 h-4 flex items-center justify-center">
{itemCount > 99 ? '99+' : itemCount}
@@ -86,7 +86,7 @@ const Header: React.FC<HeaderProps> = ({
</div>
{/* Actions */}
<div className="flex items-center gap-x-1">
<div className="flex items-center">
<SearchDialog />
<CartIcon />
@@ -98,7 +98,11 @@ const Header: React.FC<HeaderProps> = ({
className="md:hidden"
aria-label="Toggle menu"
>
{menuOpen ? <RiCloseLine size={22} /> : <RiMenu3Line size={22} />}
{menuOpen ? (
<RiCloseLine className="size-5" />
) : (
<RiMenu3Line className="size-5" />
)}
</Button>
</div>
</div>