Codice sorgente del sito personale e blog di Cristian Castellari, pubblicato su GitHub Pages all'indirizzo cristiancastellari.it.
Il tema — scritto da zero, senza framework CSS/JS di terze parti — è open source e riutilizzabile: se ti piace, puoi prenderlo come base per il tuo sito personale. I contenuti (post, CV, progetti) restano ovviamente quelli dell'autore.
- Jekyll puro, nessuna pipeline di build front-end: CSS compilato nativamente da Jekyll/Sass, zero JavaScript di terze parti (niente jQuery, niente bundler).
- Multilingua (italiano/inglese): permalink sotto prefisso
/it///en/, switcher lingua con taghreflang, tutto gestito via front matter e_data/i18n.yml— nessun plugin i18n (non compatibile con la whitelist plugin di GitHub Pages). - Tema chiaro / scuro / automatico, persistente fra le pagine (
localStorage), a scelta dell'utente o a seguito diprefers-color-scheme. - Multi-pagina: Home, CV/Esperienze, Progetti, Blog (con categorie e paginazione), Contatti — non una singola landing a scroll infinito.
- Blog con categorie configurabili (proprie per lingua), indice categorie con conteggio articoli, paginazione filtrata per lingua, ricerca client-side (fetch di
search.json, nessuna libreria esterna, risultati filtrati sulla lingua corrente), commenti Disqus, bottoni di condivisione con colori di brand. - Copertine dei post theme-adaptive: due immagini per articolo, una per tema chiaro e una per scuro, selezionate puramente via CSS.
- Statistiche GitHub live (stelle/fork) sulle card dei progetti, con cache in
localStorageper restare sotto il rate limit dell'API pubblica. - Icone via Fork Awesome, con SVG inline in
_includes/icons/per i loghi non presenti nel set (X, Bluesky) e per le bandiere dello switcher lingua. - Permalink dei post configurabili e stabili — pensato per non rompere l'indicizzazione quando si cambiano categorie o struttura del blog (vedi
jekyll-redirect-fromin_config.yml). - Favicon configurabile: una chiave
favicon:in_config.yml, nessun path hardcoded nei template. - Cookie consent GDPR con CookieConsent v3 (MIT, gratuito): banner IT/EN nativo, categorie tecnici/terze parti, tema agganciato ai token CSS del sito, blocco dichiarativo degli script di terze parti (es. Disqus) fino al consenso.
- Jekyll 4.4.1 (Ruby), dichiarato direttamente in
Gemfile— non più tramite la gemgithub-pages, sostituita per poter buildare/pubblicare tramite GitHub Actions invece della pipeline legacy "Deploy from a branch" (vedi CHANGELOG.md [2.8.0]) - Sass nativo via Dart Sass (
jekyll-sass-converter3.x) - Plugin:
jekyll-seo-tag,jekyll-redirect-from,jekyll-target-blank(link esterni contarget="_blank" rel="noopener noreferrer"automatico) — la paginazione del blog è gestita a mano, non da un plugin, perché deve filtrare per lingua - Fork Awesome per le icone
Nessuna dipendenza Node/npm: non serve alcuna build front-end.
Richiede Ruby (con Bundler).
# dipendenze Ruby (Jekyll e plugin)
bundle install
# avvia il server di sviluppo su http://127.0.0.1:4000
bundle exec jekyll serve
# per vedere anche i post con data futura (es. articoli programmati)
bundle exec jekyll serve --future_posts/it/,_posts/en/: articoli del blog (Markdown), organizzati per lingua. I post esistenti prima del multilingua restano solo init/; i nuovi si scrivono in entrambe le lingue collegandole con lo stessotranslation_keyin front matter.it/,en/: pagine statiche per lingua (Home, CV, Progetti, Contatti, pagine categoria del blog), ciascuna conpermalink:esplicito e — dove esiste una versione nell'altra lingua — lo stessotranslation_key._data/blog.yml: categorie del blog, una riga per lingua per categoria (nome visualizzato + URL); il camponameè anche la chiave con cui i post si associano alla categoria._data/i18n.yml: dizionario delle stringhe UI condivise (nav, bottoni, paginazione, ecc.),{it, en}per chiave._data/index/: contenuti strutturati di Home/CV (careers.yml,skills.yml,projects.yml), campi testo come mappa{it, en}._layouts//_includes/: template del tema;_includes/icons/per le SVG inline (loghi social, bandiere)._sass/: design system (_tokens.scssper palette/tipografia/spaziatura, un file per componente).
Ogni post può avere un campo last_modified_at: nel front matter (formato AAAA-MM-GG, o AAAA-MM-GG HH:MM:SS):
---
title: Il mio post
date: '2019-12-16 11:30:00'
last_modified_at: '2026-08-17'
---Se presente e diverso dal giorno di date, _layouts/post.html mostra "Aggiornato il" accanto a data/tempo di lettura. È volutamente manuale, non calcolato in automatico dal log Git: un calcolo automatico risentirebbe di qualunque commit tecnico che tocca il file (refactor, migrazioni, correzioni di battitura) anche senza una vera revisione del contenuto. Impostalo a mano solo quando aggiorni davvero un post nel merito.
- Identità e social: sezione
author:in_config.yml. - Favicon: chiave
favicon:in_config.yml(svg, ico, jpg, png vanno tutti bene). - Lingua di default:
default_langin_config.yml— unico punto da cambiare per spostare il fallback su un'altra lingua. - Palette colori: variabili CSS in
_sass/_tokens.scss(tema chiaro di default, override automatico/esplicito per lo scuro) —_sass/_cookieconsent.scssla riusa automaticamente, nessuna palette separata da mantenere. - Cookie consent: categorie, testi IT/EN e shortname Disqus in
_includes/cookieconsent.html/_includes/comments.html. - CV/Esperienze:
_data/index/careers.yml(timeline) e_data/index/skills.yml(competenze per area). - Progetti:
_data/index/projects.yml. - Categorie del blog:
_data/blog.yml— aggiungere una riga per lingua e creare la pagina categoria corrispondente init//en/. - Stringhe UI:
_data/i18n.yml— una chiave per etichetta, valore per lingua.
Le modifiche più significative sono tracciate in CHANGELOG.md, inclusi la ricostruzione completa del tema (v2.0) e il sito multilingua IT/EN (v2.1).
Il repository usa due licenze distinte, dettagliate in LICENSE:
- Codice del tema (
_layouts/,_includes/,_sass/,assets/): MIT — riusabile liberamente, anche per progetti propri. - Contenuti (post del blog, testi di CV/pagine, immagini): Creative Commons BY-SA 4.0 — riuso consentito con attribuzione e stessa licenza, vedi disclaimer.