Communitygithub.com

walenso/3d-portfolio

Construisez un site portfolio personnel avec un héros 3D, des objets animés pour chaque centre d'intérêt, des études de cas visuelles et un formulaire de contact fonctionnel (tRPC, Resend, Turnstile, Postgres), puis déployez-le sur Vercel. À utiliser lorsque quelqu'un demande un portfolio, un site personnel ou un « portfolio 3D » et souhaite quelque chose de plus distinctif qu'un modèle.

Qu'est-ce que 3d-portfolio ?

Cette compétence permet de créer un portfolio one-page unique, centré autour d'un concept 3D personnalisé trouvé dans la vie de l'utilisateur (ex. : un DJ avec sa caisse de vinyles). Elle guide la collecte d'informations réelles (projets, expériences, compétences, hobbies, assets) et la conception d'un objet 3D interactif principal utilisant three.js. Le site utilise une pile technique moderne : Vite, React, TypeScript, tRPC, Zod, Resend pour les emails, Cloudflare Turnstile pour la protection anti-spam, et Neon Postgres pour la persistance. L'intégration 3D est optimisée avec un seul canvas WebGL plein écran qui découpe des vues dans des éléments DOM, et chaque petite vue 3D (objets animés pour les projets, les hobbies, etc.) est construite à l'aide de « rigs » (groupes, positions de caméra, fonctions tick). Le skill détaille également la mise en œuvre d'un formulaire de contact complet avec honeypot, limite de débit, validation Turnstile, envoi d'email via Resend, sauvegarde en base de données et une option dry-run pour les tests. Enfin, il fournit des instructions de déploiement sur Vercel, des vérifications avec Playwright, et un prompt de démarrage pour que l'utilisateur fournisse toutes les informations nécessaires. La compétence met l'accent sur les faits, l'absence de métriques vaniteuses, le respect du travail d'autrui, l'accessibilité (réduction de mouvement) et une séparation claire du contenu dans un fichier typé.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Portfolio 3D

Créez un portfolio one-page qui ne pourrait appartenir qu'à cette personne. Le site qui a produit cette compétence a été construit autour de la caisse à vinyles d'un DJ : chaque projet est une pochette de disque que l'on feuillette, et le disque tourne lorsqu'il est devant. Exemple : https://alessio-simoya.vercel.app

Ne copiez pas ce concept. Suivez le processus et trouvez le leur.

1. Collectez d'abord les entrées

Demandez-les avant de concevoir. Construisez avec du contenu réel dès le départ.

  • Projets : pour chacun, un bref résumé des faits : de quoi il s'agit, leur rôle, les dates, ce qu'il a fait, la stack, un lien en direct, et une liste de choses qui ne doivent pas être revendiquées.
  • Expérience : entreprise, titre, dates, lieu et un résultat par emploi.
  • Compétences : regroupées, avec pour chacune où elles ont été démontrées.
  • Vie en dehors du travail : loisirs, équipes, ce qu'ils cuisinent, où ils sont allés. C'est de là que vient le concept, alors demandez plus qu'ils n'en offrent.
  • Assets : logos, captures d'écran de produits, vidéos, fichiers Rive ou Lottie. Demandez où se trouvent les dossiers de projet et cherchez-y.
  • Liens : LinkedIn, GitHub, etc. Ne publiez jamais leur adresse email à moins qu'ils ne le demandent.

2. Trouvez le concept unique

Choisissez la chose la plus caractéristique dans l'univers de la personne et faites-en le héros, sous la forme d'un véritable objet 3D avec lequel les gens peuvent interagir. Un DJ a une caisse de vinyles. Un grimpeur pourrait avoir un mur de prises ; un boulanger, un plateau de pains. Chaque projet devient un élément de cet objet.

Investissez l'audace là-dedans. Gardez le reste de la page sobre : champs de couleur unis, une police d'affichage avec du caractère, une police de corps simple, quatre ou cinq couleurs nommées.

