- #web
- #design-system
- #webgl
- #i18n
leafranger.dev
Questo sito: un portfolio in due lingue in Next.js 16 con un design system tutto suo, un campo di particelle che disegna il mio drago come una mesh low-poly, e accessibilità e SEO verificate dai test a ogni modifica.
- Ruolo
- Unico designer e sviluppatore: il design system, il codice e i testi in entrambe le lingue.
- Risultato
- Lighthouse 100 su accessibilità, best practice e SEO da mobile, senza spostamenti di layout. Ogni pagina in entrambe le lingue viene verificata in CI: stato, tag SEO, dati strutturati, axe in entrambi i temi, larghezza da telefono.
- Anno
- 2026
- Stato
- in corso
- Stack
- Next.js 16
- React 19
- TypeScript
- Tailwind 4
- three.js
- React Three Fiber
- Radix
- MDX
- Playwright
- Vercel
L'inizio
Una prima versione che faceva il suo lavoro
La prima versione di questo sito è andata online a giugno 2025, con una pagina per i miei lavori su Roblox e una per i progetti personali. Faceva il suo lavoro, ma volevo di più: un sito che raccontasse com'è andato ogni progetto invece di elencarlo e basta, e che sembrasse mio.
L'obiettivo
Un solo sito per tre tipi di visitatori
Clienti che cercano un bot o un'automazione, studi Roblox che cercano suoni, interfacce o script, e aziende che valutano il mio lavoro: tutti devono trovare quello che cercano e un modo chiaro per scrivermi. In italiano e in inglese, con la stessa sostanza. E doveva sembrare mio, con il mio drago e i miei colori, non un template.
L'avventura
Ricostruito da zero
L'ho ricostruito un pezzo alla volta. Prima un linguaggio visivo con regole sue: il viola per tutto ciò che si può premere, il lime per ciò che è attivo, angoli quasi squadrati, e ogni pulsante, menu e player progettato una volta e riusato ovunque. Poi ogni pagina in due lingue, i case study da leggere per schede, e le pagine Roblox dove ogni suono si ascolta e ogni interfaccia si apre.
La pagina Roblox, pensata per gli studi: il mio nome come un muro di mattoncini da sfiorare e far esplodere. La parte di cui vado più fiero è il drago in home. È fatto di particelle che lasciano lo sfondo e si radunano nel mio avatar, poi si girano a seguire il puntatore. Clicca sullo sfondo e si scompone al rallentatore, per poi ricomporsi.
La fine
Un sito che si controlla da solo
Si carica in fretta, funziona da tastiera e con uno screen reader, e resta tranquillo per chi preferisce meno movimento. Ogni volta che cambio qualcosa, ogni pagina in entrambe le lingue viene verificata automaticamente prima di andare online. È ancora in corso: il redesign arriva pagina per pagina, e questo case study cresce con lui.
- Lighthouse accessibilità, best practice e SEO
- 100
- Lighthouse accessibilità, best practice e SEO
- Lingue, stessa sostanza
- 2
- Lingue, stessa sostanza
- Spostamenti di layout durante il caricamento
- 0
- Spostamenti di layout durante il caricamento
Prossimi passi
Ancora più veloce
Su un telefono gli effetti richiedono un po' di caricamento prima della prima schermata. Caricarne una parte più tardi è il prossimo passo.
Più media miei
Screenshot e clip reali per ogni progetto, e i miei modelli 3D.
Un design system tutto suo
Ruoli dei colori (viola per premere, lime per ciò che è attivo), token tipografici fluidi, tre raggi per gli angoli e un kit di pulsanti, form, menu, media e dialoghi, tutti in una pagina laboratorio.
Un drago fatto di particelle
La home disegna il mio avatar come una mesh low-poly che si gira verso il puntatore; un clic lo fa esplodere e poi lo ricompone.
Due lingue, un solo sito
Ogni pagina in italiano e in inglese. Cambiare lingua non ferma la pagina: il nuovo testo entra dietro una linea di scansione lime.
Accessibile in partenza
Titoli e testo veri dietro ogni effetto, un contorno di focus visibile, il link per saltare al contenuto, il contrasto verificato in entrambi i temi e una versione ferma per il movimento ridotto.
Funziona anche senza effetti
La scrivania 3D nella pagina Chi sono ha accanto un elenco di pulsanti, quindi funziona da tastiera e senza WebGL, dove l'elenco è tutto.
Consenso fatto bene
Niente di Google si carica prima di una scelta. Rifiuta e Accetta hanno la stessa dimensione, e Impostazioni cookie nel footer permette di cambiare idea in ogni momento.
Leggibile da motori di ricerca e AI
Tag canonical e hreflang, immagini di condivisione generate in fase di build, una sola Person nei dati strutturati di ogni pagina e file llms.txt generati dai dati dei progetti.
Verificato a ogni modifica
La CI esegue lint, il controllo dei tipi, una build di produzione e i test su ogni pagina della sitemap.
Due temi
Il tema scuro è quello predefinito e il chiaro è a un clic, svelato da un cerchio che si allarga dal pulsante. Il tema chiaro non è lo scuro al contrario: i pannelli si sollevano su un'ombra leggera, i bordi dei campi sono più scuri per restare visibili e il testo lime passa a una tonalità più profonda, perché quella brillante è troppo chiara da leggere sul bianco.
Roblox, disciplina per disciplina
La pagina Roblox porta a una pagina per disciplina. Ogni gioco si apre come un pannello, raggruppato come è stato fatto il lavoro, con un player per ogni suono e un lightbox per ogni immagine. Un link può puntare dritto a un gioco o a un gruppo, e un pulsante per commissionare un lavoro compare appena il titolo esce dallo schermo.
Lavori da filtrare
Gli argomenti in comune tra almeno due progetti diventano filtri. La scelta resta nell'indirizzo, così un link come «Guarda questi lavori» della home apre l'elenco già filtrato.
Su un telefono
Sui telefoni il drago ha una fascia tutta sua sotto l'header e il testo inizia sotto, così niente lo copre. I pulsanti diventano a tutta larghezza, la navigazione diventa un menu a schermo intero, e il cambio lingua lo chiude così la linea di scansione passa sulla pagina.
Quando una pagina non esiste
Lo stack
Framework
Next.js 16, React 19
App Router con Turbopack, server component di default e il proxy di Next 16 per il reindirizzamento della lingua. Pubblicato su Vercel, con route API per le parti che richiedono una chiave segreta lato server.
Stile
Tailwind 4
Configurazione in CSS: i token di colori, tipografia, angoli ed easing sono variabili CSS in un unico foglio del design system, cambiate dalla classe del tema.
3D
three.js, React Three Fiber
L'hero a voxel della pagina Roblox e la scrivania della pagina Chi sono, caricati solo quando servono, messi in pausa fuori dallo schermo e sostituiti da un poster senza WebGL2.
Contenuti
MDX
Case study e pagine dei giochi sono MDX per lingua, con blocchi come il percorso, le statistiche e le sfide usabili senza import.
Il campo di particelle
Dietro la pagina ci sono due canvas: lo strato principale con il plexus e la forma dell'hero, e uno strato lontano con tre livelli di profondità sfocati.
Campionamento
Un Web Worker legge i pixel dell'avatar e li divide in punti di contorno, occhiali, denti, bocca, lingua e riempimento.Mesh
I punti vengono triangolati con Delaunator; restano solo i triangoli sull'inchiostro con lati corti.Reclutamento
Mentre l'hero è sullo schermo, le particelle dello sfondo lasciano la loro posizione e si posano sui punti della forma.Ombreggiatura
Le facce vengono raggruppate per colore e illuminate da un riflesso che gira con il drago verso il puntatore.
Ogni particella dello sfondo ha una posizione di riposo presa da una sequenza a bassa discrepanza, quindi qualunque prefisso della lista è distribuito in modo uniforme: reclutare dalla fine lascia uniforme il resto del campo. I livelli di profondità si muovono con una parallasse 1/z, e la loro sfocatura è disegnata con sprite gaussiani invece che con filtri CSS.
Le lingue
Ogni pagina vive sotto /en o /it. Un indirizzo senza lingua riceve un 307 verso la lingua salvata in un cookie, altrimenti verso quella del browser che corrisponde meglio, altrimenti verso l'inglese. I testi dell'interfaccia stanno in due dizionari, e quello italiano è tipizzato su quello inglese, quindi una chiave mancante fa fallire il controllo dei tipi. I testi lunghi sono MDX per lingua.
Sopra le lingue c'è un solo layout radice, quindi cambiare lingua è una navigazione leggera: campo di particelle, cursore e suono continuano a girare, e una view transition con nome accoppia la pagina vecchia e quella nuova sotto la linea di scansione.
Sfide
Problema
Un thread principale bloccato
Campionare il drago richiedeva circa 850 ms su un telefono di fascia media, un blocco nel mezzo della prima visita.
Soluzione
Il campionamento è passato in un Web Worker. Il suo avvio vive fuori dal modulo delle forme, perché un URL del worker dentro il modulo che il worker stesso importa mandava in loop la build di Turbopack.
Problema
Una sfocatura che costava fotogrammi
Tre canvas sfocati via CSS giravano a 3-4 fps in rendering software.
Soluzione
La sfocatura viene disegnata: i punti sono sprite gaussiani e i collegamenti un tratto largo e tenue sotto uno sottile, su un canvas a risoluzione 0,6.
Problema
Un banner dei cookie in ritardo
Disegnato dopo l'idratazione, il banner diventava l'elemento più grande e più lento di ogni prima visita.
Soluzione
Arriva già nell'HTML del server, nascosto dal CSS finché uno script inline, eseguito prima del primo disegno, non segna la pagina come in attesa di una scelta.
Problema
Un hydration mismatch
Con il proxy davanti, il percorso poteva essere diverso tra l'HTML del server e l'idratazione, e l'indicatore della pagina corrente nell'header non coincideva.
Soluzione
L'header ora legge la route dai segmenti del layout renderizzato, che fanno parte dell'albero, quindi server e client sono sempre d'accordo.
Controlli di qualità
Test di base
Playwright
Ogni pagina della sitemap: stato, un solo h1, canonical, hreflang, immagine di condivisione, dati strutturati con la Person, nessun errore in console, niente scorrimento laterale alla larghezza di un telefono.
Accessibilità
axe
Le pagine principali in entrambi i temi, senza violazioni gravi o critiche, più il flusso del consenso, il 404 localizzato e il movimento ridotto.
Sicurezza
Header
Una content security policy e gli altri header di sicurezza su ogni route, e route API che validano l'input e servono solo ciò che è in una lista consentita.
CI
GitHub Actions
Lint, controllo dei tipi, una build di produzione e i test a ogni push.
Il codice sorgente è pubblico?
No, il repository è privato. Posso mostrarne volentieri qualsiasi parte in una chiamata.
Mi traccia?
Solo se accetti gli analytics. Fino ad allora non si carica niente di Google, e l'informativa privacy elenca ogni servizio con cui parla il tuo browser.
Perché il plexus c'è solo in home?
È la firma del sito, ma dietro i testi da leggere era troppo. Tutte le altre pagine hanno uno sfondo pieno.
Cosa succede senza WebGL o con il movimento ridotto?
Le scene 3D lasciano il posto a un poster o a un elenco di pulsanti, e con il movimento ridotto le particelle restano ferme, le forme si compongono subito e l'intro viene saltata.
Puoi costruire qualcosa del genere per me?
Sì. Dimmi cosa deve fare e per quando ti serve, nella pagina contatti.
Ti serve qualcosa di simile?
Dimmi cosa deve fare, dove gira e per quando ti serve.
Altri lavori

- #web
- #discord
- #bots
Arise Crossover Tools
Timer in diretta per dungeon ed eventi di Arise Crossover, alimentati da un bot Discord che legge gli annunci.

- #computer-vision
- #automation
- #roblox
Leaf's Grand Blue Autofish
Un'app gratuita per Windows che pesca per te in Grand Blue su Roblox e registra ogni pesce che legge.

- #discord
- #bots
- #api-integration
BotFire
Un bot Discord per la mia community: giochi suggeriti e calendario Twitch sincronizzato con Google Calendar.