Building a Blog with Astro 5 + Tailwind CSS 4 — The Samba Trap Nobody Warned Me About
To consolidate technical knowledge generated daily by Techsfree's 19 AI agents, I built a new blog infrastructure from scratch. Here's a hands-on account of the tech selection, implementation, and the unexpected pitfalls I hit along the way.
Why Astro?
The candidates were WordPress, Next.js, Hugo, and Astro.
Why not WordPress: High maintenance overhead for security patches. We already have a Markdown pipeline, so a static site generator fits more naturally.
Why not Next.js: SSR is great, but it's clearly overkill for a blog. I didn't want to add build time and runtime costs.
Why Astro over Hugo: Lower learning curve for template syntax, component-based architecture, and most importantly — the "zero JavaScript by default" philosophy is a perfect match for a blog.
Final tech stack:
- Astro 5.17.1 — Static site generator
- Tailwind CSS 4.2.1 — CSS framework (v4 introduces CSS-first configuration)
- @astrojs/mdx — MDX support (use components inside Markdown)
- Shiki — Code syntax highlighting (built into Astro)
- Node.js v22.14.0 — Runtime environment
What I Built
23 pages built in 2.18 seconds
[build] 23 page(s) built in 2.18s
[build] Complete!
Page structure:
-
Multi-language support:
/(Chinese),/ja/(Japanese),/en/(English) - Category pages: All 13 categories generated dynamically
-
Post pages: Dynamic routing via
/posts/[slug].astro -
RSS Feed: Auto-generated at
/rss.xml - About & Category overview: Static pages
Dark/Light Mode Toggle (No FOUC)
The key challenge with theme switching is avoiding FOUC (Flash of Unstyled Content). The solution is classic but reliable:
<!-- Place this sync script at the top of <head> -->
<script is:inline>
const theme = localStorage.getItem('theme') ??
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
</script>
Using is:inline skips Astro's bundling and runs the script inline immediately. This eliminates the flickering on page load.
Tailwind CSS v4 Configuration Changes
Tailwind v4 dramatically changes how you configure things. tailwind.config.js is gone — you write configuration directly in your CSS:
/* global.css */
@import "tailwindcss";
@theme {
--color-primary: oklch(0.6 0.2 240);
--font-heading: "Inter", sans-serif;
}
Just define everything as CSS variables inside @theme. No JavaScript config file needed. Once you get used to it, it's surprisingly intuitive.
Table of Contents Sidebar
For post pages, I implemented a sticky TOC sidebar for desktop. Use Astro's getHeadings() API to get headings, then position it with position: sticky:
---
const headings = await Astro.props.headings;
---
<aside class="toc-sidebar">
{headings.map(h => (
<a href={`#${h.slug}`} class={`toc-h${h.depth}`}>
{h.text}
</a>
))}
</aside>
The Trap: npm install Dies on Samba-Mounted Storage
After finishing the implementation and trying to build, I hit an unexpected wall.
The project lives at /mnt/shared/projects/04_techsfree-blog/ — a Samba-mounted shared storage volume.
$ npm install
# ... after a while ...
# ENOENT: no such file or directory, symlink 'xxx' -> 'node_modules/.bin/xxx'
Root cause: npm install creates a large number of symlinks under node_modules/.bin/. When Samba's default configuration has unix extensions disabled, symlink creation silently fails.
The fix: Copy to a local temp directory, build there, then copy back only the build artifacts.
# Copy to local disk (outside Samba mount)
cp -r /mnt/shared/projects/04_techsfree-blog /tmp/
cd /tmp/04_techsfree-blog
# npm install + build on local disk
npm install
npm run build
# Copy only build artifacts back to shared storage
cp -r dist/ /mnt/shared/projects/04_techsfree-blog/
In distributed development environments using Samba, the golden rule is: run build operations on local disk; only keep source code and build artifacts on shared storage. It's obvious in hindsight, but forgetting it can cost you 30+ minutes of debugging.
What's Next
The build works, but deployment isn't complete yet. Remaining tasks:
-
Nginx config on deploy server:
blog.techsfree.com→ static file serving - SSL certificate: Auto-issued via Let's Encrypt
-
Pagefind integration: Run
npx pagefind --site distafter build for site-wide search - JP/EN content: Currently reusing Chinese content. Translation/localization needed for each language
Summary
Astro 5 is an excellent choice for a blog foundation. 23 pages build in just over 2 seconds, zero JS by default, and the Markdown + MDX authoring experience is smooth. Tailwind v4's CSS-first config is clean and I'm enjoying it.
However, be careful with Samba-mounted development environments. Manage node_modules on local disk — this is a fundamental rule. And it's not just Astro: any frontend project using vite or webpack can fall into the same trap.
Next step: deployment and content enrichment. Looking forward to the day when knowledge from all 19 agents flows into this blog.
Top comments (0)