portfolio-workspace.mdx
Portfolio Workspace
A Next.js portfolio presented as an editor-inspired workspace with MDX-driven project entries.
Project links

Overview
This portfolio is itself a project: a content-driven Next.js site that presents software work through an original editor-inspired interface. The shell borrows useful workspace ideas such as explorer navigation, tabs, breadcrumbs, a command palette, and a status bar, while the pages remain semantic and readable.
The problem
The goal was to build a portfolio that looks memorable to developers without hiding the actual case studies. A literal code-editor clone would be confusing, and a static set of cards would become annoying to maintain as projects grow.
Architecture
Projects live as MDX files with structured frontmatter. The same metadata feeds the explorer, archive, search, filters, command palette, project pages, sitemap, and previous/next navigation.
| Area | Decision |
|---|---|
| Framework | Next.js App Router |
| Content | Local MDX files with frontmatter |
| Styling | Tailwind CSS and a custom editor-inspired palette |
| Localization | English routes under /en and Portuguese routes under /pt-BR, with / redirecting to /pt-BR |
| Data source | Filesystem content, no database |
Technical decisions
The implementation keeps Server Components as the default and uses Client Components only for interactions such as the command palette, mobile explorer drawer, filters, and language toggle. Project search uses lightweight metadata instead of loading every MDX body into the browser.
Modular deployment layer
The portfolio eventually became the public front door for several independent projects, so the deployment model had to avoid turning every update into a full-stack restart. The final shape separates public application repositories from a private deployment control plane:
| Layer | Responsibility |
|---|---|
| Project repositories | Own application code, Dockerfiles, and GitHub Actions workflows |
| GitHub Container Registry | Stores one image per project |
| Private deploy directory | Owns Compose overlays, Caddy routes, runtime env files, and volumes |
| Oracle VPS | Runs Caddy plus project containers on an internal Docker network |
Each project is represented by two small private deployment files: a Compose overlay for the service and a Caddy route for the subdomain. The portfolio stays at the root domain, while projects use subdomains such as futemon.<domain> and livetrivia.<domain>.
The GitHub Actions workflows publish project images to GHCR and then SSH into the VPS to run a targeted Compose update:
docker compose -f compose.yml -f projects/<service>.compose.yml pull <service>
docker compose -f compose.yml -f projects/<service>.compose.yml up -d --no-deps <service>
--no-deps keeps unrelated services from being recreated. Runtime secrets stay on the VPS in private env files, so the public repositories can remain reusable without carrying deployment credentials or environment-specific state.
Production lessons
The deployment work surfaced the kind of issues that rarely appear in a local build: DNS delegation delays, Caddy certificate validation, Docker socket permissions from GitHub Actions SSH sessions, private GHCR access, and ARM64 image compatibility on Oracle's Ampere instances.
Those problems shaped the final setup. Workflows now build ARM64 images on an ARM64 GitHub-hosted runner, Caddy is containerized with the rest of the stack, and the deployment docs separate one-time VPS setup from per-project redeploys.
Current state
The site supports English and Brazilian Portuguese routes, automatic first-visit language selection from browser language preferences, MDX case studies, SEO metadata, responsive navigation, Docker image publishing, and modular VPS deployment for sibling projects.
Lessons learned
The editor metaphor works best when it improves navigation rather than becoming decoration. The surrounding shell can feel technical and familiar while the main content remains normal, accessible web content.
The deployment layer followed the same principle: use familiar tools in a way that reduces coordination. A new project should need a Dockerfile, one Compose overlay, one Caddy route, and one workflow, not a redesign of the whole portfolio.
Screenshots


