Context / Problem

After years building software for public administration systems, internal tools, and freelance clients, I had accumulated a significant body of knowledge — documented decisions, hard-won lessons, architectural trade-offs — scattered across private notes, commit histories, and conversations. None of it was shareable.

I needed a single place to do three things: publish a technical blog where I could document experiences as they happened, present a curated portfolio of real projects with honest architectural context, and maintain a living résumé that I could point to in any recruitment process without manually updating five different platforms. The last point mattered especially because I was targeting opportunities in European markets, where a clear online presence often matters more than a PDF attachment.

The project is self-referential by design — this case study lives inside the portfolio it describes.

Constraints

The stack had to support three languages (Portuguese, English, Spanish) from day one, given the European job market target. Content needed to be easy to write and update quickly — the moment publishing becomes a chore, it stops happening. Hosting had to be fast globally without managing infrastructure. And critically: I wanted the freedom to drop in any UI framework in the future without rewriting the whole thing.

One deliberate constraint I placed on myself: write the code manually and stay close to Astro. AI-assisted development is part of my daily workflow on other projects, but for this one I wanted to understand every line — using AI only for content corrections, not code generation. A portfolio that I can’t fully explain is a liability in an interview.

Architecture Decisions

Astro over Nuxt: I had a previous Nuxt 2 → 3 migration experience that left scars. Even knowing that kind of breaking change is unlikely to repeat, Nuxt felt like a risk I didn’t want to carry into a personal project. Astro’s island architecture, framework-agnostic component model, and exceptional content-authoring story made it the clear alternative — and one that would let me adopt Vue, React, or anything else later without paying a migration tax.

MDX for content: Blog posts and project case studies are written in MDX, colocated with the project source. No CMS, no API, no external dependency for content. This keeps the writing workflow as close to a text editor as possible, which was non-negotiable for sustainable publishing cadence.

SSR on Vercel: Chose server-side rendering primarily for SEO and Core Web Vitals scores. Static generation alone would have worked for most pages, but SSR gives flexibility for future dynamic features — newsletter integration, course platform — without a rearchitecture.

i18n for UI, not content: The internationalization library covers the site shell: homepage, résumé, navigation, and all UI strings in three languages. Blog posts and project pages are authored in the primary language and surfaced to other-language visitors with a soft callout suggesting Google Translate. This was a deliberate trade-off — full content translation would have slowed publishing to a crawl.

Figma design system first: Before writing a single line of code, the visual language was designed in Figma — color tokens, typography scale, spacing, and component states. This upfront investment paid off during implementation: every CSS variable had a named counterpart in the design file, and visual decisions didn’t have to be made on the fly while coding.

Themed Bootstrap over Tailwind: Tailwind is the default choice in most modern stacks, but for this project Bootstrap’s theming system was a better fit. The goal was full ownership of the CSS — no utility class sprawl, no mental overhead of memorizing shorthand. Bootstrap’s Sass variables let me wire the Figma token system directly into the framework and produce a consistent, highly customized result. The output looks nothing like default Bootstrap.

View Transitions API: Astro’s built-in support for the View Transitions API makes page navigation feel native and fluid. This was a quality-of-life choice, but one that meaningfully changes how the site feels to browse.

Challenges

Internationalization is always underestimated, and working with static content in Astro made it more complex than expected. Routing, locale detection, and content collection queries all had to account for language context. The trickiest part was deciding exactly where the i18n boundary would sit — which content deserved full translation infrastructure and which didn’t — without ending up with an inconsistent experience.

Coming into Astro without prior experience was less of an obstacle than anticipated. The mental model is intuitive for anyone who has worked with a component-based JS framework, and the documentation is genuinely good. The learning curve was real but short.

Trade-offs

The most significant trade-off was accepting partial i18n. Blog posts and project case studies are only available in the language they were written. Visitors reading in English will see Portuguese content (or vice versa) with a gentle nudge toward machine translation. This was the right call — a portfolio that never publishes because translation is blocking every post is worse than one that publishes consistently in one language. Speed of iteration won over completeness of localization.

The upside of this constraint is that writing and publishing stay lightweight. A post can go from draft to live in the time it takes to open a Markdown file and push a commit.

Result

The portfolio is live at zwinglio.com and in active daily use — including as the host of this case study. The résumé section is the single source of truth for recruitment processes, eliminating the multi-platform update problem. The blog is the ongoing outlet for technical documentation that previously had nowhere to go.

Next planned additions: a newsletter integration and a course platform to produce more structured, visual versions of the accumulated knowledge. The foundation is stable enough that both can be layered on without structural changes.