The Problem
The journey started with a simple need: I wanted a blog where I could write posts with my preferred markdown editor, Markdown Monster by Rick Strahl, and just upload them via FTP to my server.
That's it. No complex CMS. No database setup. No admin panels. Just:
- Write markdown locally
- Upload via FTP
- Post appears automagically on the website
Sounds simple, right?
The Search
I started searching for existing solutions:
- WordPress? Too heavy. Requires database setup, PHP, and a whole ecosystem.
- Jekyll/Hugo? Static site generators are great, but require building and deploying the entire site.
- Ghost? Still too complex with Node.js and database requirements.
- Markdown CMS solutions? Most were either abandoned or overcomplicated.
Nothing matched the vision of "upload a markdown file and it just works."
The Decision
So I decided to vibe code this blog software!
And that's exactly what happened. With the power of GitHub Copilot and modern .NET, we built MDBlog from scratch in just one evening.
But here's the thing: It wasn't a one-shot generation. It was an iterative process - a dialogue between human and AI.
The Collaborative Process
This wasn't about asking AI to "build me a blog" and getting complete code. It was:
- π£οΈ Discussing the vision and requirements
- π Brainstorming architecture decisions together
- π¨ Building feature by feature, step by step
- π Finding errors together and debugging
- π¨ Refining the design through iterations
- π Documenting as we went
Each feature was a conversation:
"Hey, can we make the header collapse on scroll?"
β Implemented CSS transitions
β Tested together
β Found performance issue
β Fixed with passive listeners
β Refined animation timing
β Perfect!
Every challenge was solved together:
"Images aren't showing up correctly..."
β Investigated the issue
β Discovered path resolution problem
β Tried regex solution
β Tested various scenarios
β Refined the approach
β Problem solved! π―
This iterative, conversational approach is what made the difference. Not AI replacing the developer, but AI amplifying the developer's vision through collaborative problem-solving.
The Vision
Core Requirements
- Markdown-First: Posts are just
.mdfiles with YAML front matter - FTP-Friendly: Upload files, restart app, done
- No Database Setup: SQLite for caching, auto-generated
- Beautiful Design: Inspired by bitboxx.net's matrix aesthetic
- Developer-Friendly: Built with Blazor and .NET 10
Non-Requirements
- β No admin dashboard needed
- β No online editor required
- β No complex authentication
- β No database migrations
The Architecture
Why Blazor + .NET 10?
Blazor was chosen because:
- Modern, fast, and interactive
- C# everywhere (no JavaScript mess)
- Server-side rendering for SEO
- Native .NET performance
.NET 10 because:
- Latest and greatest
- Amazing performance improvements
- Future-proof
The File Structure
Each blog post lives in its own directory:
BlogPosts/
βββ 2024-11-20-building-mdblog/
β βββ post.md # The content
β βββ images/ # Post images
β βββ cover.svg
Why this structure?
- Easy to organize
- FTP-friendly (just upload a folder)
- Date in directory name for sorting
- Images live with the post
The Magic Sauce
Here's how it works:
- Background Service scans
BlogPosts/directory on startup - YAML Front Matter is parsed for metadata
- SQLite Cache stores metadata for fast queries
- Markdown Rendering happens on-demand with Markdig
- Image Paths are automatically converted to absolute URLs
// This runs on startup
await dbContext.Database.EnsureCreatedAsync();
await blogService.RefreshPostsFromFileSystemAsync();
The beauty? Delete the database, restart the app, and it rebuilds from the markdown files! π―
Key Features Implemented
1. Workflow β
Write your post in your preferred markdown editor:
---
title: "My Post"
date: 2024-11-20
author: "Torsten Weggen"
tags: [blazor, markdown]
image: "images/cover.jpg"
---
# My Post
Content here...
Save. Upload via FTP. Done! β¨
2. Syntax Highlighting π¨
Using Highlight.js from CDN:
public class BlogPost
{
public string Title { get; set; }
public DateTime PublishedDate { get; set; }
}
Supports C#, JavaScript, Python, PowerShell, SQL, and more!
3. Theme System π¨
- Customizable themes - Switch between themes instantly
- Default theme - Clean, professional blue & white
- Bitboxx theme - Matrix-inspired with collapsing header
- Easy switching - Just change config, restart, done!
Want your own look? Create a custom theme with just CSS, JavaScript (optional), and a config file!
4. Smart Image Handling
Write markdown like this:

