Building cydia.space in a Day: A Modern Static Site Generation Story
Aug 9, 2025• 16 min read
web-devnextjsautomationblogging

You know that feeling when you have a weekend free and think "I'll just throw together a simple website"? That was me on August 9th, 2025. Fast forward 12 hours, and I'm staring at a fully-featured blogging platform with RSS feeds, automated image optimization, and a deployment pipeline that would make a DevOps engineer proud.
Was I supposed to build all of this? Absolutely not. Did I have fun doing it anyway? You bet.
Here's the honest story of how cydia.space went from "just a personal site" to a complete static site generator in one caffeinated day—including the mistakes, the "wait, that actually worked?" moments, and the three times I almost gave up.
The Foundation (Early Hours - AKA "How Hard Can This Be?")
13:28 - First Commit: Setting the Stage
I started with what every developer tells themselves: "I'll just update my bio and maybe add a simple blog." The plan was humble—a standard Next.js setup with a few pages.
Spoiler alert: The plan didn't survive contact with my curiosity.
But here's something I learned that morning that every junior developer should know: start with your content structure first. I see so many developers (myself included, in past projects) who spend hours configuring webpack, debating CSS frameworks, and optimizing build processes before they even know what they're building.
Don't do that. Write your first blog post in markdown, sketch out your navigation, define what success looks like. Then build the tools to support it.
13:37 - Performance Optimization Wave (Because Why Not?)
Seven minutes later, and I'm already down a rabbit hole. Dynamic imports, bundle analysis, prefetching logic—my "simple site" was already getting complicated.
Here's the thing though: this wasn't feature creep, it was preparation. The @next/bundle-analyzer
and browserslist configuration I added here saved me hours later when the site grew. As a junior developer, you might think "I don't need to worry about performance yet," but trust me—it's easier to build it right from the start than to optimize later.
Real talk: I probably spent 20 minutes just staring at bundle analysis graphs because they looked cool. Sometimes the best learning happens when you're just exploring.
The Creative Sprint (Midday Momentum - Things Get Interesting)
13:49 - Image Pipeline Revolution (My First "Wait, I Can Automate That?" Moment)
This is where I had my first big realization of the day. I was manually resizing and converting images, and it hit me: I'm a programmer. Why am I doing this by hand?
Enter optimize-images.mjs
—my custom script that automatically converts images to WebP and AVIF formats. But here's what the initial version looked like:
// My first terrible attempt
for (const file of imageFiles) {
await sharp(file).webp().toFile(file.replace('.jpg', '.webp'));
}
This worked, but it processed EVERY image EVERY time. On a site with 50+ images, that's painfully slow. The breakthrough came when I added timestamp checking:
// Much better - only process changed files
const shouldProcess = !fs.existsSync(outputPath) ||
fs.statSync(inputPath).mtime > fs.statSync(outputPath).mtime;
Junior dev tip: Always optimize for the 10th time you run your script, not just the first. Future you will thank present you.
14:13 - Blog System Architecture (The Decision That Changed Everything)
Here's where I faced my first real architectural choice: CMS or static files?
I spent 30 minutes researching headless CMSs, setting up Contentful accounts, reading Strapi docs. Then I asked myself: "What problem am I actually solving?"
I wanted to write in markdown, track changes in git, and deploy statically. Why was I adding a database and API to that simple workflow?
So I built this instead:
---
title: "My Post Title"
date: 2025-08-09
keywords: ["nextjs", "blog"]
draft: false
---
My post content here...
The system auto-generates:
- Tag index pages
- Reading time estimates (≈220 words/minute if you're curious)
- Link normalization for accessibility
- Automatic post scheduling
Real lesson: Sometimes the best solution is the simplest one. Don't add complexity just because you can.
14:25 - Interactive Magic (Where I Definitely Went Overboard)
You know that voice in your head that says "this site needs particle effects"? I listened to it.
The AnimatedBackground
component was pure scope creep, but it taught me so much about:
- Canvas performance optimization
- Respecting
prefers-reduced-motion
(accessibility matters!) - Creating smooth animations that don't tank older devices
// The key insight - respect user preferences
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (prefersReducedMotion) return null;
Was this necessary? No. Was it fun to build? Absolutely. Sometimes the "unnecessary" features teach you the most.
The Features Explosion (Afternoon - When Simple Plans Go Sideways)
14:33 - Analytics Integration (My First "Production Mindset" Moment)
This is where my weekend project suddenly felt real. Adding Microsoft Clarity wasn't just about tracking visitors—it was about understanding how people actually use the site.
But here's what I learned about third-party integrations: always have a fallback plan. My first implementation looked like this:
// Bad - blocks the entire page if Clarity is down
await loadClarityScript();
renderMyApp();
The better version:
// Good - site works even if analytics fail
try {
loadClarityScript();
} catch (error) {
console.warn('Analytics failed to load, continuing anyway');
}
renderMyApp();
Junior dev lesson: Never let external services break your core functionality. Users don't care if your analytics fail, but they definitely care if your site doesn't load.
15:06 - Content Strategy (The Reality Check)
I wrote my first real blog post and immediately realized: my build system was broken.
The post was marked as draft: true
, but it was still showing up in RSS feeds and sitemaps. Rookie mistake! This led to an hour of debugging and learning how Next.js static generation actually works.
The fix:
// Filter drafts everywhere, not just the UI
const publishedPosts = posts.filter(post => !post.frontMatter.draft);
Hard truth: You'll discover your edge cases when you actually use your system. Build something, use it, then fix what breaks.
15:22 - Social Features (The "Why Is URL Encoding So Hard?" Hour)
ShareBar component time! Sounds simple, right?
Wrong. Social sharing URLs have the most inconsistent encoding requirements I've ever seen. Twitter wants one format, LinkedIn wants another, and don't even get me started on URL-encoding spaces vs. plus signs.
After way too much trial and error:
const encodedTitle = encodeURIComponent(title);
const encodedSummary = encodeURIComponent(summary);
const encodedUrl = encodeURIComponent(fullUrl);
Pro tip: Test your social sharing links manually. Nothing's more embarrassing than sharing a broken link to your own blog post.
The Publishing Pipeline (Late Afternoon - When Everything Clicks)
22:27 - The Build System Awakens (The Moment I Realized I'd Built a CMS)
By evening, I was staring at my terminal, watching scripts run, and it hit me: I hadn't just built a website—I'd built a complete publishing platform.
But let me tell you about the journey to get here. My first build script was a mess:
# My terrible first attempt
node generate-rss.js
node generate-sitemap.js
node optimize-images.js
next build
Half the time, scripts would fail silently. The other half, they'd run in the wrong order. I learned the hard way that order matters in build pipelines:
{
"build": "npm run generate:og && npm run generate:post-og && npm run optimize:images && npm run generate:rss && npm run generate:jsonfeed && npm run generate:sitemap && npm run generate:search-index && next build"
}
Each &&
represents a lesson learned from a failed build.
The magic happened when I realized these weren't separate features—they were all part of one system. RSS feeds, sitemaps, OG images, search indexes—they all need the same post data, just formatted differently.
Architecture insight: If you're duplicating logic across scripts, you probably need a shared data layer.
22:33 - Article Scheduling (The Timezone Nightmare)
"How hard can timezone support be?" - Famous last words.
I wanted posts to go live at reasonable times in Australian Central Standard Time, regardless of where GitHub Actions runs the build. Sounds simple until you realize:
- JavaScript Date objects are evil
- Timezones are harder than you think
- "Morning" in Melbourne isn't "morning" in UTC
After two hours of debugging and three different date libraries, I landed on this:
import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
dayjs.extend(utc);
dayjs.extend(timezone);
const now = dayjs().tz('Australia/Adelaide');
const postDate = dayjs(post.date).tz('Australia/Adelaide');
const isPublished = postDate.isBefore(now);
Junior dev wisdom: Date/time is always more complex than you think. Use a good library (dayjs, date-fns) and test with multiple timezones.
22:45 - The Implementation Marathon (When Muscle Memory Kicks In)
Between 22:45 and 23:11, something magical happened. All the patterns I'd established earlier in the day clicked into place. Complex features that should have taken hours were done in minutes.
This is what people mean when they talk about "developer productivity." It's not about typing fast—it's about building consistent patterns you can reuse. The scheduling system used the same frontmatter parsing, the same file filtering, the same build pipeline hooks I'd already built.
The real lesson: Good architecture isn't about planning everything perfectly upfront. It's about recognizing patterns as they emerge and refactoring to support them.
The Mobile Revolution (Late Evening - The Accessibility Wake-Up Call)
22:51 - Mobile Navigation (When I Learned About Real Users)
I tested my beautiful site on mobile for the first time around 11 PM. It was... not great. Tiny text, broken layouts, navigation that required eagle-eye precision to tap.
Reality check: most people will visit your site on mobile. Build for them first, not last.
The hamburger menu seemed straightforward until I started thinking about accessibility:
- What about screen readers?
- Keyboard navigation?
- Focus management when the menu opens/closes?
- What if JavaScript fails to load?
My final solution ended up being CSS-first with JavaScript enhancement:
/* Works without JavaScript */
.menu-toggle:checked ~ .mobile-menu {
transform: translateX(0);
}
/* JavaScript adds smooth animations */
.menu-animated {
transition: transform 0.3s ease;
}
Accessibility tip: Test your site with a screen reader. It's humbling and educational. On Mac, press Cmd+F5 to enable VoiceOver and navigate your site with your eyes closed.
23:11 - Final Polish (The Last 5% Takes 50% of the Time)
Those final commits weren't just about mobile navigation—they were about all the tiny details that separate a weekend project from something you'd actually want to use:
- Focus indicators that don't look terrible
- Proper heading hierarchy for screen readers
- Color contrast that passes WCAG guidelines
- Touch targets that are actually touchable (44px minimum!)
Here's what I wish I'd known earlier: accessibility isn't an add-on, it's good design. When you build for people with different abilities, you end up with a better experience for everyone.
The Technology Stack That Made It Possible
Core Framework:
- Next.js 14 with TypeScript for the foundation
- Static site generation with dynamic capabilities
- Tailwind CSS for rapid styling
Content Pipeline:
- Gray-matter for frontmatter parsing
- Remark/Rehype for markdown processing
- Prism for syntax highlighting
- Custom scripts for RSS, JSON feeds, and sitemaps
Performance Optimizations:
- Dynamic imports to reduce bundle size
- Image optimization pipeline (WebP/AVIF)
- Prefetching for smooth navigation
- Bundle analysis for continuous optimization
Deployment:
- Azure Static Web Apps for hosting
- Azure DNS for domain management
- GitHub Actions for CI/CD
- Automated builds with integrity verification
The Mistakes I Made (So You Don't Have To)
Let's be honest—this wasn't 12 hours of perfect code. Here are the mistakes that taught me the most:
1. The RSS Feed That Ate XML My first RSS generation script produced invalid XML because I forgot to escape HTML entities in post titles. One post with an ampersand broke the entire feed.
// Wrong - breaks RSS readers
<title>Building a Blog & Other Adventures</title>
// Right - properly escaped
<title>Building a Blog & Other Adventures</title>
Lesson: Always validate your generated feeds. RSS readers are pickier than browsers.
2. The Case of the Disappearing Images I spent 2 hours debugging why my optimized images weren't showing up. Turns out, I was generating WebP files but still referencing the original JPGs in my markup.
Lesson: Test your optimizations in production mode, not just development.
3. The Build Script That Lied My early build scripts would fail silently, showing "success" even when files weren't generated. I learned to add explicit checks:
if (!fs.existsSync(outputFile)) {
throw new Error(`Failed to generate ${outputFile}`);
}
Lesson: Make your build scripts loud when they fail. Silent failures are the worst kind.
4. The Mobile Nightmare I built the entire desktop experience before testing on mobile. Big mistake. Retrofitting responsive design is 10x harder than building mobile-first.
Lesson: Test on mobile early and often. Your users probably are.
5. The Timezone Confusion I tested scheduling with posts dated "tomorrow" and couldn't figure out why they weren't appearing. Turns out "tomorrow" in UTC isn't "tomorrow" in Australia.
Lesson: Always test edge cases with real data, not just happy path examples.
Why This Was Actually Possible in One Day (And How You Can Do It Too)
1. I Knew When to Stop (Scope Management for Humans) Every feature solved a real problem I was having right then. No gold-plating, no building for imaginary future requirements.
When my inner perfectionist whispered "you should add comments," I asked: "do I need comments today?" Answer: no. Comments went on the maybe-later list.
Junior dev tip: Write down feature ideas instead of implementing them immediately. Most "urgent" features aren't actually urgent.
2. I Automated the Boring Stuff Early Instead of manual processes that would slow me down later, I built scripts first. Even when it felt like "premature optimization."
Example: The image optimization script took 30 minutes to write but saved me 5 minutes every time I added an image. After image #6, it had paid for itself.
The math: If you'll do something more than 3 times, automate it.
3. I Built Progressive Enhancement Into Everything Core functionality works without JavaScript. Enhancements layer on top without breaking the foundation.
This isn't just good practice—it made debugging way easier. When something broke, I could always fall back to the base functionality and isolate the problem.
4. I Used Tools I Already Knew Next.js, TypeScript, Tailwind—I didn't try to learn a new framework while building a complex project.
Controversial opinion: The best tool is the one you already know well. Learn new tech on toy projects, not production ones.
5. I Committed Early and Often 30+ commits across 12 hours means I was saving progress every 20-30 minutes. When I broke something (and I did, multiple times), I could easily revert to the last working state.
# My most-used commands of the day
git add .
git commit -m "Working image optimization"
# ... break something ...
git checkout -- problematic-file.js
Git tip: Commit when something works, not when it's perfect.
The "Simple" Decisions That Made Everything Possible
What looks like rapid development was actually the result of a few key architectural decisions made early. Here's what really mattered:
Build-Time Generation (The Performance Secret) All dynamic content (feeds, sitemaps, OG images) generates at build time, not runtime. This means:
- The deployed site is completely static
- No database queries slowing down page loads
- No server costs (thanks, CDN!)
- Natural caching everywhere
The tradeoff: Build times are longer, but user experience is instant.
Unified Configuration (DRY for Humans) I have one source of truth for site metadata:
// config/site.js
export const siteConfig = {
title: "cydia.space",
description: "...",
url: "https://cydia.space"
};
This config flows through all generated assets. Change the site title once, and it updates in:
- RSS feeds
- OG images
- Sitemaps
- Social sharing links
- HTML meta tags
Time saved: About 10 minutes every time I want to update site info.
Graceful Degradation (The Accessibility Win) Every interactive feature has a non-JavaScript fallback:
- Mobile menu works with pure CSS
- Social sharing has mailto fallbacks
- Search works even if JS fails to load
This isn't just good practice—it made development faster. I could test the core functionality before adding the fancy interactions.
Type Safety (The Confidence Multiplier) TypeScript throughout means runtime errors get caught at build time. When you're moving fast and making lots of changes, this safety net is invaluable.
// This error gets caught before deployment
interface BlogPost {
title: string;
date: string;
slug: string;
}
const post: BlogPost = {
title: "My Post",
date: "2025-08-09",
// Missing 'slug' - TypeScript catches this
};
The real benefit: I could refactor aggressively without fear of breaking things.
Your Next Day-Long Build: A Practical Guide
Want to pull off your own "build it in a day" project? Here's what actually matters:
Start with Content Structure (Not Code) Write your first blog post in a text file. Sketch your navigation on paper. Define what "done" looks like.
# My first post
title: Getting Started
tags: [learning, beginners]
draft: false
This is my first post...
Don't touch code until you know what content you're supporting.
Automate Early, Not Late If you'll do something more than twice, script it:
#!/bin/bash
# deploy.sh - saved me 20 minutes per deployment
npm run build
npm run test
git push
Junior dev mindset: "This manual process is fine for now." Senior dev mindset: "I'll automate this before I forget how it works."
Think in Systems, Not Features Look for patterns across your features:
- Blog posts and project pages both need frontmatter parsing
- RSS feeds and sitemaps both need post filtering
- Social sharing and email links both need URL encoding
Build the shared system once, then extend it for each use case.
Performance as a Feature (Not an Afterthought) Install bundle analyzer on day one:
npm install --save-dev @next/bundle-analyzer
Set your performance budget early:
- First Load JS < 100KB
- Images under 500KB
- Build time under 60 seconds
Scale from Day One (Even for Personal Projects) Build patterns that grow with you:
- Static generation (scales to millions of pages)
- CDN-friendly architecture (global performance)
- Automated builds (no manual deployment steps)
- Environment variables for configuration
Your future self will thank you when your weekend project becomes your main thing.
The Numbers (What Actually Got Built)
By the end of the day, I had:
- 30+ commits across 12 hours (that's one every ~20 minutes)
- A complete blog system with markdown processing and automatic tagging
- Automated RSS and JSON feed generation (because readers matter)
- Mobile-responsive design with accessibility features built in
- Image optimization pipeline (WebP + AVIF support)
- Search functionality that works offline
- Social sharing integration for 5 platforms
- Article scheduling with timezone support
- OG image generation for every post
- Complete Azure deployment pipeline with GitHub Actions
The real metrics:
- Development time: ~12 hours of focused work (with coffee breaks)
- Lines of code: ~2,500 (measured, not estimated)
- External dependencies: Just 13 production packages (minimal bloat)
- Page load time: Under 2 seconds on 3G
- Lighthouse score: 100/100/100/100 (Performance/Accessibility/Best Practices/SEO)
Resources for Junior Developers
Essential Tools I Used:
- Next.js Documentation - Start here for React SSG
- Tailwind CSS - Utility-first CSS framework
- Gray Matter - Frontmatter parsing
- Sharp - Image optimization
- Day.js - Date handling that doesn't make you cry
Learning Resources:
- Web.dev - Performance and accessibility best practices
- MDN Web Docs - When you need to understand how web standards actually work
- GitHub Actions Documentation - CI/CD that doesn't require a DevOps degree
Testing Tools:
- Lighthouse - Performance auditing
- WebAIM WAVE - Accessibility testing
- RSS Validator - Because broken feeds are embarrassing
What's Next? (The Roadmap I'm Actually Excited About)
The beauty of building a solid foundation is that future features become easier, not harder. The patterns I established—automated builds, consistent metadata handling, performance-first architecture—mean adding new capabilities is fun instead of stressful.
Coming soon (when I have another free weekend):
- Newsletter integration with ConvertKit
- Comment system (probably using GitHub Discussions)
- Analytics dashboard to see which posts people actually read
- Content recommendations based on tags and reading history
- Multi-author support (for when I convince friends to write guest posts)
But those are projects for future weekends.
The Real Takeaway (Why This Matters)
This wasn't about speed coding or showing off. It was about proving a point: with the right tools and mindset, you can go from idea to production faster than you think.
I'm not a 10x developer. I don't have magical productivity hacks. I just:
- Started with a clear (small) goal
- Used tools I already knew well
- Built incrementally and tested often
- Automated the boring stuff early
- Didn't try to solve every problem at once
For junior developers reading this: You can absolutely build something this complex. Start small, be consistent, and don't be afraid to ship something imperfect. Your first version doesn't have to be your last version.
Most importantly: The best way to learn web development is to build real projects that solve real problems (even if that problem is "I want a blog"). Tutorials are great, but nothing beats the learning that happens when you're trying to make something actually work.
Now stop reading and go build something. You've got this.
This entire site is open source on GitHub. Every feature mentioned in this article was built and deployed in a single day, proving that modern web development tools are incredibly powerful when you know how to use them.