{"id":48217727,"url":"https://github.com/cdilga/ghost-blog","last_synced_at":"2026-04-04T19:01:47.363Z","repository":{"id":335453505,"uuid":"1136579435","full_name":"cdilga/ghost-blog","owner":"cdilga","description":"Ghost blog theme and deployment for chris.dilger.me","archived":false,"fork":false,"pushed_at":"2026-02-22T15:24:56.000Z","size":65436,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-22T16:20:32.842Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cdilga.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-01-18T00:08:02.000Z","updated_at":"2026-02-22T11:31:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cdilga/ghost-blog","commit_stats":null,"previous_names":["cdilga/ghost-blog"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/cdilga/ghost-blog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdilga%2Fghost-blog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdilga%2Fghost-blog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdilga%2Fghost-blog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdilga%2Fghost-blog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cdilga","download_url":"https://codeload.github.com/cdilga/ghost-blog/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdilga%2Fghost-blog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31409471,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T19:01:46.842Z","updated_at":"2026-04-04T19:01:47.353Z","avatar_url":"https://github.com/cdilga.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"```\n         ██████╗██╗      █████╗ ██╗   ██╗██████╗ ███████╗     ██████╗ ██████╗ ██████╗ ███████╗██████╗\n        ██╔════╝██║     ██╔══██╗██║   ██║██╔══██╗██╔════╝    ██╔════╝██╔═══██╗██╔══██╗██╔════╝██╔══██╗\n        ██║     ██║     ███████║██║   ██║██║  ██║█████╗      ██║     ██║   ██║██║  ██║█████╗  ██║  ██║\n        ██║     ██║     ██╔══██║██║   ██║██║  ██║██╔══╝      ██║     ██║   ██║██║  ██║██╔══╝  ██║  ██║\n        ╚██████╗███████╗██║  ██║╚██████╔╝██████╔╝███████╗    ╚██████╗╚██████╔╝██████╔╝███████╗██████╔╝\n         ╚═════╝╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝     ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═════╝\n```\n\n\u003cdiv align=\"center\"\u003e\n\n# chris.dilger.me\n\n### A production blog built entirely by AI agents. Zero human-written code.\n\n[![Ghost](https://img.shields.io/badge/Ghost-5.x-738A94?style=for-the-badge\u0026logo=ghost\u0026logoColor=white)](https://ghost.org)\n[![Claude](https://img.shields.io/badge/Claude-Opus%204.5-D97757?style=for-the-badge\u0026logo=anthropic\u0026logoColor=white)](https://claude.ai)\n[![Deploy](https://img.shields.io/badge/Deploy-GitHub%20Actions-2088FF?style=for-the-badge\u0026logo=github-actions\u0026logoColor=white)](https://github.com/features/actions)\n\n---\n\n**[Live Site](https://chris.dilger.me)** · **[View Commits](../../commits/main)** · **[Deployment Pipeline](.github/workflows/deploy.yml)**\n\n\u003c/div\u003e\n\n---\n\n## The Experiment\n\n\u003e *\"What if you never touched a keyboard, and AI agents built your entire production website?\"*\n\nThis repository is the answer. **13,615 lines of code** across a custom Ghost theme, WebGL effects, CI/CD pipelines, image optimization infrastructure, and E2E tests—all generated through conversation with Claude.\n\n```\n┌─────────────────────────────────────────────────────────────────────────────┐\n│                                                                             │\n│    HUMAN-WRITTEN CODE:    0 lines                                           │\n│    AI-GENERATED CODE:     13,615 lines                                      │\n│                                                                             │\n│    HUMAN-WRITTEN CONTENT: 100%    (all blog posts \u0026 articles)               │\n│    AI-GENERATED CONTENT:  0%                                                │\n│                                                                             │\n└─────────────────────────────────────────────────────────────────────────────┘\n```\n\nThe code is AI. The thoughts are mine.\n\n---\n\n## By The Numbers\n\n```\n╔════════════════════════════════════════════════════════════════════════════╗\n║                         RESOURCE CONSUMPTION                               ║\n╠════════════════════════════════════════════════════════════════════════════╣\n║                                                                            ║\n║    Tokens Consumed         899.48M tokens                                  ║\n║    API Equivalent Cost     $607.49                                         ║\n║    Claude Max Plan Usage   ~80% of $200/week limit                         ║\n║                                                                            ║\n║    Parallel Agents         Up to 8 concurrent                              ║\n║    Git Worktrees           Up to 3 simultaneous                            ║\n║    Total Commits           130                                             ║\n║                                                                            ║\n║    Time to Ship            1 weekend (with tweaks through the week)        ║\n║                                                                            ║\n╚════════════════════════════════════════════════════════════════════════════╝\n\n    Measured with ccusage (github.com/ryoppippi/ccusage)\n```\n\n---\n\n## What Got Built\n\n### Custom Ghost Theme\n\nA scrollytelling portfolio with WebGL depth effects, smooth scroll animations, and a desert-inspired design system.\n\n```\nthemes/chris-theme/\n├── assets/\n│   ├── js/\n│   │   ├── main.js                 # Animation orchestration\n│   │   ├── hero-depth.js           # PixiJS depth displacement\n│   │   ├── claude-codes-depth.js   # WebGL showcase effects\n│   │   ├── projection-carousel.js  # 3D perspective carousel\n│   │   ├── terminal-spawn.js       # Terminal typing animations\n│   │   ├── windswept-mask.js       # Scroll-triggered masks\n│   │   └── inline-depth.js         # Content depth mapping\n│   └── css/\n│       └── style.css               # Design system (4000+ lines)\n├── partials/                       # Handlebars components\n└── *.hbs                           # Page templates\n```\n\n**Key Features:**\n- **Lenis + GSAP ScrollTrigger** — Buttery 60fps scrolling with precise animation triggers\n- **PixiJS WebGL** — GPU-accelerated depth displacement using greyscale depth maps\n- **Reduced-motion support** — Full accessibility compliance without sacrificing visual impact\n- **Fluid typography** — Single `clamp()` scale that adapts from mobile to 4K\n- **Accelerometer parallax** — Mobile device tilt affects depth layers\n\n### Image Optimization Pipeline\n\nThree-stage optimization that runs at build time, upload time, and migration time:\n\n```\n┌──────────────┐     ┌──────────────┐     ┌──────────────┐\n│  BUILD TIME  │     │ UPLOAD TIME  │     │  MIGRATION   │\n│              │     │              │     │              │\n│  Sharp-based │     │   Custom     │     │   Batch      │\n│  WebP gen    │────▶│   Storage    │────▶│   Legacy     │\n│  Responsive  │     │   Adapter    │     │   Convert    │\n│  Sizing      │     │              │     │              │\n└──────────────┘     └──────────────┘     └──────────────┘\n     │                     │                    │\n     ▼                     ▼                    ▼\n  Theme images      Every upload         Existing content\n  optimized at      auto-generates       migrated to WebP\n  4 breakpoints     WebP + sizes         with DB updates\n```\n\n### CI/CD Pipeline\n\nProduction-grade deployment with intelligent change detection:\n\n```yaml\n# Simplified view of deploy.yml (233 lines)\n\ndetect-changes ──▶ dorny/paths-filter identifies what changed\n       │\n       ├──▶ test ──────────▶ gscan validation + Playwright E2E\n       │\n       ├──▶ deploy-theme ──▶ rsync + MySQL theme activation\n       │\n       ├──▶ deploy-storage ─▶ Custom adapter + npm install\n       │\n       └──▶ restart-ghost ──▶ 60-second health check\n```\n\n**Resilience features:** SSH retry with exponential backoff, pre-deployment backups, database-driven theme activation, health verification.\n\n### Testing Infrastructure\n\n```javascript\n// tests/smoke.spec.js - Catches the bugs that matter\n\ntest('hero scroll cycle maintains visibility', async () =\u003e {\n  // This test caught a z-index stacking bug that would have\n  // made the hero disappear after scrolling back up.\n  //\n  // It scrolls to bottom, back to top, and pixel-diffs\n  // against the initial state with 1% tolerance.\n});\n```\n\n**Coverage:**\n- Theme validation (gscan)\n- Console error detection\n- Scroll system initialization\n- Visual regression testing\n- Storage adapter unit tests\n\n---\n\n## Architecture Decisions\n\nA few choices that might interest you:\n\n| Decision | Why |\n|----------|-----|\n| **Database-driven theme activation** | Ghost CLI has permission complexity. Raw SQL update to `settings` table is atomic and reliable. |\n| **Custom storage adapter over plugins** | Full control over the optimization pipeline. WebP URLs returned by default. |\n| **Flat image naming** (`hero-1280.webp`) | Easier glob patterns, simpler template references than nested directories. |\n| **Depth maps over CSS transforms** | GPU-accelerated displacement creates parallax that CSS can't match. |\n| **Reduced-motion keeps parallax** | Accessibility doesn't mean boring—static parallax layers still work. |\n\n---\n\n## Local Development\n\n```bash\n# Prerequisites: Docker, Node.js 20+\n\n# Start Ghost locally\nnpm start              # Spins up Docker Compose\n\n# Run the full test suite\nnpm test               # Validates theme + runs Playwright\n\n# Package theme for manual upload\nnpm run package        # Creates theme.zip\n```\n\nThe Docker setup mounts the theme directory for live reloading. Edit, save, refresh.\n\n---\n\n## The Tech Stack\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                         FRONTEND                                │\n├─────────────────────────────────────────────────────────────────┤\n│  Ghost 5           │  Headless CMS with Handlebars templating   │\n│  PixiJS            │  WebGL rendering for depth effects         │\n│  GSAP              │  Animation timeline \u0026 ScrollTrigger        │\n│  Lenis             │  Smooth scroll virtualization              │\n│  Lite-YouTube      │  Lazy-loaded video embeds                  │\n├─────────────────────────────────────────────────────────────────┤\n│                        TOOLING                                  │\n├─────────────────────────────────────────────────────────────────┤\n│  Sharp             │  High-performance image processing         │\n│  Playwright        │  E2E testing \u0026 visual regression           │\n│  gscan             │  Ghost theme validation                    │\n│  Docker Compose    │  Local development environment             │\n├─────────────────────────────────────────────────────────────────┤\n│                       DEPLOYMENT                                │\n├─────────────────────────────────────────────────────────────────┤\n│  GitHub Actions    │  CI/CD with selective deployment           │\n│  rsync over SSH    │  Atomic file transfers                     │\n│  MySQL             │  Production database                       │\n│  Self-hosted VPS   │  Full infrastructure control               │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n---\n\n## Reading The Commit Log\n\nThe [commit history](../../commits/main) tells the story of iterative AI development:\n\n```\n9d60370 Initial commit: documentation files\n    │\n    ├── Motion research phase\n    │   34895a2 Add motion design principles\n    │   6947ce1 Add Lenis + GSAP ScrollTrigger foundation\n    │\n    ├── Feature development\n    │   c8e7a2d Implement dual depth-map canvas system\n    │   4e4788c feat(motion): implement terminal dock-spawn animation\n    │\n    ├── Bug fixing (the real work)\n    │   6fb2a1a fix(hero): add z-index to hero-parallax for proper stacking\n    │   b2057de fix(depth): prevent edge gaps on unusual aspect ratios\n    │\n    └── 70e6a62 fix(ci): activate theme after deployment (latest)\n```\n\nEvery commit is AI-generated. Every bug was AI-diagnosed and AI-fixed through conversation.\n\n---\n\n## Why This Exists\n\nI wanted to test a hypothesis: **Can AI agents ship production software if you're specific enough about what you want?**\n\nThe answer is yes—with caveats:\n- You still need taste (design direction, UX decisions)\n- You still need domain knowledge (Ghost, WebGL, CI/CD patterns)\n- You still need to review (AI makes confident mistakes)\n\nBut the keyboard? Optional.\n\n---\n\n## FAQ\n\n**Q: Is this sustainable for larger projects?**\n900M tokens for a blog is expensive at API rates. The Claude Max plan ($200/week) made this feasible. For production teams, the economics depend heavily on velocity gains vs. token costs.\n\n**Q: What about the articles themselves?**\nAll editorial content is human-written. The AI built the house; I write what goes inside.\n\n**Q: Would you do this again?**\nAbsolutely. The parallel agent workflow (8 agents across 3 worktrees) is genuinely faster than traditional development for this type of project.\n\n**Q: What's the catch?**\nContext windows. Long debugging sessions require careful prompt engineering to keep the AI focused. The [CLAUDE.md](CLAUDE.md) file exists specifically to maintain context across sessions.\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n## License\n\nMIT — Do what you want.\n\n---\n\n*Built with [Claude Code](https://claude.ai/code) over one very productive weekend.*\n\n*If you made it this far, you might enjoy the [actual blog](https://chris.dilger.me).*\n\n```\n        ┌──────────────────────────────────────────────────────────┐\n        │                                                          │\n        │   \"The best code is the code you didn't have to write\"   │\n        │                                                          │\n        │                    — taken literally                     │\n        │                                                          │\n        └──────────────────────────────────────────────────────────┘\n```\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcdilga%2Fghost-blog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcdilga%2Fghost-blog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcdilga%2Fghost-blog/lists"}