Frontends com Vibecoding Capítulo: Apple e Temu: os dois extremos do engajamento Trechos: 9 pedidos prontos para colar no seu agente Leia o LEIA-ME.txt desta pasta antes de usar. A página do capítulo explica a decisão; estes trechos só executam o que a página já decidiu. Aviso de ética que vale para o capítulo inteiro: nenhum destes pedidos autoriza usar fonte, ícone, render ou marca de terceiro. Copia-se o método, nunca o que pertence a alguém. ### O que faz: monta a escala de tipos que cresce com a tela, sem fonte proprietária ### Onde entra: no arquivo de estilo global, antes de qualquer componente ### Cuidado: título e corpo não querem o mesmo espaçamento entre letras Configure um sistema tipográfico em CSS puro inspirado no Dynamic Type da Apple, usando apenas fontes livres (NUNCA San Francisco/SF Pro). Requisitos: - Defina uma escala por tokens em :root com clamp(): --fs-large-title, --fs-title, --fs-headline, --fs-body (~17px), --fs-footnote, --fs-caption. O clamp deve crescer com a viewport entre um mínimo e um máximo legíveis. - A stack de fonte começa pela fonte do sistema do usuário: system-ui, -apple-system, "Segoe UI", Roboto, Inter, Arial, sans-serif. Comente que SF Pro é proibida fora do ecossistema Apple. - Tracking por tamanho: letter-spacing negativo (~-0.02em) nos títulos grandes (papel de 'display'); neutro no corpo (papel de 'text'). Explique no comentário por que título e corpo não querem o mesmo tracking. - line-height: ~1.05 nos títulos, ~1.5 no corpo. - Defina [data-theme="dark"] reatribuindo cores de texto com contraste AA. Entregue o bloco :root, a regra de tipografia (large-title, title, headline, body) e o tema escuro. Cores por tokens, contraste AA. ### O que faz: pede a cena em que o produto se monta enquanto a pessoa rola a página ### Onde entra: na primeira dobra de uma página de produto, e só quando o orçamento de banda permitir ### Cuidado: todos os quadros precisam ser baixados antes de a cena começar, e essa conta é cobrada de quem está no celular Crie um componente React chamado HeroScrubbing que reproduza o efeito de "o produto se monta conforme o usuário rola", estilo página de produto, com disciplina de performance e acessibilidade. Comportamento: - Recebe por prop uma lista de URLs de frames (sequência de imagens) e um poster estático. - Um wrapper alto cria a pista de scroll; um fica em position: sticky no topo. - Pré-carregue TODOS os frames (new Image() + decode()) antes de habilitar o efeito; mostre o poster enquanto carrega. - Mapeie a fração de scroll para o índice do frame e desenhe SEMPRE dentro de requestAnimationFrame; o listener de scroll é { passive: true }. Acessibilidade e performance (não-negociáveis): - Se prefers-reduced-motion: reduce, NÃO anexe o listener de scroll: pinte apenas o poster (um quadro estático). Comente isso. - O tem aria-label descritivo; a informação essencial NÃO depende do movimento. - fetchpriority="high" só no poster (é o LCP); os frames não bloqueiam o primeiro paint. - Nada de animar width/top; só desenho no canvas. Ética: use apenas imagens próprias/genéricas; nada de assets, render, fonte ou marca da Apple. Tokens de cor; contraste AA. ### O que faz: pede a seção que fica presa na tela enquanto as legendas se revelam ### Onde entra: no meio da página, quando uma ideia por vez precisa de tempo de tela ### Cuidado: onde o recurso ainda não existe, o conteúdo tem que aparecer inteiro em vez de sumir Crie uma seção 'cinematográfica' em CSS puro (com mínimo JS) em que um visual fica fixo enquanto legendas se revelam ao rolar, estilo Apple, sem bibliotecas pesadas. Comportamento: - Um bloco visual com position: sticky; top: 0 que permanece na tela enquanto a coluna de texto ao lado rola. - As legendas aparecem ao entrar na viewport usando CSS scroll-driven animations: animation-timeline: view() e animation-range entry/cover. - Lembre o gotcha do WebKit: a propriedade animation-timeline vem DEPOIS do shorthand animation. Fallback e acessibilidade (obrigatórios): - @supports not (animation-timeline: view()) deixa todo o conteúdo visível e estático (nada some sem suporte). - @media (prefers-reduced-motion: reduce) desliga as animações e mostra tudo pronto. - Ordem de leitura lógica no DOM; o sticky é só visual, não muda a semântica; foco visível nos links. Ética e tokens: nenhuma fonte/asset/ícone proprietário; cores por var(--*); contraste AA nos dois temas. ### O que faz: pede os pequenos gestos de resposta ao toque e ao foco, feitos para rodar barato ### Onde entra: em cartões e botões, na camada de estilo compartilhada ### Cuidado: o anel de foco nunca é removido, mesmo quando destoa do visual Crie um conjunto de micro-interações CSS para cards e botões, estilo Apple (sutis, com profundidade), que rodem na GPU e respeitem a preferência de movimento. Comportamento: - Hover/focus de card: leve elevação (translateY de -4px) e sombra mais suave; transição apenas de transform e box-shadow/opacity (compostas na GPU), nunca de width/height/top. - Botão: estado :hover, :active e :focus-visible com foco SEMPRE visível (anel de foco com contraste AA); o :active dá um leve scale(0.98) para sensação tátil. - Duração curta (150-250ms) e easing suave (ease-out na entrada). Acessibilidade (obrigatória): - Tudo dentro de @media (prefers-reduced-motion: no-preference), ou seja, as transições só existem se o usuário NÃO pediu redução; sob reduce, estados mudam sem movimento. - :focus-visible nunca é removido; contraste do anel >= 3:1. Tokens de cor; sem asset/fonte proprietária; contraste AA. Comente por que animar transform/opacity é barato e width/top é caro. ### O que faz: pede a troca de tela com continuidade visual, usando o recurso nativo do navegador ### Onde entra: na navegação entre telas de um mesmo produto ### Cuidado: onde o recurso não existe, a troca acontece na hora, sem quebrar nada Implemente transições de página/estado suaves estilo Apple usando a View Transitions API, com degradação elegante onde não há suporte. Comportamento: - Em uma SPA (React/Next): ao trocar de visão, envolva a atualização do DOM em document.startViewTransition(() => atualizar()). Use view-transition-name em um elemento herói (ex.: a imagem do produto) para um morph contínuo entre as telas. - Em navegação multi-página: inclua a regra CSS @view-transition { navigation: auto; } e estilize ::view-transition-old/new se necessário. Suporte e acessibilidade (obrigatórios): - Detecte suporte: if (!document.startViewTransition) { atualizar(); return; }. Sem suporte, troca instantânea, sem quebra. - Sob @media (prefers-reduced-motion: reduce), reduza a duração das ::view-transition a ~0.01ms (transição efetiva sem movimento perceptível). - Mencione no comentário: a transição dentro do mesmo documento entrou em Baseline newly available em outubro de 2025, com o Firefox 144; a transição entre documentos diferentes, declarada por @view-transition, ainda não é Baseline segundo a MDN, então precisa de degradação explícita. Ética: conteúdo próprio; nada de marca/asset Apple. Tokens; contraste AA. ### O que faz: pede o componente de imagem que serve o formato e o tamanho certos para cada aparelho ### Onde entra: em qualquer página com foto grande, principalmente na primeira dobra ### Cuidado: sem largura e altura declaradas, a página pula quando a imagem chega Crie um componente de imagem responsiva e performática para um hero/galeria estilo Apple, com formatos modernos e zero CLS. Requisitos: - Use com a cadeia AVIF -> WebP -> JPEG (source type por formato; o JPEG como fallback final). - srcset com múltiplas larguras e sizes corretos para servir o tamanho certo por viewport. - width e height (ou aspect-ratio) explícitos para evitar layout shift (CLS). - decoding="async" sempre; loading="lazy" para imagens abaixo da dobra; loading="eager" + fetchpriority="high" SOMENTE para o LCP. - alt descritivo e verdadeiro (ou alt="" se for puramente decorativa). - Para vídeo: poster leve, preload="metadata", muted, playsinline; e respeite prefers-reduced-motion para não dar autoplay de movimento intenso. Ética: imagens próprias/genéricas, nada de render ou marca da Apple. Comente o trade-off de peso: imersão custa banda, então sirva o necessário, não o máximo. ### O que faz: pede o aviso de prova social que só aparece quando existe número medido por trás ### Onde entra: em página de produto, junto do botão de compra ### Cuidado: se o dado não vier, o componente não mostra nada, em vez de mostrar um número de exemplo Implemente uma microinteração de PROVA SOCIAL verdadeira em React + Tailwind: - Um toast "{N} pessoas compraram nas últimas 24h", onde {N} vem de um endpoint real; se não houver dado, não mostre nada. - O número anima em count-up via requestAnimationFrame; sob prefers-reduced-motion, aparece direto no valor final. - O toast entra com transform: translateY + opacity (compositor), aria-live="polite", some sozinho, e não rouba o foco do teclado. - Nada de evento fabricado, nome aleatório nem contador que reinicia. Comente de onde o número vem e por que ele sobrevive a um reload. ### O que faz: pede a oferta com prazo de verdade, que encerra quando o relógio zera ### Onde entra: em campanha com data de fim já definida no sistema ### Cuidado: o prazo mora no servidor; prazo guardado no navegador reinicia a cada visita e denuncia a farsa Construa uma seção de oferta com urgência VERDADEIRA em React + Tailwind v4: - Cronômetro regressivo que conta até um prazo real (timestamp do backend); ao zerar, a oferta some de verdade (sem reinício). - Indicador de estoque só se vier do estoque real; nada de "resta 1" fixo. - Microinteração de destaque no botão (transform/opacity), sob prefers-reduced-motion, com estado base visível. - Contraste AA, foco visível, operável por teclado. Não use cronômetro que reinicia, escassez fabricada nem confirmshaming. Comente de onde cada número vem. ### O que faz: pede a mecânica de prêmio com as chances declaradas na própria tela ### Onde entra: em programa de recompensa, quando o prêmio existe e pode ser resgatado ### Cuidado: o resultado precisa ser legível sem a animação, para quem não vê o giro Crie uma mecânica de recompensa gamificada e ÉTICA (estilo roleta), em React + Tailwind: - A roleta gira e para num resultado vindo do servidor; as PROBABILIDADES de cada prêmio ficam visíveis ao usuário. - Todo prêmio exibido existe de fato e é resgatável; nada de "quase ganhou" manipulado. - Animação só sob prefers-reduced-motion: no-preference; o resultado é legível mesmo sem o giro (acessível a leitor de tela com aria-live). - Sem login forçado nem dark pattern para resgatar. Entregue o componente e uma nota de por que cada decisão respeita o usuário.