La web personal de xarop — front-end engineer. Versió 2026.
Sense WordPress. Sense base de dades. Sense JS obligatori. Només HTML semàntic, CSS modern i Markdown.
Demo: xarop.com · Disseny: DESIGN.md · Agents: AGENTS.md
- Static site generat des de Markdown amb un script de Node.
- Topbar sticky amb 3 columnes (logo · nav · eines): selector d'idioma, selector de sabor i toggle de tema en un menú desplegable. Hamburger CSS-only al mòbil (sense JS).
- Set sabors de color intercanviables (
maduixa,nabiu,gerd,menta,llimona,taronja,regalessia). - Mode clar/fosc automàtic (
prefers-color-scheme) amb toggle manual sol/lluna. - Multilingüe estàtic (CA / EN / ES / SV / IT): pàgines generades a
/en/,/es/,/sv/,/it/ambhreflang,x-defaulti sitemap complet. Els slugs de blog i portfolio es tradueixen per idioma. - Comentaris via giscus (GitHub Discussions) als posts del blog, sincronitzats amb el tema clar/fosc.
- Zero JS obligatori. Millores progressives opcionals via
enhance.js. - Accessible (WCAG 2.1 AA), semàntica HTML pura.
- Deploy automàtic a GitHub Pages via Actions.
- RSS feed incorporat.
.
├── content/ Markdown (la font de veritat)
│ ├── pages/ Pàgines (index, cv, contacte)
│ ├── cv/ CVs per perfil (cv-frontend-react.md, cv-design-engineer.md…)
│ ├── blog/ Articles
│ └── portfolio/ Projectes
├── src/
│ ├── css/ tokens.css · flavors.css · main.css
│ ├── js/ enhance.js (opcional)
│ ├── templates/ base.html
│ └── assets/ logo, fonts, imatges
├── scripts/
│ ├── build.js Build script (genera CA + EN/ES/SV/IT)
│ ├── i18n.js Idiomes i etiquetes de navegació
│ ├── translate.js Motor de traducció (Claude/DeepL/Google/MyMemory/Libre)
│ └── import-wp-comments.js Migració de comentaris WordPress → giscus
├── translations/ Caché de traduccions (git-ignored)
│ ├── en/ {slug}.json per cada peça traduïda
│ ├── es/
│ ├── sv/
│ └── it/
├── dist/ Generat (git-ignored)
├── DESIGN.md Sistema de disseny
├── AGENTS.md Ús d'agents IA en el projecte
└── .github/workflows/ Deploy a GH Pages
Requereix Node.js ≥ 18.
npm install # instal·lar dependències
npm run build # generar dist/
npm run dev # servir a http://localhost:4000Crea content/blog/el-meu-post.md:
---
title: El meu post
date: 2026-04-23
description: Una descripció breu.
tags: [css, web]
flavor: menta
---
Contingut en **Markdown**.Guarda, git push. GitHub Actions el publica automàticament.
content/portfolio/projecte.md:
---
title: Nom del projecte
year: 2026
role: Front-end
description: Descripció breu.
url: https://exemple.com
---
Detalls en Markdown...Qualsevol projecte amb image: i url: al frontmatter mostra automàticament el screenshot a l'aside lateral. No cal cap marcador addicional.
Si es vol un aside completament personalitzat (contingut diferent de la captura), s'usen els marcadors <!-- aside --> i <!-- main --> al cos del Markdown:
---
...
---
<!-- aside -->
Contingut personalitzat de l'aside (HTML o Markdown).
<!-- main -->
## El contingut principalQualsevol fitxer de blog/ o portfolio/ que tingui url: al frontmatter però no image: serà processat per l'script de screenshots:
npm run screenshotsL'script:
- Detecta tots els
.mdamburl:i senseimage:. - Obre cada URL amb Puppeteer (headless Chrome), fa scroll complet per activar lazy load, i converteix a WebP (1200px ample, q85).
- Desa la imatge a
src/assets/images/screenshot-{slug}.webp. - Afegeix automàticament
image: assets/images/screenshot-{slug}.webpal frontmatter del fitxer.
Per forçar un nou screenshot d'un fitxer que ja té image:: elimina la línia image: del frontmatter i torna a executar npm run screenshots.
content/cv/cv-nom.md amb frontmatter mínim:
---
title: Rol — especialitat
slug: nom-del-rol
description: Descripció breu per a la llista.
role: Rol
---
Contingut en **Markdown**.Publicat automàticament a /cv/nom-del-rol/. La pàgina comparteix el aside de la secció CV (foto + contacte).
content/pages/pagina.md → publicada a /pagina/.
El build genera cinc versions estàtiques del lloc: CA (arrel), EN (/en/), ES (/es/), SV (/sv/), IT (/it/).
# Motor de traducció
TRANSLATE_ENGINE=deepl # claude | deepl | google | mymemory | libre
# Claus d'API (la del engine triat)
ANTHROPIC_API_KEY=...
DEEPL_API_KEY=...
# Scope: quants articles/projectes recents traduir
TRANSLATE_BLOG_LIMIT=10 # "all" per traduir-ho tot
TRANSLATE_PORTFOLIO_LIMIT=10| Engine | Qualitat | Límit gratis | Clau necessària |
|---|---|---|---|
claude |
Molt alta, entén Markdown | ~quota Anthropic | ANTHROPIC_API_KEY |
deepl |
Excel·lent | 500k cars/mes | DEEPL_API_KEY |
google |
Bona | Gratis (pot bloquejar IPs) | Cap |
mymemory |
Acceptable | 5k–50k cars/dia | Opcional (MYMEMORY_EMAIL) |
libre |
Variable | Depèn instància | LIBRE_URL + LIBRE_API_KEY |
Les traduccions es desen a translations/{lang}/{tipus}-{slug}.json. En el pròxim build es reutilitzen directament (sense trucades a l'API). Per forçar una re-traducció: esborra el fitxer de caché o posa TRANSLATE_FORCE=1.
- Pàgines, CV i índexos → sempre traduïts (tots els idiomes). Els slugs no canvien.
- Blog i portfolio → els
Nmés recents (TRANSLATE_BLOG_LIMIT/TRANSLATE_PORTFOLIO_LIMIT). El slug de la URL es tradueix per idioma (ex:/blog/lamistat-sha-tornat-low-cost/→/en/blog/friendship-has-become-low-cost/). Els articles antics queden en català i s'indexen al sitemap sense hreflang d'altres idiomes. - La paraula "xarop" mai es tradueix (protecció de marca).
dist/sitemap.xml inclou <xhtml:link rel="alternate" hreflang="…"> (inclòs x-default → CA) per a cada URL. Els posts i projectes traduïts fan servir el slug traduït per idioma. Les pàgines no traduïdes apareixen al sitemap en CA sense alternates. robots.txt apunta al sitemap.
Set sabors disponibles, cadascun amb la seva paleta CSS completa:
| Sabor | Color | Caràcter |
|---|---|---|
maduixa |
#FF0000 |
El clàssic. Vermell intens. |
nabiu |
#3B4CCA |
Blau fosc, digital. |
gerd |
#CF255E |
Rosa fosc, femení. |
menta |
#00A878 |
Fresc, net, verd. |
llimona |
#D4A300 |
Àcid, daurat. |
taronja |
#FF6B35 |
Càlid, enèrgic. |
regalessia |
#1A1A1A |
Seriós, quasi negre. |
Cada pàgina pot tenir el seu sabor via front-matter (flavor: menta). L'usuari pot canviar-lo al picker del header (requereix JS). Per afegir un sabor nou: edita src/css/flavors.css.
El JS és 100% opcional — la web, la navegació i el hamburger del mòbil funcionen sense ell.
Quan el JS és actiu, enhance.js afegeix:
- Flavor picker al topbar (desplegable, persit a localStorage)
- Theme toggle sol/lluna (persit a localStorage;
?theme=dark|lightvia URL) - Selector d'idioma desplegable al topbar (amaga l'idioma actiu)
- Sincronització de tema amb giscus (comentaris clar/fosc en temps real)
El hamburger del mòbil funciona sense JS (CSS-only via <input type="checkbox">).
Codi: MIT. Contingut: © xarop, reservats tots els drets.