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é
- 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.
- 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 ».
- Expérience : une ligne par emploi avec un grand nombre chacun.
- Parcours : écoles ou étapes clés, chacune avec son propre objet animé.
- Compétences : lignes de texte en mouvement, pas un tableau.
- Hors travail : un objet animé par centre d'intérêt.
- Voyages : lieux comme des aimants de réfrigérateur sur une porte en acier qui peuvent être déplacés.
- 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 :
- retourne un succès silencieusement si le champ honeypot caché est rempli ;
- applique une petite limite de débit par adresse ;
- vérifie le jeton Turnstile auprès de Cloudflare et refuse s'il échoue ;
- envoie un email au propriétaire via Resend, avec l'expéditeur comme
reply_to; - sauvegarde chaque message dans une table
messages; - 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
frompeut déjà êtreNom <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 avecserver.ssrLoadModuleet lui passe uneRequest. - 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=swiftshaderpour que WebGL soit rendu headless) ; - confirmez que
document.documentElement.scrollWidth - innerWidthest é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.vercelignoré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 :