← La bibliothèque

Noray

Portfolio pour une designeuse qui code — trois pages, un seul ciel de particules

Template complet Réservé aux membres Ouvrir la démo Débloquer la bibliothèque
Aperçu du template Noray : un nuage de particules lumineuses formant une étincelle sur un ciel bleu nuit, le titre en italique « I build web experiences people remember » et deux boutons.
  • 3 200 particules, 30 KoLe ciel est en WebGL brut, écrit à la main — les nappes de fond qui dérivent comprises. L’original demande Three.js, React Three Fiber et une passe de post-traitement : 3,6 Mo de JavaScript.
  • 19 animations mesuréesLues dans le bundle de l’original — durées, courbes, décalages, seuils — puis refaites sans GSAP, sans ScrollTrigger, sans Lenis.
  • 0 requête externeAucune dépendance, aucun CDN, aucune étape de build. On ouvre le fichier et ça marche.

Ce que c’est

Le portfolio d’un développeur créatif, en trois pages. L’accueil : un hero plein écran, une déclaration, trois projets encadrés de deux rails collants, une scène épinglée où neuf services défilent en trois panneaux, un processus en quatre étapes, un appel de fin en très grand, un pied de page. La page « à propos » : le parcours, les outils dans une section entièrement en négatif, l’expérience. La page « work » : les trois projets en carrousel plein écran. Derrière les trois, le même champ de particules, qui change de forme section après section.

Le design reprend celui du site d’un design engineer en activité, dont rien n’a été gardé : ni les images, ni le code. L’original est une application Next.js qui embarque Three.js, React Three Fiber, GSAP avec ScrollTrigger et SplitText, Lenis et opentype.js : 3,6 Mo de JavaScript pour la seule page d’accueil. Ici la même page tient en 42 Ko.

C’est tout l’intérêt : ce template n’est pas là pour être admiré, il est là pour être lu, cassé et refait.

Ce que tu changes en premier

Les couleurs — quatre lignes

Tout en haut de assets/css/style.css :

--night: #070b1a;   /* le fond, et la lueur qui monte du bas */
--halo:  #243c7e;   /* la lumière derrière la page */
--spark: #4a5a9e;   /* les particules */
--ink:   #f4f6fa;   /* le texte */

Une chose à comprendre avant de toucher au reste : la couleur des particules existe deux fois. Un shader ne lit pas une variable CSS, donc --spark est répété dans field.js sous forme de trois nombres entre 0 et 1. Changer l’une oblige à changer l’autre.

Le nom

Cherche « Noray » dans index.html. Aucune classe CSS ne porte le nom de la marque : un rechercher-remplacer ne peut rien casser. Un endroit en plus : sampleGlyph('N') dans field.js — cette lettre est la forme que prennent les particules du pied de page.

Les tailles

L’échelle typographique est déclarée une fois, puis redéclarée à chaque point de rupture. Un titre n’a donc jamais sa propre media query : tu changes --h2 et toute la page suit.

Les 19 effets, et comment ils sont faits

L’original ne cache pas ses réglages : ils sont dans son bundle, sous forme de constantes nommées et d’appels GSAP en clair. Tout ce qui suit vient de là, puis a été mesuré dans le navigateur — jamais deviné à l’œil.