It automatically becomes:
<img src="/BlogPosts/2024-11-20-my-post/images/photo.jpg" />
No manual path management! π
5. Placeholder SVG Generation πΌοΈ
No cover image? No problem! Auto-generated SVG placeholders:
<svg>
<text>Blog Post Title</text>
</svg>
6. SEO Optimization
Every post gets:
- Meta descriptions
- Open Graph tags (Facebook)
- Twitter Cards
- Semantic HTML
- Proper heading hierarchy
7. View Tracking
Simple analytics built-in:
- View counts per post
- Stored in SQLite
- No external tracking needed
The Development Process
The Iterative Journey
This wasn't a linear "write code, done" process. It was iterative and conversational - each phase built on learnings from the previous one.
The pattern repeated:
- π¬ Discuss what we want to achieve
- π― Plan the approach together
- π» Implement step by step
- π§ͺ Test and find issues
- π§ Debug together
- β¨ Refine until perfect
- π Document what we learned
Real examples from our sessions:
"Let's add syntax highlighting!"
- First attempt: Added Highlight.js
- Issue: Not applying to code blocks
- Discussion: When does Blazor render?
- Solution: JavaScript interop after render
- Refinement: Proper lifecycle hooks
- Result: Beautiful syntax highlighting! π¨
"The bitboxx.net design looks cool!"
- Started with basic layout
- Tried matrix theme
- Issue: Header too static
- Idea: Make it collapse on scroll
- Implementation: CSS + JS
- Debugging: Performance issues
- Fix: Passive event listeners
- Polish: Smooth transitions
- Final: Awesome animated header! π
"Images aren't working..."
- Problem identified together
- Multiple solutions discussed
- Regex approach chosen
- Edge cases tested
- Path conversion refined
- Placeholder SVG added as bonus
- Complete solution! πΈ
Phase 1: Core Structure
- β Blazor app setup
- β File system scanning
- β SQLite integration
- β Basic routing
Challenge: "How do we scan markdown files?"
Solution: Background service with file system watcher
Iteration: Added caching, error handling, logging
Phase 2: Markdown Processing
- β Markdig integration
- β YAML front matter parsing
- β Image path conversion
- β Code syntax highlighting
Challenge: "YAML parsing is tricky..."
Solution: YamlDotNet with custom deserializer
Iteration: Added fallbacks, validation, error messages
Phase 3: Design
- β bitboxx.net inspired layout
- β Collapsing header animation
- β Responsive design
- β Matrix theme
Challenge: "How to make it responsive?"
Solution: Media queries + flexible layout
Iteration: Tested on mobile, tablet, desktop - refined breakpoints
Phase 4: Theme System
- β Theme architecture
- β Default theme (generic)
- β Bitboxx theme (personal)
- β Theme switching
Challenge: "How to support multiple designs without code duplication?"
Solution: Theme system with CSS, JS, and config per theme
Iteration: Made Bitboxx theme private, Default public
Phase 5: Polish
- β Placeholder images
- β SEO optimization
- β Error handling
- β Mobile optimization
Challenge: "Posts without images look empty..."
Solution: Auto-generated SVG placeholders
Phase 6: Documentation
- β README.md with theme docs
- β THEMES.md for custom themes
- β IIS deployment guide
Challenge: "How to explain the theme system?"
Solution: Separate THEMES.md for developers, README for users
Iteration: Consolidated everything into README
Iteration: Structured, organized, complete guide
What Made This Work?
The dialogue was key:
β Not: "Build me a blog system" β generates 10,000 lines of code
β
Instead: Step-by-step feature building with constant feedback
β Not: AI doing everything alone
β
Instead: Human vision + AI implementation + shared debugging
β Not: Accepting first solution
β
Instead: Iterating until it's exactly right
The power of "What if we..." questions:
- "What if we make the header collapse?"
- "What if we add a ticker with recent posts?"
- "What if we auto-generate placeholders?"
- "What if we use green like bitboxx.net?"
Each question led to exploration, implementation, and refinement.
Challenges Overcome
1. Image Path Resolution
Challenge: Relative image paths in markdown don't work when served from Blazor.
Solution: Regex-based path transformation after markdown rendering:
var imgRegex = new Regex(@"<img\s+([^>]*?)src=""(?!http|\/BlogPosts\/)([^""]+)""");
html = imgRegex.Replace(html, match => {
var imagePath = match.Groups[2].Value;
return $"<img {beforeSrc}src=\"/BlogPosts/{slug}/{imagePath}\"{afterSrc}>";
});
2. Code Block Syntax Highlighting
Challenge: Highlight.js needs to run after Blazor renders.
Solution: JavaScript interop with proper timing:
window.highlightCode = function() {
document.querySelectorAll('pre code').forEach((block) => {
hljs.highlightBlock(block);
});
};
Called from Blazor after content renders! β
3. Header Collapse Animation
Challenge: Smooth header animation on scroll without performance issues.
Solution: CSS transitions + passive scroll listener:
window.addEventListener('scroll', handleScroll, { passive: true });
4. Git Workflow
Challenge: How to track source code but not blog content?
Solution: Smart .gitignore:
# Ignore posts and database
BlogPosts/
*.db
# But keep structure
!BlogPosts/.gitkeep
!BlogPosts/README.md
The Result
A blog system that:
- β Works with Markdown Monster - Write locally, upload, done!
- β FTP-Friendly - Just upload folders
- β Auto-Regenerating - Delete database? No problem!
- β Beautiful Design - Theme system with multiple styles
- β Customizable - Switch themes or create your own
- β Developer-Friendly - Built with modern .NET
- β Lightweight - ~50MB deployment
- β Fast - SQLite caching, on-demand rendering
- β SEO-Ready - All the meta tags you need
Workflow in Action
Here's the actual workflow now:
1. Write in Markdown Monster:
---
title: "My New Post"
tags: [dotnet, blazor]
---
# Content here...
2. Save images in images/ folder:
2024-11-20-my-post/
βββ post.md
βββ images/
βββ photo.jpg
3. FTP upload to server:
/BlogPosts/2024-11-20-my-post/
4. Restart app (or wait for next restart):
iisreset # Or just recycle app pool
5. Post appears automatically! π
No build step. No database migration. No admin panel. Pure simplicity!
Technical Highlights
Stack
Frontend: Blazor Server + Interactive Components
Backend: .NET 10 + ASP.NET Core
Database: SQLite (auto-generated)
Markdown: Markdig
YAML: YamlDotNet
Syntax: Highlight.js (CDN)
Themes: Modular system (CSS + JS + Config)
Performance
- Startup: Background scan, < 1 second for 100 posts
- Page Load: < 50ms (metadata from SQLite)
- Markdown Rendering: On-demand, cached in memory
- Image Serving: Static file middleware, 7-day cache
Code Statistics
C# Lines: ~2,500
Razor Lines: ~800
CSS Lines: ~1,200
JS Lines: ~100
Total: ~4,600 lines
Build Size: ~50 MB
Deployment: Single folder copy
Dependencies: 5 NuGet packages
What I Learned
1. Simplicity Wins
The simpler the architecture, the easier to maintain. No complex abstractions needed.
2. File System > Database
For blog posts, the file system IS the database. SQLite is just a cache.
3. Modern .NET is Amazing
Blazor + .NET 10 made this a joy to build. No JavaScript frameworks needed!
4. Good Documentation Matters
Spent as much time on README as on code. Worth it!
5. GitHub Copilot is Powerful
Pair programming with AI is the future. But it's not about AI replacing developers - it's about augmenting human creativity with AI capabilities.
What worked:
- π¬ Constant dialogue - Not one-shot prompts, but ongoing conversation
- π Iterative refinement - Each solution built on the previous one
- π€ Shared debugging - Finding and fixing issues together
- π― Clear goals - Human sets the vision, AI helps implement
- π§ͺ Testing together - Catching edge cases and refining
The real power:
- Writing code faster, but not blindly accepting it
- Exploring solutions through conversation
- Learning from each iteration
- Debugging collaboratively
- Documenting as we go
Example dialogue pattern:
Human: "Images aren't showing up"
AI: "Let me check the path conversion..."
Human: "Ah, it's relative paths!"
AI: "We could use regex to transform them"
Human: "Yes, but what about external URLs?"
AI: "Good point, let's exclude those..."
Human: "And what about already absolute paths?"
AI: "Added that check too!"
Human: "Perfect! Now it works!"
This back-and-forth, this collaborative problem-solving, is what made the difference.
Time saved? Massive! π
Quality? Higher, because we refined together! β¨
Learning? Continuous, through every iteration! π
The future isn't AI replacing developers - it's developers + AI = superpowers! π¦ΈββοΈπ€
Future Ideas
Things I might add later:
- RSS Feed - For feed readers
- Search - Full-text search across posts
- Comments - Maybe Utterances (GitHub Issues)
- Community Themes - Share custom themes
- Draft Mode - Preview unpublished posts
- Analytics Dashboard - Visualize view counts
But for now? It does exactly what I need! β¨
Try It Yourself
The project is open source:
GitHub: https://github.com/weggetor/MDBlog
Requirements:
- .NET 10 SDK
- That's it!
Setup:
git clone https://github.com/weggetor/MDBlog
cd MDBlog
dotnet run
Create a post:
mkdir BlogPosts/2024-11-20-hello-world
cd BlogPosts/2024-11-20-hello-world
# Create post.md with YAML front matter
# Add images/ folder if needed
Restart app, and it appears! π
Conclusion
Sometimes you don't find the perfect tool because it doesn't exist yet.
Instead of compromising, we built exactly what was needed:
A lightweight, markdown-first blog system that works with Markdown Monster and FTP uploads.
No database setup. No build steps. No complexity.
Just write markdown, upload files, and blog! π
The Real Secret Sauce
But the bigger lesson here? It wasn't just about the code.
It was about the process:
- Having a clear vision
- Building incrementally
- Iterating through dialogue
- Debugging together
- Refining until perfect
This is what AI-augmented development looks like:
- Not "AI, build this"
- But "AI, let's build this together"
Every feature was a conversation.
Every challenge was solved collaboratively.
Every refinement made it better.
The result? A blog system that does exactly what I need, built in record time, through iterative collaboration between human creativity and AI capabilities.
Built with:
- β€οΈ Passion for simplicity
- π€ GitHub Copilot (as collaborative partner)
- π bitboxx.net design inspiration
- βοΈ Markdown Monster for writing
- π£οΈ Constant dialogue and iteration
The Process:
- π Vision from human
- π» Implementation with AI
- π Debugging together
- β¨ Refinement through conversation
- π Documentation as we went
Tech Stack:
- Blazor + .NET 10
- SQLite + Entity Framework Core
- Markdig + Highlight.js
- YamlDotNet
- Theme System (modular CSS/JS)
Status: Production-ready and powering this blog! β¨
Development Time: A few hours of iterative collaboration
Lines of Code: ~4,600 lines of refined, tested code
Developer Joy: Maximum! π
This post was written in Markdown Monster, uploaded via FTP, rendered by MDBlog, and documents the iterative process of building it. MetaΒ²! π
Want to try it? Clone the repo, write some markdown, and experience the simplicity yourself!
Have ideas? Open an issue, start a discussion, or fork and improve! This was built through collaboration - let's keep that spirit going! π