3. Stack

  • Vite, React et TypeScript en mode strict
  • three.js pour la 3D, écrit directement (pas besoin de framework de scène)
  • Rive uniquement si la personne a déjà des fichiers .riv ; chargez son wasm en lazy
  • tRPC et zod pour l'API, en tant que fonction Vercel dans api/trpc/[trpc].ts
  • Resend pour les emails, Cloudflare Turnstile pour le spam, Neon Postgres pour les enregistrements
  • Polices variables auto-hébergées via @fontsource-variable/*

4. Comment la 3D est assemblée

Utilisez un canvas WebGL fixe, plein écran avec pointer-events: none, et dessinez chaque vue 3D dans le rectangle d'un placeholder 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);

Effacez une fois par image, activez le test de découpage et ignorez tout élément qui est hors écran. Cela permet d'avoir plusieurs objets 3D sur la page pour le coût d'un seul contexte, et ils défilent avec la mise en page.

Donnez à chaque petit objet un « rig » : un groupe, une position de caméra, un point de visée et une fonction tick(t) pilotée par une horloge partagée. Construisez les rigs paresseusement et affichez-en un à la fois. Dessinez les textures sur un canvas 2D (logos, étiquettes, motifs) afin qu'il n'y ait aucun fichier de modèle à charger.

Les choses qui ont mal tourné et comment les éviter :

  • Un plan d'ombre au sol plus grand que l'élément apparaît comme un rectangle dur. Dimensionnez-le pour qu'il tienne à l'intérieur de la vue.
  • Faites pivoter un groupe, puis faites tourner un enfant, lorsque vous avez besoin de « faire tourner autour de son propre axe ».
  • Les sprites et les plans transparents se dessinent après les maillages solides ; une lueur sous un objet doit être un plan plat en dessous, pas un sprite orienté vers la caméra.

5. Sections qui ont fonctionné

  1. Héros : nom très grand, un sticker qui fait défiler leurs rôles, une phrase et l'objet interactif. Un disque coloré derrière change avec l'élément en vedette.
  2. Travail : chaque étude de cas s'ouvre sur une bande dans la couleur de cette marque avec son vrai logo, puis un carrousel d'images latéral, de vidéo et d'animation, trois faits marquants courts, quelques gros chiffres et la stack sous forme de chips. Le texte long va derrière « Lire l'histoire complète ».
  3. Expérience : une ligne par emploi avec un grand nombre chacun.
  4. Parcours : écoles ou étapes clés, chacune avec son propre objet animé.
  5. Compétences : lignes de texte en mouvement, pas un tableau.
  6. Hors travail : un objet animé par centre d'intérêt.
  7. Voyages : lieux comme des aimants de réfrigérateur sur une porte en acier qui peuvent être déplacés.
  8. Dites bonjour : le formulaire de contact.

Un en-tête flottant en forme de pilule marque la section actuelle avec un IntersectionObserver.

6. Règles de la maison

  • Que des faits. Utilisez ce que la personne a fourni. Si une section a besoin de quelque chose qu'elle n'a pas donné, laissez-la de côté. Gardez la liste « ne pas revendiquer » de chaque projet dans le fichier de contenu sous forme de commentaire.
  • Pas de compteurs de vanité. Les commits, les lignes de code, le nombre de pages ou de modèles donnent une impression d'immaturité. Montrez le produit et ce qu'il a fait.
  • Montrez moins de texte. Si un écran est principalement composé de paragraphes, transformez-le en visuel.
  • Respectez le travail des autres. Ne reconstruisez pas un modèle payant à partir de son aperçu, et ne copiez pas le design d'un produit de marque (un ballon de match nommé, par exemple) ; dessinez un original dans le même esprit. Utilisez uniquement des images sous licence libre et créditez-les sur la page.
  • Les captures d'écran ne doivent pas montrer de vrais noms de clients, montants ou visages.
  • Le mouvement a un interrupteur. Honorez prefers-reduced-motion, ajoutez un bouton « Pause motion », et proposez un repli statique lorsque WebGL est absent.
  • Mettez tout le contenu dans un seul fichier typé (src/content/portfolio.ts) afin que la personne puisse modifier les mots sans toucher aux composants.

7. Le formulaire de contact

Une procédure tRPC, contact.send, avec une entrée zod. Dans l'ordre, elle :

  1. retourne un succès silencieusement si le champ honeypot caché est rempli ;
  2. applique une petite limite de débit par adresse ;
  3. vérifie le jeton Turnstile auprès de Cloudflare et refuse s'il échoue ;
  4. envoie un email au propriétaire via Resend, avec l'expéditeur comme reply_to ;
  5. sauvegarde chaque message dans une table messages ;
  6. uniquement si le visiteur a coché une case « tenir au courant » décochée par défaut, les sauvegarde dans une table subscribers (avec le libellé de consentement et l'heure) et dans une audience Resend.

N'ajoutez jamais de personnes à une liste de diffusion sans cette case cochée.

Variables d'environnement : RESEND_API_KEY, ENQUIRY_TO_EMAIL, ENQUIRY_FROM_EMAIL, RESEND_AUDIENCE_ID, DATABASE_URL, TURNSTILE_SECRET, et la clé publique du site pour le navigateur.

Les choses qui ont mal tourné :

  • La valeur from peut déjà être Nom <adresse>. L'encapsuler à nouveau fait répondre Resend par un 422. Utilisez-la telle quelle lorsqu'elle contient <.
  • Vite n'expose que les variables qui commencent par un préfixe dans envPrefix. Si la clé du site arrive sous un autre nom, ajoutez ce préfixe.
  • Un widget Turnstile ne fonctionne que sur les noms d'hôtes qui lui sont listés dans Cloudflare.
  • En production, sans secret Turnstile, refusez d'envoyer. Utilisez les clés de test publiées par Cloudflare uniquement en développement local.
  • Ajoutez un interrupteur CONTACT_DRY_RUN=1, ignoré en production, pour que tout le chemin puisse être testé sans envoyer d'email.
  • Pour exécuter le même gestionnaire sous vite dev, ajoutez un petit plugin qui charge la fonction avec server.ssrLoadModule et lui passe une Request.
  • Construisez l'HTML de l'email avec des tableaux et des styles en ligne, et échappez chaque champ.

8. Vérifiez votre travail au fur et à mesure

Après chaque série de modifications :

  • lancez la build ;
  • prenez des captures d'écran Playwright de chaque section à 1440 et 390 pixels de large (lancez Chromium avec --use-gl=swiftshader pour que WebGL soit rendu headless) ;
  • confirmez que document.documentElement.scrollWidth - innerWidth est égal à 0 ;
  • regardez vous-même les captures d'écran avant de dire que c'est terminé.

Dites clairement ce qui n'a pas été vérifié : vrais téléphones, Safari, la sensation du mouvement, la livraison réelle des emails.

9. Mettez en ligne

  • git init, avec .env* et .vercel ignorés. Scannez les fichiers stagés pour détecter les clés avant le premier commit.
  • vercel link, ajoutez les variables d'environnement, vercel --prod.
  • Vérifiez que la page en direct se charge sans requêtes échouées et que l'API rejette une requête sans jeton Turnstile.
  • Demandez au propriétaire d'envoyer un vrai message via le formulaire en direct.

Prompt de démarrage

Donnez à la personne ceci à remplir :

Construis mon portfolio 3D en utilisant la compétence 3d-portfolio.

Mon nom et une description en une ligne :
Rôles à faire défiler dans le héros (4 à 6) :
Projets (pour chacun : faits, mon rôle, dates, lien, ce qu'il ne faut pas revendiquer) :
Expérience (entreprise, titre, dates, un résultat chacun) :
Compétences, regroupées :
Écoles ou étapes clés :
Centres d'intérêt en dehors du travail (soyez précis) :
Lieux où je suis allé :
Où se trouvent mes logos, captures d'écran et animations sur cette machine :
Liens à montrer (LinkedIn, GitHub, etc.) :
Choses que je ne veux jamais voir sur le site :

Skills associés