Skip to content
Rezha Julio
Go back

Redesigning my blog, part 1: the audit

4 min read

This blog runs on AstroPaper v5 with Astro 5 and Tailwind v4. It looked fine. It worked. But it also looked like every other AstroPaper blog on the internet, and that bugged me.

I wanted to redesign it. New layout, new typography, new everything. But before touching any CSS, I figured I should audit the codebase first. No point making something pretty if the foundation has cracks.

So I spent a weekend going through the source, running Lighthouse, poking at edge cases. I found more issues than I expected.

Security

The share links component had rel="noreferrer" on external links but was missing noopener. Modern browsers handle this fine, but older ones (looking at you, Safari on iOS 12) can still be exploited via window.opener. Easy fix:

<a href={shareUrl} target="_blank" rel="noopener noreferrer">

The search page had a worse problem. It was reading the query parameter straight from the URL and injecting it into the page without sanitization. Classic XSS vector. If someone crafted a URL with a script tag in the query string and shared it, the browser would execute it.

Then there were the fonts. Google Fonts were loading from Google’s CDN, which means every visitor’s IP gets sent to Google. Not ideal for privacy. I switched to self-hosting via @fontsource packages. Fonts load from my own domain now, no external requests.

SEO

This section had the most problems.

First: trailing slashes. Some URLs ended with /, some didn’t. Google sees /posts/hello and /posts/hello/ as two different pages. I made sure internal links were consistent with trailing slashes, and considered adding trailingSlash: "always" to the Astro config but held off since Cloudflare Pages handles redirects automatically.

The canonical URLs were also messy. If someone shared a link with ?utm_source=twitter or whatever, that query string would end up in the <link rel="canonical"> and og:url tags. Two different URLs pointing to the same content. I wrote a small normalizeCanonical() helper in Layout.astro that strips query params and enforces the trailing slash:

function normalizeCanonical(url: string): string {
const u = new URL(url);
u.search = "";
if (!u.pathname.endsWith("/")) {
u.pathname += "/";
}
return u.toString();
}

The theme toggle script was another headache. It was loaded via a relative path, which meant it worked on the homepage but 404’d on nested routes like /posts/some-post/. The fix was bundling it as a regular Astro import:

---
import "@/scripts/theme.ts";
---

Bundling through Astro’s import system gives you a path that works from any route, regardless of nesting depth.

I also added theme-color meta tags so mobile browsers show the correct color in the address bar for both light and dark mode:

<meta name="theme-color" content="#fff" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#1a1a2e" media="(prefers-color-scheme: dark)" />

And an og:type meta tag was missing entirely. Small thing, but social media crawlers use it to decide how to render your link preview.

Accessibility

The theme toggle button had aria-label="auto" which is meaningless to a screen reader. “Auto” what? I changed it to describe the actual action, like “Switch to dark mode” or “Switch to light mode” depending on the current state.

View transitions were eating focus state. When you navigate between pages, Astro’s view transitions swap the DOM, but they weren’t moving focus to the new content. A keyboard user would lose their place after every page change. I added a focus management handler in the astro:after-swap event.

The SVG icons in social and share link components were purely decorative but didn’t have aria-hidden="true". Screen readers were trying to announce them, reading out gibberish path data. Quick fix across all the icon components:

<svg aria-hidden="true" ...>

What I didn’t touch

I left the visual design completely alone for now. The colors, layout, typography, spacing: all stock AstroPaper. This post is about the stuff underneath.

What’s next

This is part 1 of a series about redesigning this blog. The boring stuff is done. Now the fun starts.


Related Posts


Next Post
Why I Left Zola's Simplicity for Astro's Power