Personal portfolio website built with Nuxt 3, designed as a technical book (vol. 2026: preface, contents, chapters, appendices), with line-drawn figures and multilingual support.
Live: benmacha.tn
- Framework: Nuxt 3 (prerendered static pages)
- Styling: plain CSS with design tokens (
assets/css/main.css) and scoped component styles - Animations: Motion — scroll reveal, hover/press feedback, scroll progress, score pops
- i18n: @nuxtjs/i18n — French (default), English, Arabic (RTL)
- Fonts: Nunito (headings), Mulish (text) and JetBrains Mono (numbers, dates, captions) via @nuxtjs/google-fonts
- Themes: @nuxtjs/color-mode — night (default) and day
- Deployment: Cloudflare Pages
- Technical book design: running heads, chapter and page numbers, thin rules, dark (default) and light palettes
- Experience chapter drawn as a year axis with one branch per job, coloured by contract
- Trilingual support (FR / EN / AR) with full RTL layout for Arabic
- Technical blog (PHP, Symfony, Docker, Linux, DevOps) with category filters
- SEO: meta tags, JSON-LD for articles, sitemap, canonical URLs
- Respects
prefers-reduced-motion
| Route | Description |
|---|---|
/ |
Preface — Fig. 1, key facts, contents, latest jobs, Appendix A (open source) |
/experience |
Work experience as a level map |
/skills |
Skills inventory by category |
/projects |
Open source projects ("?" blocks) |
/open-source |
Open source contributions: pull requests by project, Packagist packages and organizations, drawn as isometric figures |
/education |
Education, internships and interests |
/blog |
Technical blog, /blog/:slug for articles |
- Node.js 22 (Nuxt 3.21 requires Node
^20.19or>=22.12) - npm 10 — Cloudflare installs with
npm cion npm 10; regenerate the lockfile withnpx npm@10.9.4 installif you use a newer npm
# Install dependencies
npm install
# Start dev server (http://localhost:3000)
npm run dev
# Generate the static site into .output/public
npm run generate
# Preview the generated site
npm run previewdata/opensource.ts holds the pull requests, the Packagist packages and the organizations shown on /open-source. Refresh its generated block from the GitHub search API and Packagist (Node built-ins only, the token is optional and only raises the rate limit):
GITHUB_TOKEN=$(gh auth token) node scripts/update-opensource.mjsThe workflow .github/workflows/update-opensource.yml runs it every day at 05:00 UTC (and on demand from the Actions tab) and commits data/opensource.ts to master when something changed, which redeploys the site.
The repositories and packages it reads are listed at the top of the script. The figures on the page (pull request machine, package shelf, organizations) are SVG strings built at render time in utils/anatomy/ with the isometric kit from Anatomy (MIT, see utils/anatomy/NOTICE), coloured by assets/css/anatomy.css.
public/CV.pdf and public/CV.docx are built from the hand-written Word CV in scripts/cv/source/CV.source.docx:
npm run cv # = python3 scripts/cv/build.py [--source scripts/cv/source/CV.source.docx] [--out public]Prerequisites: Python 3.10+ (standard library only), LibreOffice (soffice on the PATH), Google Chrome (set CHROME to its binary if it is not at the macOS default path), poppler (pdftotext) and ImageMagick (magick).
The script colours the company names by contract, draws the experience timeline (a red year axis with one branch per job: green for salaried jobs, yellow for the Keytchens freelance period turning green at the CDI), places each drawing from the measured position of the job titles (two LibreOffice passes), adds the open source section with the pull request counts of data/opensource.ts and the logos of utils/anatomy/logos.mjs, then exports the PDF. Running it twice gives the same files. Scratch files go to scripts/cv/.build/ (ignored).
Text changes go in the source .docx. The job dates and contracts used by the timeline (JOBS, CONTRACTS, KEYTCHENS) are at the top of scripts/cv/build.py: they mirror the experience chapter (i18n/locales/*.json and data/experience.ts), so update both when a job changes.
├── assets/css/ # Design tokens and shared book primitives
├── components/ # Layout, sections and UI components
│ └── open-source/ # Open source figures, ledger and lists
├── composables/ # Resume data, blog helpers, chapters, Motion effects
├── data/ # Blog articles, skills, site links, open source contributions
├── i18n/locales/ # Translation files (fr.json, en.json, ar.json)
├── layouts/ # Default layout (header, footer, progress bar)
├── pages/ # File-based routing
├── plugins/ # Motion directives (v-reveal, v-lift, v-wiggle)
├── public/ # Static assets (CV.pdf, images)
├── scripts/ # update-opensource.mjs (refreshes data/opensource.ts), cv/ (CV build)
├── utils/anatomy/ # Isometric figure builders (vendored Anatomy kit)
└── nuxt.config.ts # Nuxt configuration
Cloudflare Pages builds and deploys master automatically (npm run build, output dist). Other branches get preview deployments.
MIT