From 850a5e12168c4bdc66d7d02690acb4bd9bc46d4d Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Mon, 20 Apr 2026 22:10:19 -0600 Subject: [PATCH] feat(shop): redesign /shop pages with editorial theme and Tailwind primitives Scoped-only rewrite of the /shop landing, PDP, cart, drawer, and collection/search/policy pages to match a new editorial merch-store direction (cyan accent, DM Sans display, JetBrains Mono for prices/meta). Theme tokens live on a `.shop-scope` wrapper with light and dark variants, exposed to Tailwind via `@theme inline` so authoring stays in utility classes. New primitives under `src/components/shop/ui/` (Button, Badge, Chip, Size, Qty, Select, Input, Tab, Panel, Pulse, Mono, Crumb) and composed pieces (Hero, DropCard, Strip, Specs, Note) back the pages. Global site, navbar, and non-shop routes are unchanged; `shop.css` and the DM Sans / JetBrains Mono fonts only load on /shop. `ProductListItem` gains `productType` and `tags` to power the hover tag + New/Low-stock badges. --- src/components/shop/Breadcrumbs.tsx | 52 ---- src/components/shop/CartDrawer.tsx | 114 +++++---- src/components/shop/ProductCard.tsx | 113 +++++++-- src/components/shop/ShopDropCard.tsx | 65 +++++ src/components/shop/ShopHero.tsx | 34 +++ src/components/shop/ShopLayout.tsx | 70 ++---- src/components/shop/ShopNote.tsx | 23 ++ src/components/shop/ShopSpecs.tsx | 23 ++ src/components/shop/ShopStrip.tsx | 18 ++ src/components/shop/ui/Badge.tsx | 33 +++ src/components/shop/ui/Button.tsx | 44 ++++ src/components/shop/ui/Chip.tsx | 42 ++++ src/components/shop/ui/Crumb.tsx | 45 ++++ src/components/shop/ui/Input.tsx | 24 ++ src/components/shop/ui/Mono.tsx | 40 ++++ src/components/shop/ui/Panel.tsx | 24 ++ src/components/shop/ui/Pulse.tsx | 15 ++ src/components/shop/ui/Qty.tsx | 69 ++++++ src/components/shop/ui/Select.tsx | 27 +++ src/components/shop/ui/Size.tsx | 39 +++ src/components/shop/ui/Tab.tsx | 40 ++++ src/components/shop/ui/index.ts | 12 + src/routes/shop.cart.tsx | 200 ++++++++-------- src/routes/shop.collections.$handle.tsx | 45 ++-- src/routes/shop.index.tsx | 229 +++++++++++------- src/routes/shop.pages.$handle.tsx | 23 +- src/routes/shop.policies.$handle.tsx | 23 +- src/routes/shop.products.$handle.tsx | 305 ++++++++++++++---------- src/routes/shop.search.tsx | 49 ++-- src/routes/shop.tsx | 14 ++ src/styles/shop.css | 80 +++++++ src/utils/shopify-queries.ts | 7 +- 32 files changed, 1377 insertions(+), 564 deletions(-) delete mode 100644 src/components/shop/Breadcrumbs.tsx create mode 100644 src/components/shop/ShopDropCard.tsx create mode 100644 src/components/shop/ShopHero.tsx create mode 100644 src/components/shop/ShopNote.tsx create mode 100644 src/components/shop/ShopSpecs.tsx create mode 100644 src/components/shop/ShopStrip.tsx create mode 100644 src/components/shop/ui/Badge.tsx create mode 100644 src/components/shop/ui/Button.tsx create mode 100644 src/components/shop/ui/Chip.tsx create mode 100644 src/components/shop/ui/Crumb.tsx create mode 100644 src/components/shop/ui/Input.tsx create mode 100644 src/components/shop/ui/Mono.tsx create mode 100644 src/components/shop/ui/Panel.tsx create mode 100644 src/components/shop/ui/Pulse.tsx create mode 100644 src/components/shop/ui/Qty.tsx create mode 100644 src/components/shop/ui/Select.tsx create mode 100644 src/components/shop/ui/Size.tsx create mode 100644 src/components/shop/ui/Tab.tsx create mode 100644 src/components/shop/ui/index.ts create mode 100644 src/styles/shop.css diff --git a/src/components/shop/Breadcrumbs.tsx b/src/components/shop/Breadcrumbs.tsx deleted file mode 100644 index d27218fd7..000000000 --- a/src/components/shop/Breadcrumbs.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from 'react' -import { Link } from '@tanstack/react-router' -import { ChevronRight } from 'lucide-react' - -export type Crumb = { - label: string - href?: string -} - -/** - * Accessible breadcrumb trail. The last crumb renders as plain text - * (current location); intermediate crumbs link back. - */ -export function Breadcrumbs({ crumbs }: { crumbs: Array }) { - return ( - - ) -} diff --git a/src/components/shop/CartDrawer.tsx b/src/components/shop/CartDrawer.tsx index e25e3b103..1cb62c02a 100644 --- a/src/components/shop/CartDrawer.tsx +++ b/src/components/shop/CartDrawer.tsx @@ -1,4 +1,3 @@ -import * as React from 'react' import * as Dialog from '@radix-ui/react-dialog' import { Link } from '@tanstack/react-router' import { Minus, Plus, ShoppingCart, Trash2, X } from 'lucide-react' @@ -6,6 +5,7 @@ import { twMerge } from 'tailwind-merge' import { useCart, useRemoveCartLine, useUpdateCartLine } from '~/hooks/useCart' import { formatMoney, shopifyImageUrl } from '~/utils/shopify-format' import type { CartLineDetail } from '~/utils/shopify-queries' +import { ShopLabel, ShopMono } from './ui' type CartDrawerProps = { open: boolean @@ -14,10 +14,8 @@ type CartDrawerProps = { /** * Slide-in cart drawer. Shares state with /shop/cart through the same - * useCart React Query key, so adds in the drawer mirror the full page - * and vice-versa. Pinned to the right on desktop; full-width slide-up on - * mobile would be nice later, but a right-anchored sheet is the standard - * Shopify-theme pattern and works on phones too. + * useCart React Query key. The root element wears `shop-scope` because the + * drawer is portaled outside the ShopLayout tree. */ export function CartDrawer({ open, onOpenChange }: CartDrawerProps) { const { cart, totalQuantity } = useCart() @@ -26,26 +24,28 @@ export function CartDrawer({ open, onOpenChange }: CartDrawerProps) { return ( - + -
- - Cart{totalQuantity > 0 ? ` (${totalQuantity})` : ''} +
+ + + Cart{totalQuantity > 0 ? ` (${totalQuantity})` : ''} + @@ -53,7 +53,7 @@ export function CartDrawer({ open, onOpenChange }: CartDrawerProps) { {hasLines ? ( <> -
    +
      {cart.lines.nodes.map((line) => ( void }) { return ( -
      - -

      Your cart is empty.

      +
      + +

      Your cart is empty.

      Shop all products + + → +
      ) @@ -98,26 +105,33 @@ function DrawerFooter({ }) { const subtotal = cart.cost.subtotalAmount return ( -