My little corner of the internet showcasing personal projects and updates about my work
Frontend Tech Design Doc
Overview
Fusite Web is a custom-built static site generator that transforms Notion databases into a high-performance, SEO-optimized personal website. Rather than using conventional static site generators like Hugo or Jekyll, this system is purpose-built to leverage Notion as a headless CMS, providing a seamless content management experience while delivering blazing-fast static HTML.
Key Characteristics
Why Custom-Built?
Traditional static site generators require content in markdown files or proprietary formats. By building a custom generator, Fusite achieves:
Technology Stack
Core Runtime & Build Tools
| Technology | Version | Purpose |
| Bun | 1.0.0+ | JavaScript runtime with built-in bundler and fast package management |
| TypeScript | 5.6+ | Type-safe development with strict mode enabled |
| Handlebars | 4.7.8 | Server-side HTML templating engine |
| PostCSS | 8.4.47 | CSS processing pipeline |
Frontend Technologies
| Technology | Purpose |
| Tailwind CSS | Utility-first CSS framework for responsive design |
| PrismJS | Syntax highlighting for code blocks (supports 30+ languages) |
| @tailwindcss/typography | Beautiful typographic defaults for content |
| @tailwindcss/forms | Consistent form styling |
AWS Infrastructure
| Service | Purpose |
| S3 | Static file hosting |
| CloudFront | Global CDN distribution |
| AWS SDK v3 | Programmatic deployment and cache invalidation |
Notion Integration
| Package | Purpose |
| fusite-commons | Shared library for Notion API integration, type definitions, and content transformation |
| @notionhq/client | Official Notion SDK (abstracted through fusite-commons) |
System Architecture
High-Level Data Flow
┌─────────────────┐
│ Notion │
│ Databases │
│ - Projects │
│ - Chronicles │
│ - About Page │
└────────┬────────┘
│
▼
┌─────────────────┐
│ fusite-commons │
│ NotionService │
│ - API calls │
│ - Transforms │
└────────┬────────┘
│
▼
┌─────────────────┐
│ DataService │
│ - Caching │
│ - Aggregation │
└────────┬────────┘
│
▼
┌─────────────────┐
│ SiteBuilder │
│ Orchestrator │
└────────┬────────┘
│
├─────────────┬─────────────┬─────────────┬─────────────┐
▼ ▼ ▼ ▼ ▼
Route Gen Template CSS Process Asset Copy SEO Gen
(URLs) Rendering (Tailwind) (Static) (Sitemap)
│ │ │ │ │
└─────────────┴─────────────┴─────────────┴─────────────┘
│
▼
┌─────────────┐
│ /dist │
│ Static HTML │
└──────┬──────┘
│
▼
┌─────────────┐
│ S3 Bucket │
└──────┬──────┘
│
▼
┌─────────────┐
│ CloudFront │
│ Global CDN │
└─────────────┘Data Sources & Content Model
Notion Database Structure
Fusite sources all content from four Notion databases:
1. Projects Database
Properties:
title (Title): Project nameslug (Rich Text): URL-safe identifierdescription (Rich Text): Short project summarystatus (Select): Active | Completed | On Hold | Archivedtags (Multi-select): Technology/category tagsstartDate (Date): Project startendDate (Date): Project completionfeaturedImage (URL): Open Graph imagepublic (Checkbox): Visibility in dev environmentContent:
2. Fuschronicles Database (Development Logs)
Properties:
title (Title): Chronicle/post titleslug (Rich Text): URL identifierexcerpt (Rich Text): Brief summarytype (Select): documentation | changelog | logpublishedDate (Date): Publication timestampfeaturedImage (URL): Cover imagerelatedProject (Relation): Links to Projects databaseContent Types:
/projects/{slug}/docs/{docSlug})/fuschronicles feed)3. About Page
Structure:
/about routeStatic Site Structure
Generated Directory Layout
The build process creates this structure in /dist:
dist/
├── index.html # Home page (/)
├── about/
│ └── index.html # About page (/about)
├── projects/
│ ├── index.html # Projects listing (/projects)
│ ├── fusclock/
│ │ ├── index.html # Project detail (/projects/fusclock)
│ │ ├── docs/
│ │ │ ├── getting-started/
│ │ │ │ └── index.html # Documentation (/projects/fusclock/docs/getting-started)
│ │ │ └── api-reference/
│ │ │ └── index.html
│ │ └── timeline/
│ │ └── index.html # Changelogs (/projects/fusclock/timeline)
│ ├── fuschantt/
│ │ └── ...
│ └── [other-projects]/
├── fuschronicles/
│ ├── index.html # Chronicles listing (/fuschronicles)
│ ├── 2025-01-15-new-feature/
│ │ └── index.html # Individual chronicle (/fuschronicles/2025-01-15-new-feature)
│ └── [other-chronicles]/
├── assets/
│ ├── css/
│ │ └── main.css # Compiled and minified CSS
│ ├── images/
│ │ ├── fuschimp_transparent.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ └── og-default.jpg
├── sitemap.xml # SEO sitemap
├── robots.txt # Search engine directives
└── 404.html # Error page (optional)Route Generation Logic
Routes are generated programmatically by the SiteBuilder:
Static Routes
| Route | Template | Purpose |
/ | home-direct.hbs | Landing page with featured projects and latest chronicles |
/about | about-direct.hbs | Personal bio and information |
/projects | projects-direct.hbs | Complete project portfolio listing |
/fuschronicles | fuschronicles-direct.hbs | Development log feed (excludes documentation type) |
Dynamic Routes (Generated per Content Item)
Projects:
/projects/{slug} → project-complete.hbs- Full project details
- Related chronicles sidebar
- Documentation links
Project Documentation:
/projects/{slug}/docs/{docSlug} → project-doc.hbs- Documentation content
- Project navigation sidebar
- Breadcrumb navigation
Project Timelines:
/projects/{slug}/timeline → timeline.hbs- Chronological changelogs for the project
- Documentation sidebar
Chronicles:
/fuschronicles/{slug} → fuschronicle-complete.hbs- Full chronicle content
- Previous/Next navigation
- Related project links
URL Slug Generation
Slugs are generated from Notion properties using the slug.ts utility:
// Helper functions available in templates
projectUrl(slug) // → /projects/{slug}
chronicleUrl(slug) // → /fuschronicles/{slug}
projectTimelineUrl(slug) // → /projects/{slug}/timeline
projectDocUrl(projectSlug, docSlug) // → /projects/{slug}/docs/{docSlug}
projectDocsUrl(slug) // → /projects/{slug}/docsSlug Rules:
Build Pipeline
Build Process Flow
The build transforms Notion content into a production-ready static website in 8 sequential phases:
Typical build time: 3-5 seconds → produces /dist directory ready for S3 + CloudFront deployment
Key Architectural Decisions
1. Notion as Primary CMS
Rationale:
Trade-offs:
2. Static Generation over SSR
Rationale:
Trade-offs:
3. Handlebars over React/Vue/Svelte
Rationale:
Trade-offs:
4. Multi-Environment Architecture
Rationale:
Implementation:
.env.{environment} files for configuration5. In-Memory Caching (Not Persistent)
Rationale:
Trade-offs:
Analytics
PostHog Integration
Analytics Setup:
persistence: 'memory')Privacy Considerations:
Tracked Events:
Separate Projects:
Lessons Learned
Successes
Challenges
Summary
Fusite Web demonstrates that custom static site generators can be simple, fast, and powerful. By leveraging Notion as a headless CMS and deploying to AWS infrastructure, the system achieves:
The architecture is well-suited for personal websites, portfolios, and documentation sites where content updates are infrequent and build times are acceptable. For high-frequency content updates or very large sites (1000+ pages), consider adding incremental builds or switching to server-side rendering.
Appendix
File Structure Reference
fusite/web/
├── src/
│ ├── build/
│ │ ├── cli.ts # Build CLI entry point
│ │ ├── builder.ts # Main build orchestrator
│ │ ├── renderer.ts # Handlebars template engine
│ │ ├── css-processor.ts # PostCSS/Tailwind pipeline
│ │ └── validate.ts # Build validation (planned)
│ ├── services/
│ │ └── data.ts # Notion data fetching & caching
│ ├── dev/
│ │ ├── server.ts # HTTP dev server
│ │ └── serve.ts # CLI for dev server
│ ├── utils/
│ │ ├── config.ts # Configuration management
│ │ ├── logger.ts # Structured logging
│ │ ├── slug.ts # URL slug utilities
│ │ ├── date.ts # Date formatting
│ │ └── syntax-highlighter.ts # PrismJS integration
│ ├── templates/
│ │ ├── layouts/
│ │ │ ├── base.hbs # Root HTML layout
│ │ │ ├── page.hbs # Page wrapper
│ │ │ └── article.hbs # Article layout
│ │ ├── pages/
│ │ │ ├── home-direct.hbs # Home page
│ │ │ ├── about-direct.hbs # About page
│ │ │ ├── projects-direct.hbs # Projects listing
│ │ │ ├── project-complete.hbs # Project detail
│ │ │ ├── project-doc.hbs # Documentation page
│ │ │ ├── timeline.hbs # Project timeline
│ │ │ ├── fuschronicles-direct.hbs # Chronicles listing
│ │ │ ├── fuschronicle-complete.hbs # Chronicle detail
│ │ │ └── 404.hbs # Error page
│ │ └── partials/
│ │ ├── header.hbs # Site header/nav
│ │ ├── footer.hbs # Site footer
│ │ ├── project-card.hbs # Project card component
│ │ ├── fuschronicle-card.hbs # Chronicle card
│ │ └── project-about-navigation.hbs # Project sidebar
│ ├── styles/
│ │ ├── main.css # Tailwind entry point
│ │ ├── components.css # Custom components
│ │ ├── utilities.css # Custom utilities
│ │ └── syntax-highlighting.css # PrismJS theme
│ └── assets/
│ └── images/
│ ├── fuschimp_transparent.png
│ ├── favicon-*.png
│ └── og-default.jpg
├── dist/ # Generated site (gitignored)
├── cache/ # Build cache (gitignored)
├── .env # Base configuration
├── .env.local # Local secrets (gitignored)
├── .env.dev # Dev environment
├── .env.prod # Prod environment
├── package.json # Dependencies & scripts
├── tsconfig.json # TypeScript configuration
├── tailwind.config.js # Tailwind settings
├── postcss.config.js # PostCSS configuration
└── bun.lock # Bun lockfile