Personal
2026
Personal portfolio
This site. Pages are Astro. Copy lives in three glob collections — projects, experience, education — with a Zod schema of bilingual `{ en, es }` fields. Projects carry a `kind` (personal or company) and an editorial `order`. Spanish is a mirrored tree under `src/pages/es/` that shares the same components; English has no URL prefix. A small URL layer keeps the logical route when you switch language, including old-path redirects in the Astro config. Identity and skills stay in a typed `site` module, not in Markdown. Live at miguelfervi.dev.
Home has a portrait, selected work, and skills. Selected work is not a dump of featured flags: at most one company card first, then personal apps, capped at three. The projects index puts personal apps first — Resume Builder, Finanzas, Granada Joven, the pace calculator — then one card per company with public brand images. The resume page composes the experience and education collections with that static skill list. The Read page body is frontmatter paragraphs, not a Markdown AST.
Theme is applied in the head before paint (localStorage or system), then a preferences module re-applies it on Astro View Transitions, resyncs bilingual toggle labels, and restores scroll when you hop `/` ↔ `/es`. SEO is one `pageSeo()` helper: canonical, hreflang (x-default English), Open Graph, Twitter, and a localized Person JSON-LD. Sitemap comes from `@astrojs/sitemap`. Analytics are Vercel Analytics plus Speed Insights. CI is a single `verify` job: eight Vitest suites on the lib, `astro check`, a production build, then a smoke pass that parses the HTML in `dist/` for lang, language toggles, demo links, and redirects.
- Astro
- TypeScript
- Tailwind CSS
- Vitest