Loja de artesanato e sabores regionais brasileiros. Catálogo em JSON, vitrine e carrinho montados no navegador. HTML, CSS e JS puros — sem framework, sem build.
Projeto do Bootcamp AI/R, Trilha Commerce. Loja fictícia, feita para estudo.
No ar: https://devyasmimoraes.github.io/cumbuca-commerce/
index.html a vitrine — nasce vazia, sem nenhum produto no HTML
css/style.css estilos
js/app.js fetch do catálogo, busca, filtro, carrinho e checkout
data/products.json o catálogo: 10 produtos
assets/ 11 ilustrações SVG
como-fiz/ a página do vídeo
python3 -m http.server 8787Abra http://localhost:8787.
Não adianta abrir o index.html clicando duas vezes: em file:// o navegador
bloqueia fetch e o catálogo não carrega. Precisa de servidor HTTP.
- 10 produtos carregados via
fetchdedata/products.json - Busca por nome, categoria, cidade, estado e descrição, ignorando acentos
- Filtro por categoria, com os botões gerados a partir do catálogo
- Busca e filtro funcionam juntos
- Carrinho com quantidade, subtotal e total, salvo em
localStorage - Checkout em 4 etapas: itens → entrega → pagamento → confirmação
- CEP preenche o endereço via ViaCEP
- PIX ou cartão, com validação de Luhn, validade e CVV
Arquitetura
- Catálogo separado do front (headless): preço e estoque mudam toda semana, layout quase nunca. Quem edita produtos mexe em dados, não em código. O mesmo JSON serviria um app mobile sem reescrever a vitrine.
- Sem framework: não há estado compartilhado entre telas nem componentes reaproveitados que justifiquem o bundle. Total Blocking Time ficou em 0 ms.
- Os botões de categoria saem de
new Set(...)sobre o catálogo. Escritos no HTML seriam uma segunda fonte da verdade, pronta para divergir.
Vitrine
- A busca normaliza acentos com
normalize("NFD")+\p{Diacritic}. Sem isso "cafe" não acha "Café". - As peças são ilustrações SVG, não fotos: meio KB cada, vetor, sem licença para resolver. Numa loja real precisariam ser fotos — está nas limitações.
- Os cards têm
imagemeemojino catálogo. Faltando a imagem, cai no emoji em vez de deixar um quadrado vazio. alt=""nas imagens é intencional: o nome do produto já está no<h2>ao lado, e repetir faria o leitor de tela anunciar duas vezes.escapar()antes doinnerHTML. Hoje o catálogo é confiável, mas se vier de um CMS um produto chamado<script>viraria código executado.
Carrinho e checkout
- O carrinho guarda só
{id, quantidade}. Guardar cópia do produto deixaria o preço velho salvo no navegador de quem visitou ontem. - Ler o
localStoragetemtry/catche um filtro por id existente: o conteúdo pode ser lixo de uma versão antiga, e um produto salvo ontem pode ter saído do catálogo hoje. - As 4 etapas vivem no mesmo painel, controladas por uma variável e uma tabela
ETAPAS. Mudar a ordem do checkout é editar um objeto. - Entrega e pagamento são
<form>separados, então cada "Continuar" dispara a validação nativa só dos seus campos. - Os campos de cartão não são
required: com PIX escolhido eles ficam escondidos, e campo obrigatório invisível trava o envio sem mostrar erro. A validação deles é feita em JS. sincronizarPagamento()existe porqueform.reset()volta o radio ao padrão sem dispararchange. Sem ela, depois de um pedido o formulário ficava marcando PIX com os campos de cartão abertos.- O ViaCEP nunca bloqueia o envio. É API de terceiro: pode cair, demorar ou
responder
{"erro": true}com status 200.
- A busca compara texto literal. "panela pra cozinhar feijão" não acha a panela de barro — isso pediria busca semântica.
- Ilustração não substitui foto numa loja de artesanato, onde cada peça é única.
- O checkout é encenação: nada é enviado, cobrado ou salvo. Não há backend.
- Luhn confere se o cartão é bem formado, não se existe ou tem saldo.
- CLS de 0,157 no Lighthouse: a vitrine nasce vazia e empurra o rodapé quando os produtos chegam. Resolveria reservando a altura da grade.