EffetComment
Lignes qui montent26 blocs de textele texte est découpé en vraies lignes par la mesure, chacune masquée ; yPercent 320 → 0 avec 10° d’inclinaison, 0,4 s, 0,07 s d’écart
Texte qui s’imprime4 titresdécoupé en caractères, opacité 0,1 → 1 pilotée par le défilement ; le JS écrit une seule valeur, clamp() fait le reste
Étapes qui s’éclairentle processusl’opacité suit la distance au centre de l’écran — l’étape au milieu est celle qu’on peut lire
Scène épingléeservicesmême mécanique, mais trois écrans au lieu de six : un écran de défilement par panneau. À six, la section met deux fois trop longtemps à passer
Couvertures en directtravaux, page « work »chaque couverture charge le vrai site voisin dans un cadre quand la carte arrive à l’écran — rendu en 1440 de large puis mis à l’échelle, pour qu’il serve bien sa mise en page de bureau. L’image fixe reste dessous, en repli et en attente
Rails jumeauxtravauxle projet le plus proche du milieu de l’écran donne son nom à gauche et son lien à droite
Champ de particulestoute la page3 200 points en WebGL brut ; les formes sont calculées par superquadriques, pas modélisées — une étoile, un diamant taillé, une flamme, une lettre
Retrait devant le textesections de lectureabsent de l’original : sur une section marquée data-field-quiet, le canvas descend à 30 % d’opacité. Un nuage dense entre l’œil et un paragraphe, c’est joli une seconde et illisible ensuite
Ciel d’étoilestoute la page45 % des points vivent sur une nappe lointaine, dessinée dans sa propre passe : elle ne tourne pas avec la forme, sinon la moitié des étoiles passerait derrière la caméra deux fois par tour
Nappes de fondtoute la pageun rectangle plein écran, deux octaves de bruit fractal qui glissent entre --night et --halo ; descendre les accélère, remonter les freine
Lettre en particulespied de pagele glyphe est dessiné une fois dans un canvas 2D et ses pixels relus : 30 lignes au lieu d’une bibliothèque de 90 Ko
Six tours de défilementtoute la pagele nuage tourne de −2160° du haut en bas de la page, et continue un instant après l’arrêt
Graisse au curseurlogo, menu, liens du piedRed Hat Display est une police variable : l’axe wght suit le curseur, 400 → 900 en 80 px
Curseur personnalisépartoutquatre traits en mix-blend-difference qui se replient en anneau au-dessus d’un lien
Menu qui se déplieen haut à droiteclip-path plutôt qu’une hauteur, plus inert pour que le panneau fermé quitte l’ordre de tabulation
Aperçu en grandcouvertures de projetune carte ouvre le projet plein écran dans un <dialog> natif : le site voisin s’y charge vivant, il se défile et se clique pour de bon. Adresse relative, même origine, chargée au clic et jamais au chargement ; l’image fixe reste le repli
Transition de pageentre les trois pagesle contenu sort en 0,36 s, le nuage se disperse, puis le lien suit son cours. Seule une navigation ordinaire est interceptée — sans JavaScript, un lien reste un lien
Section en négatifpage « à propos »le ciel bascule pour de bon : field.js interpole les deux shaders — particules comprises — vers leur variante claire. Une nappe CSS double la mise, et porte la section seule si WebGL manque
Carrousel de projetspage « work »un vrai défileur horizontal avec scroll-snap : il glisse, se balaye et répond aux flèches avant qu’aucun script ne tourne. Le script ajoute la perspective, le tirage au pointeur, la molette verticale transformée en déplacement latéral, et le compteur

Ce qu’il faut savoir

  • Aucune requête externe. Tout est local, polices comprises. L’original va chercher ses bundles et ses polices sur son propre CDN.
  • Le ciel est en CSS, pas dans le canvas. Le dégradé est un vrai fond ; le canvas ne dessine que les particules. Sans JavaScript, sans WebGL, ou si le contexte est perdu, il reste un ciel de nuit.
  • Un contexte WebGL perdu se peint en blanc. Un portable qui sort de veille suffit. Le canvas écoute webglcontextlost, s’efface, et reconstruit tout quand le contexte revient.
  • L’apparition au défilement ne peut pas masquer le contenu. L’état caché n’existe qu’une fois le JavaScript passé. Vérifié en retirant les balises <script> : les 26 blocs restent visibles et les trois panneaux de services s’empilent les uns sous les autres.
  • Zéro style en ligne, zéro script en ligne. Le template respecte la politique de sécurité stricte du site, vérifié en le servant avec cette politique devant — pas supposé.
  • Vérifié à 390, 1440 et 2560 px, au chargement et après avoir déroulé toute la page, sans débordement horizontal. Le banc est livré avec : node _mesures/banc.mjs.

Ce qui n’est pas parfait

Mieux vaut le dire ici que te le laisser découvrir.

  • Les trois couvertures de projet sont des rendus abstraits, pas des captures. Les couvertures de l’original sont des logotypes de marques réelles : impossible à reprendre, et une image générée n’a pas le droit d’en inventer un. Le nom du projet est écrit en HTML par-dessus l’image — ce qui a au moins l’avantage d’être traduisible.
  • Les vidéos de la page « work » d’origine ne sont pas reprises. Elle y joue trois captures animées de 3 à 4 Mo chacune, en textures WebGL. Elles appartiennent à ses clients et ne sont pas redistribuables : le carrousel montre les mêmes images fixes que l’accueil. On perd le mouvement dans les cartes, on gagne 11 Mo.
  • Les cartes ne se déforment pas comme celles de l’original. Là-bas, un shader plie le plan pendant le glissement. Ici c’est une perspective CSS et un cisaillement proportionnel à la vitesse : le mur incurvé y est, l’élasticité non.
  • « See project » pointe sur l’ancre du projet. Les trois projets sont inventés, il n’y a donc pas de destination honnête. Remplace les trois href par tes URL.
  • Pas de post-traitement. L’original passe la scène dans un bloom. Ici la lueur est dans le shader du point lui-même — un cœur, un halo, un mélange additif. La différence se voit surtout sur les plus gros points, un peu moins diffus.
  • Le sélecteur de langue est décoratif. L’original a une version espagnole complète ; un template d’une page n’a qu’une langue.
  • Le formulaire de contact n’envoie rien. C’est un <dialog> natif et le bouton d’envoi remet les champs au client de messagerie du visiteur. L’endroit où brancher ton point d’entrée est indiqué dans main.js.

Il t’attend dans la bibliothèque.

Noray est inclus dans l’abonnement, avec le reste de la bibliothèque et la formation complète.

Accès immédiat · Sans engagement · Résiliable en un clic