Portfólio 3D
Crie um portfólio de página única que só poderia pertencer a esta pessoa. O site que produziu esta skill foi construído em torno de uma caixa de discos de um DJ: cada projeto é uma capa de disco que você folheia, e o disco gira quando está à frente. Exemplo: https://alessio-simoya.vercel.app
Não copie esse conceito. Siga o processo e encontre o deles.
1. Colete os dados primeiro
Peça estes antes de projetar. Construa com conteúdo real desde o início.
- Projetos: para cada um, um breve resumo dos fatos: o que é, seu papel, datas, o que fez, a stack, um link ao vivo e uma lista de coisas que não devem ser reivindicadas.
- Experiência: empresa, cargo, datas, local e um resultado por trabalho.
- Habilidades: agrupadas, com onde cada uma foi comprovada.
- Vida fora do trabalho: hobbies, times, o que cozinham, onde estiveram. É daí que vem o conceito, então peça mais do que eles oferecem.
- Ativos: logotipos, capturas de tela de produtos, vídeos, arquivos Rive ou Lottie. Pergunte onde as pastas do projeto residem e pesquise-as.
- Links: LinkedIn, GitHub e assim por diante. Nunca publique o endereço de e-mail a menos que eles peçam.
2. Encontre o conceito único
Escolha a coisa mais característica no mundo da pessoa e transforme-a no herói, como um objeto 3D real com o qual as pessoas possam interagir. Um DJ ganha uma caixa de discos. Um escalador pode ganhar uma parede de agarras; um padeiro, uma bandeja de pães. Cada projeto se torna um item nesse objeto.
Gaste a ousadia lá. Mantenha o resto da página sóbrio: campos de cor sólida, uma fonte de exibição com personalidade, uma fonte de corpo simples, quatro ou cinco cores nomeadas.
3. Stack
- Vite, React e TypeScript em modo estrito
- three.js para o 3D, escrito diretamente (nenhum framework de cena necessário)
- Rive apenas se a pessoa já tiver arquivos
.riv; carregue seu wasm de forma preguiçosa - tRPC e zod para a API, como uma função Vercel em
api/trpc/[trpc].ts - Resend para e-mail, Cloudflare Turnstile para spam, Neon Postgres para registros
- Fontes variáveis auto-hospedadas através de
@fontsource-variable/*
4. Como o 3D é montado
Use uma tela WebGL fixa em tela cheia com pointer-events: none, e
desenhe cada visão 3D dentro do retângulo de um espaço reservado do DOM:
const r = el.getBoundingClientRect();
renderer.setViewport(r.left, innerHeight - r.bottom, r.width, r.height);
renderer.setScissor(r.left, innerHeight - r.bottom, r.width, r.height);
renderer.render(scene, camera);
Limpe uma vez por quadro, ative o teste de tesoura e pule qualquer elemento que esteja fora da tela. Isso fornece muitos objetos 3D pela página pelo custo de um contexto, e eles rolam com o layout.
Dê a cada objeto pequeno um "rig": um grupo, uma posição de câmera, um ponto de
observação e uma função tick(t) acionada por um relógio compartilhado. Construa rigs de forma preguiçosa e
mostre um de cada vez. Desenhe texturas em uma tela 2D (logotipos, rótulos, padrões) para que não
haja arquivos de modelo para carregar.
Coisas que deram errado e como evitá-las:
- Um plano de sombra no chão maior que o elemento aparece como um retângulo duro. Dimensione-o para caber dentro da vista.
- Gire um grupo, depois gire um filho, quando você precisa "girar em torno de seu próprio eixo".
- Sprites e planos transparentes desenham depois de malhas sólidas; um brilho sob um objeto deve ser um plano plano abaixo dele, não um sprite voltado para a câmera.
5. Seções que funcionaram
- Herói: nome bem grande, um adesivo que percorre seus cargos, uma frase e o objeto interativo. Um disco colorido atrás dele muda com o item em foco.
- Trabalho: cada estudo de caso abre em uma faixa na cor daquela marca com seu logotipo real, depois um carrossel lateral de imagens, vídeo e animação, três destaques curtos, alguns números grandes e a stack como chips. Texto longo vai atrás de "Leia a história completa".
- Experiência: uma linha por trabalho com um número grande cada.
- Jornada: escolas ou marcos, cada um com seu próprio objeto animado.
- Habilidades: linhas de texto em movimento, não uma tabela.
- Fora do expediente: um objeto animado por interesse.
- Viagens: lugares como ímãs de geladeira em uma porta de aço que podem ser arrastados.
- Fale conosco: o formulário de contato.
Um cabeçalho de pílula flutuante marca a seção atual com um
IntersectionObserver.
6. Regras da casa
- Apenas fatos. Use o que a pessoa forneceu. Se uma seção precisar de algo que ela não deu, omita-a. Mantenha a lista "não reivindicar" de cada projeto no arquivo de conteúdo como um comentário.
- Sem contagens de vaidade. Commits, linhas de código, número de páginas ou modelos parecem imaturos. Mostre o produto e o que ele fez.
- Mostre menos texto. Se uma tela estiver cheia de parágrafos, transforme-a em algo visual.
- Respeite o trabalho dos outros. Não reconstrua um template pago a partir de sua prévia, e não copie o design de produto de uma marca (por exemplo, uma bola de partida nomeada); desenhe um original no mesmo espírito. Use apenas imagens que sejam licenciadas livremente e dê crédito a elas na página.
- Capturas de tela não devem mostrar nomes reais de clientes, valores ou rostos.
- Movimento tem um botão de desligar. Honre
prefers-reduced-motion, adicione um botão "Pausar movimento" e forneça uma alternativa estática quando o WebGL estiver ausente. - Coloque todo o conteúdo em um único arquivo tipado (
src/content/portfolio.ts) para que a pessoa possa editar palavras sem tocar nos componentes.
7. O formulário de contato
Um procedimento tRPC, contact.send, com entrada zod. Em ordem, ele:
- retorna sucesso silenciosamente se o campo escondido de honeypot estiver preenchido;
- aplica um pequeno limite de taxa por endereço;
- verifica o token Turnstile com a Cloudflare e recusa se falhar;
- envia um e-mail para o proprietário através do Resend, com o remetente como
reply_to; - salva cada mensagem em uma tabela
messages; - somente se o visitante marcou uma caixa "mantenha-me informado" (desmarcada por padrão),
salva-o em uma tabela
subscribers(com o texto de consentimento e hora) e em uma audiência do Resend.
Nunca adicione pessoas a uma lista de e-mail sem essa marcação.
Variáveis de ambiente: RESEND_API_KEY, ENQUIRY_TO_EMAIL,
ENQUIRY_FROM_EMAIL, RESEND_AUDIENCE_ID, DATABASE_URL,
TURNSTILE_SECRET, e a chave pública do site para o navegador.
Coisas que deram errado:
- O valor
frompode já serNome <endereço>. Envolvê-lo novamente faz o Resend responder com 422. Use-o como fornecido quando contiver<. - O Vite só expõe variáveis que começam com um prefixo em
envPrefix. Se a chave do site chegar com outro nome, adicione esse prefixo. - Um widget Turnstile só funciona em nomes de host listados para ele na Cloudflare.
- Em produção, sem segredo Turnstile, recuse-se a enviar. Use as chaves de teste publicadas pela Cloudflare apenas no desenvolvimento local.
- Adicione uma chave
CONTACT_DRY_RUN=1, ignorada em produção, para que todo o caminho possa ser testado sem enviar e-mail. - Para executar o mesmo manipulador sob
vite dev, adicione um pequeno plugin que carregue a função comserver.ssrLoadModulee passe a ela umRequest. - Construa o HTML do e-mail com tabelas e estilos inline, e escape cada campo.
8. Verifique seu trabalho enquanto avança
Após cada rodada de alterações:
- execute o build;
- tire capturas de tela do Playwright de cada seção com 1440 e 390 pixels de largura
(inicie o Chromium com
--use-gl=swiftshaderpara que o WebGL renderize sem cabeça); - confirme que
document.documentElement.scrollWidth - innerWidthé 0; - olhe para as capturas de tela você mesmo antes de dizer que está pronto.
Diga claramente o que não foi verificado: telefones reais, Safari, a sensação do movimento, entrega real de e-mail.
9. Implante
git init, com.env*e.vercelignorados. Verifique arquivos preparados em busca de chaves antes do primeiro commit.vercel link, adicione as variáveis de ambiente,vercel --prod.- Verifique se a página ao vivo carrega sem requisições com falha e que a API rejeita uma requisição sem token Turnstile.
- Peça ao proprietário para enviar uma mensagem real através do formulário ao vivo.
Prompt inicial
Dê à pessoa isto para preencher:
Construa meu portfólio 3D usando a skill 3d-portfolio.
Nome e descrição de uma linha sobre mim:
Cargos para percorrer no herói (4 a 6):
Projetos (para cada um: fatos, meu papel, datas, link, o que não reivindicar):
Experiência (empresa, cargo, datas, um resultado cada):
Habilidades, agrupadas:
Escolas ou marcos:
Interesses fora do trabalho (seja específico):
Lugares onde estive:
Onde meus logotipos, capturas de tela e animações residem nesta máquina:
Links para mostrar (LinkedIn, GitHub e assim por diante):
Coisas que nunca quero no site: