This site has just been rebuilt from scratch. The previous version was a React app that fetched projects and posts from APIs in the browser; it worked, but the content only existed after JavaScript ran. For a portfolio and a blog that’s backwards: the content is the product, and it should arrive ready.
The new version is a static site generated with Hugo and deployed on Cloudflare Pages. Here’s a summary of the decisions.
Why Hugo
- It’s a single binary. No
node_modules, no bundler step. The whole site builds in under a second. - Content is Markdown. Each post and each project is a folder with an
index.mdand its images next to it. - Built-in image processing. Project screenshots go into the repository as PNG and come out as WebP, in several sizes, with
srcset. - Truly multilingual. Portuguese and English share the same templates, and Hugo generates
hreflangtags and one sitemap per language.
Custom theme, no dependencies
Instead of adopting an existing theme, I wrote the templates and a single CSS file. The result weighs a few kilobytes and loads no JavaScript at all.
Light and dark mode follow the system preference using only CSS:
:root {
color-scheme: light dark;
--bg: #f5f2ec;
--text: #1a1814;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #121110;
--text: #ece7dd;
}
}
No toggle, no localStorage and no flash of the wrong theme on load.
SEO from the first commit
A few things every page ships with:
- unique
title,descriptionand canonical URL; - Open Graph and Twitter Cards, with a share image generated at build time for each page;
- JSON-LD structured data (
Person,WebSite,BlogPosting,BreadcrumbList); sitemap.xml,robots.txtand an RSS feed;- self-hosted fonts with
preloadandfont-display: swap.
Deploy
The GitHub repository is connected to Cloudflare Pages: every git push to main triggers a new deploy, and every pull request gets a preview URL. The build configuration in the dashboard comes down to three fields:
Build command: hugo --gc --minify
Output directory: public
HUGO_VERSION: 0.167.0
Pinning the version with HUGO_VERSION matters: without it, Pages uses the build image’s default version, which tends to lag well behind the current one.
What’s next
The plan is to use this space to write down what I learn day to day with React Native, Expo, Cloudflare Workers and product development. If you want to follow along, subscribe to the RSS feed.