Moda premium editorial · checkout no WhatsApp
RJjstore

Problema
Lojas de roupa de marca vendem por foto no Instagram, sem uma vitrine com cara de grife que transmita desejo e exclusividade.
Solução
E-commerce de moda com estética de estúdio de luxo: hero cinematográfico, lookbook com scroll horizontal, iluminação dinâmica seguindo o cursor, vista rápida do produto, carrinho persistente e pedido fechado direto no WhatsApp, com tema Areia e Branco alternável.
Como foi construído
Next.js 14 (App Router) sem nenhuma dependência pesada de e-commerce: zero carrinho de terceiros, zero CMS, zero state manager externo. Só framer-motion, Lenis (smooth scroll) e next/font (Cormorant Garamond para os títulos editoriais, Inter para o resto). O README documenta o próprio orçamento de performance: First Load JS ≈ 156 kB na home, com todas as animações resolvidas em CSS e Framer Motion em vez de bibliotecas de canvas ou WebGL. É a decisão certa para um projeto onde o produto é a fotografia, não um efeito 3D.
O carrinho é um Context puro (CartProvider) guardando { id, size, qty }[]. A chave de identidade de uma linha é o par produto e tamanho, não só o produto, então a mesma peça em P e G vira duas linhas independentes. A persistência em localStorage usa um useRef como guarda de hidratação: o primeiro useEffect lê o storage e popula o state, e só um segundo useEffect, que checa a ref, volta a escrever. Sem essa guarda, o efeito de escrita dispararia no primeiro render com array vazio e apagaria o carrinho salvo antes mesmo de ele ser lido.
Tamanho é obrigatório antes de qualquer conversão. O ProductDetail mantém um ensureSize() que bloqueia o "Adicionar à sacola" e acende um aviso inline se nada foi escolhido, mas o botão de WhatsApp nunca fica desabilitado: sem tamanho, ele monta a mensagem com "a combinar" no lugar, pra não perder o lead por fricção. No carrinho completo, orderMessage() agrega todas as linhas num único texto formatado (produto, marca, tamanho, subtotal, total em negrito markdown do WhatsApp) e abre o wa.me com encodeURIComponent. Todo o fluxo de conversão termina fora do app, sem backend e sem gateway de pagamento.
A estética "editorial de luxo" é construída em camadas de motion coordenadas, não em uma única lib de efeitos. O LookbookScroll fixa a seção (position: sticky) e usa useScroll e useTransform do Framer Motion pra mapear o progresso de rolagem em três transforms independentes: o trilho horizontal desliza num sentido, a imagem dentro de cada card contra-desliza em outro (scale-125 mais imgX) e a legenda se move num terceiro, criando parallax multicamada só com CSS transforms. O Spotlight (luz que segue o cursor em soft-light) é implementado com useMotionValue e useSpring, e se desliga sozinho via matchMedia('(hover: hover) and (pointer: fine)') e useReducedMotion. É luxo visual que não penaliza mobile nem acessibilidade. O toggle de tema entre Areia/Espresso e Branco evita flash de tema errado com um script inline e bloqueante no head que lê o localStorage e aplica data-theme antes da hidratação do React. E os detalhes "neo-skeuomórficos" (relevo sutil nos swatches de cor, profundidade no botão sólido) vivem isolados num único arquivo de tokens (lib/theme.ts) pra não vazar pro resto da UI, que é deliberadamente flat.
Carrinho por variante
Chave composta id+tamanho no Context, persistida em localStorage com guarda de hidratação anti-sobrescrita.
WhatsApp sem fricção
Tamanho obrigatório pro carrinho, mas o link de WhatsApp nunca trava: sem seleção, ele soma "a combinar".
Lookbook em 3 camadas
Scroll fixo com useScroll/useTransform movendo trilho, imagem e legenda em direções opostas.
Tema sem flash
Script inline bloqueante aplica data-theme do localStorage antes da hidratação do React.
Mais telas
Stack
- Next.js
- TypeScript
- Tailwind
- Framer Motion
- Lenis


