Portfolio Website
Bilingual portfolio site built with Next.js 16, a file-based Markdown content pipeline, and type-safe architecture for showcasing data science and full-stack projects.
Professional portfolio website with English/Spanish support, Markdown-driven project pages, dark/light theming, and interactive project filtering. Built with Next.js 16, React 19, TypeScript, and Tailwind CSS 4, deployed on Vercel with static generation for performance.

Gallery
Project Overview
This is the site you’re looking at. It’s a bilingual portfolio that presents my work in data science, machine learning, and full-stack development. The technical goal was straightforward: build a site where adding a new project means writing an MDX file, not touching application code.
Content Architecture
MDX-Based Project Pages
Each project lives as a pair of .mdx files (one per language) in the content/projects/ directory. The frontmatter is validated against a Zod schema at build time, so type errors and missing fields are caught before deployment, not in production.
The body is plain Markdown: headings, lists, tables and code blocks. It is parsed at build time and rendered to HTML, so a project page is as easy to write as a blog post while the frontmatter above it stays a typed data model.
Automatic Route Generation
Next.js App Router generates project pages dynamically from the content directory. Adding a new project creates its route, adds it to the project listing, and makes it available in both languages, all from the file system.
Internationalization
The site uses next-intl for localization with /en and /es route prefixes. A single component tree serves both languages. Translations live in JSON message files, and content-level translation happens through the paired MDX files. SEO metadata (titles, descriptions, Open Graph tags) is generated per-locale with proper hreflang attributes.
Technical Stack
Frontend
- Next.js 16 with App Router and Static Site Generation
- React 19 with concurrent features and Suspense boundaries
- TypeScript 5 across the entire codebase
- Tailwind CSS 4 with a custom design system (spacing, colors, typography)
Interactive Layer
- Framer Motion for page transitions, scroll animations, and hover interactions
- Radix UI for accessible primitives (theme toggle, navigation menu, dialog)
- Dark/light theming with system preference detection and manual override
Content Pipeline
- Markdown parsed with
markdown-it, not an MDX compiler: the content is prose, so it has no need for JSX - Gray Matter for frontmatter extraction
- Zod schemas for build-time validation of project metadata
- File-based routing for zero-configuration page generation
Deployment
The site runs on Vercel with automatic deployments from the GitHub repository. Static Site Generation means pages are pre-rendered at build time, resulting in fast load times and strong SEO without a runtime server.
Design Decisions
- No CMS: MDX files in the repo are the content source. This keeps the stack simple and version-controlled.
- Radix UI over custom components: accessibility is built-in rather than bolted on.
- Tailwind over CSS Modules: utility-first approach keeps styling colocated and consistent.
- SSG over SSR: portfolio content changes infrequently, so static generation is the right tradeoff.
The Challenge
- Building a content system where adding a new project requires only an MDX file, not code changes
- Supporting two languages without duplicating layout logic or component code
- Balancing animation richness with fast load times across devices
- Maintaining accessibility standards alongside custom interactive components
The Approach
- Markdown pipeline with Zod-validated frontmatter for type-safe, file-based content management
- next-intl with locale-based routing, keeping a single component tree for both languages
- Framer Motion animations with Intersection Observer-based activation and code splitting
- Radix UI primitives for accessible interactive components (dialogs, dropdowns, toggles)
Results
- 95+ Lighthouse scores across performance, accessibility, SEO, and best practices
- Adding a new project takes one MDX file per language, no deployment configuration needed
- Seamless English/Spanish switching with localized content, metadata, and URLs
- Responsive design tested across mobile, tablet, and desktop breakpoints
Impact
To publish a project I write two text files, one per language, and push. The page, its entry in the list and both language versions build themselves. If I leave a field out, the site refuses to build, so I find out before publishing instead of after.
Project Details
Objective
Create a maintainable, bilingual portfolio that makes it easy to add new projects and presents technical work clearly across devices and languages.
Theme
Modern, clean design with smooth animations and professional presentation.
Date
January 8, 2026