Communitygithub.com

walenso/3d-portfolio

Crie um site de portfólio pessoal com um herói 3D, objetos animados para cada interesse, estudos de caso visuais e um formulário de contato funcional (tRPC, Resend, Turnstile, Postgres) e implante no Vercel. Use quando alguém pedir um portfólio, site pessoal ou 'portfólio 3D' e quiser algo mais marcante que um template.

O que é 3d-portfolio?

Esta skill capacita você a construir um portfólio de página única verdadeiramente único, centrado em um conceito 3D personalizado que reflete a pessoa por trás do site. Diferente de templates genéricos, o processo começa coletando informações reais do usuário – projetos, experiências, hobbies e ativos – para então encontrar um conceito central, transformando o objeto ou interesse mais característico da pessoa (como uma caixa de discos para um DJ ou uma parede de agarras para um escalador) em um herói 3D interativo. A skill oferece uma estrutura técnica robusta com Vite, React, TypeScript, three.js para o 3D (usando uma única tela WebGL com viewports para vários objetos), tRPC e Zod para a API, Resend para e-mail, Cloudflare Turnstile para proteção contra spam e Neon Postgres para armazenamento. Inclui seções pré-definidas que funcionaram bem, como herói, trabalho, experiência, habilidades, jornada e formulário de contato, além de regras claras para garantir um resultado profissional e ético. O resultado final é implantado no Vercel, e a skill fornece um prompt inicial para o usuário preencher com suas informações, facilitando a criação de um site que é tanto tecnicamente sólido quanto pessoalmente significativo.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/walenso/3d-portfolio-skill/tree/HEAD/skills/3d-portfolio

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

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

  1. 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.
  2. 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".
  3. Experiência: uma linha por trabalho com um número grande cada.
  4. Jornada: escolas ou marcos, cada um com seu próprio objeto animado.
  5. Habilidades: linhas de texto em movimento, não uma tabela.
  6. Fora do expediente: um objeto animado por interesse.
  7. Viagens: lugares como ímãs de geladeira em uma porta de aço que podem ser arrastados.
  8. 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:

  1. retorna sucesso silenciosamente se o campo escondido de honeypot estiver preenchido;
  2. aplica um pequeno limite de taxa por endereço;
  3. verifica o token Turnstile com a Cloudflare e recusa se falhar;
  4. envia um e-mail para o proprietário através do Resend, com o remetente como reply_to;
  5. salva cada mensagem em uma tabela messages;
  6. 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 from pode já ser Nome <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 com server.ssrLoadModule e passe a ela um Request.
  • 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=swiftshader para 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 .vercel ignorados. 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:

Habilidades Relacionadas