---
title: "How I revamped this site using Astro, Tailwind and Cloudflare"
date: 2026-08-19
lang: en
translationKey: how-i-rebuilt-this-site
tags: [Astro, Tailwind CSS, Cloudflare Pages]
description: "I revamped my site: Astro for static HTML, Tailwind for styling, Cloudflare Pages for hosting. The tools and the reasoning behind them."
---

If you read my post on [why I had picked Grav CMS](/blog/perche-ho-scelto-grav-cms/) (in Italian), you already know how it ended: at some point I switched to [Astro](https://astro.build/). Truth be told, I had already moved to Astro before writing this article, but the layout was bare — let's be honest: it worked, but it was **plain ugly**. So I decided to start almost from scratch and rebuild everything, with a hand from **Claude**, this time with a sharper design idea and a few more tools to back it up.

This article is a tour of the **tools and technologies** I used, with the _why_ behind each choice. No step-by-step tutorial: more of an overview for anyone curious about what's under the hood.

## Astro: static HTML, (almost) zero JavaScript

The heart of it all is [Astro](https://astro.build/), a framework for building static sites. The idea that won me over is simple: by _default_ Astro ships **plain HTML**, without sending the browser any JavaScript it doesn't need. You add interactive code only where it's actually required; what's left is lightweight HTML that loads in a blink.

For a site like this one — mostly made of **content you read** — it's the ideal choice: pages that weigh next to nothing, great response times and solid SEO behaviour, without any hand-tuning.

One detail I appreciate: Astro doesn't force me into a single way of writing components and lets me use the syntax I prefer, while staying _framework-agnostic_. In short: little ceremony, plenty of control.

## Tailwind for styling

For the visual side I use [Tailwind CSS](https://tailwindcss.com/). Instead of writing separate stylesheets and inventing class names, I compose styles right in the _markup_ using ready-made classes (`flex`, `text-lg`, `mt-4`, and so on).

Why does it click for me? Because the styling lives **next to** the element I'm building: no jumping back and forth between files, and no "dead" CSS that nobody dares touch anymore. At _build_ time Tailwind keeps only the classes actually in use, so the final CSS is tiny. **Light/dark mode** is handled the same way, with a handful of dedicated classes.

## Cloudflare Pages for hosting

The site is hosted on [Cloudflare Pages](https://pages.cloudflare.com/). It works exactly how I'd expect a modern platform to: I `git push`, Cloudflare builds the project and publishes the result automatically, served from their global network. No servers to manage, HTTPS included, **zero-downtime** deploys in seconds.

There's one aspect worth calling out, though: since this is a **static** site, in theory there's no "server-side code". But I still wanted a bit of dynamic behaviour, and this is where **Pages Functions** come in — small _serverless_ functions running at the _edge_ of Cloudflare's network. I use them for two things:

- the **"Now Playing"** widget on the [About page](/en/about/), which asks Spotify what I'm listening to right now (or the last track I played);
- a _gate_ for **staging**: when needed, I can protect the staging version with authentication, so I can preview it before going to production, all without touching the public version.

Basically the best of both worlds: static and blazing fast by default, with a sprinkle of dynamism where it matters.

## Content: Markdown + frontmatter

Luckily I was able to "recycle" every article published up to that point (including the ones I had written on the first version, the one based on GravCMS). The idea of steering clear of databases, CMSs, template engines, frameworks and external platforms turned out to be a winning one.

Every article is a plain [Markdown](https://en.wikipedia.org/wiki/Markdown) file. At the top of the file, a **frontmatter** block describes the metadata: title, date, language, tags, description and little else. Convenient, standard, easy to move around: if I ever switched technology, the content would come with me — no painful migrations.

Astro lends an extra hand with **Content Collections**: a _schema_ (validated with [Zod](https://zod.dev/)) checks that every article has the right fields, in the right format. If I forget a required field or blow past the description's character limit, I find out **at build time**, not after publishing. It's a small safety net that brings peace of mind.

## Bilingual IT/EN

I wanted to publish in both Italian and English, without duplicating pages and components for every article. The solution: Italian lives at the root (`/`, `/blog/`), English under `/en/`, but both languages draw from the **same** content, filtered by language.

The two versions of an article are separate files sharing a **translation key**: that's the glue that lets me show the _"also available in…"_ link and jump from one language to the other. An article can also exist in a single language: no obligation to translate everything.

## What's better than before

The previous version was functional, but bare. Rebuilding it, I added quite a few things that were missing before — and this is where much of the work (and the fun) lives:

- **Look and typography.** A coherent design (the **Atlas** theme) and careful typography: [Hanken Grotesk](https://fonts.google.com/specimen/Hanken+Grotesk) for body text, [JetBrains Mono](https://www.jetbrains.com/lp/mono/) for code. Before, everything was very bare-bones (not to say plain ugly).
- **Light/dark theme.** A light/dark toggle, with the right theme applied _before_ the page paints: no classic white flash when opening the site in dark mode.
- **Tags on articles.** Every article has its own tags, with dedicated pages to browse content by topic.
- **Reading time.** Each article shows an estimated reading time in minutes.
- **Article outline.** A small side _table of contents_ with the section headings, to get your bearings and jump straight to the part you care about.
- **Latest posts on the homepage.** The home page shows the most recent blog posts, so there's always an entry point into the content.
- **RSS feeds.** Links to the feeds (one for Italian, one for English) for anyone who prefers following the blog from an RSS reader.
- **Nice-looking code snippets.** Code blocks coloured at build time (no JavaScript to load), with two coordinated light/dark themes, **line numbers** where they help and a _"copy"_ button on every block.
- **"View in Markdown" button.** Every article can also be opened in its raw Markdown form, for those who prefer it.
- **Contact form on the homepage.** A form to write to me straight from the home page, sent through an external service ([Web3Forms](https://web3forms.com/)) with a small anti-spam _honeypot_. No _backend_ to maintain just to receive a handful of emails.
- **"Uses" page.** A [dedicated page](/en/uses/) for the hardware, software and tools I use every day.
- **Automatic social images and no "jumps".** Every article gets its own 1200×630 preview **generated automatically** at build time, and images don't cause _layout shift_ while they load.

## Wrapping up

As I said in the Grav article, I don't believe there's one tool that's flat-out better than all the others: it **depends** on your needs (and on what you already know). For a personal site made mostly of content, this stack — **Astro** for static HTML, **Tailwind** for styling, **Cloudflare Pages** for hosting — gave me exactly what I was after: light, fast pages, Markdown content that stays mine, a touch of dynamism where needed and, at last, a **layout I'm happy with**.

If you're thinking of rebuilding your site or starting one, I'd suggest taking a look at Astro: the learning curve is gentle and, once you find your rhythm, it's genuinely a pleasure to use.
