CLAUDE.md — SVGarden Platform
Project: SVGarden — A massive, searchable bank of SVG + CSS animations with copy-ready code snippets. Author: Simon-Pierre Boucher — contact@spboucher.ai Production URL: https://www.svgarden.dev (served via ngrok from node
m3u96b)
1. Mission
You (Claude Code) are building SVGarden, a static-first web platform that hosts a large, ever-growing library of SVG/CSS animations. Every animation is a self-contained snippet that visitors can preview live, customize, and copy in one click.
The platform must be:
- Scalable by design — adding a new animation means adding ONE file. The gallery, search index, tags, and category pages regenerate automatically at build time.
- Zero-dependency for visitors — every copied snippet must work when pasted into a blank
.htmlfile. No frameworks, no build step, no external assets required by the snippets themselves. - Pedagogical — every snippet ships with a short "How it works" explanation.
- Beautiful — the site itself should demonstrate the craft it teaches.
2. Mandatory file header (NON-NEGOTIABLE)
EVERY code file in this repository — HTML, CSS, JS, config files, snippet files, build scripts, everything — MUST begin with an author header. No exceptions. If you create a file without this header, fix it immediately.
HTML / Snippet files
<!--
============================================================
SVGarden — https://www.svgarden.dev
Author : Simon-Pierre Boucher
Contact: contact@spboucher.ai
File : {relative/path/to/file}
Desc : {one-line description}
============================================================
-->CSS files
/*
============================================================
SVGarden — https://www.svgarden.dev
Author : Simon-Pierre Boucher
Contact: contact@spboucher.ai
File : {relative/path/to/file}
Desc : {one-line description}
============================================================
*/JavaScript / Node files
/**
* ============================================================
* SVGarden — https://www.svgarden.dev
* Author : Simon-Pierre Boucher
* Contact: contact@spboucher.ai
* File : {relative/path/to/file}
* Desc : {one-line description}
* ============================================================
*/Shell scripts / YAML / config
# ============================================================
# SVGarden — https://www.svgarden.dev
# Author : Simon-Pierre Boucher
# Contact: contact@spboucher.ai
# File : {relative/path/to/file}
# Desc : {one-line description}
# ============================================================Additionally, the copy-to-clipboard output of every snippet must include the HTML-comment version of this header at the top, so attribution travels with the code.
3. Tech stack
| Layer | Choice | Rationale |
|---|---|---|
| Runtime | Node.js ≥ 20 (LTS) | Runs on node m3u96b |
| Framework | Astro (latest stable) | Static output, content collections, zero client JS by default |
| Styling | Vanilla CSS with custom properties | The site must eat its own dog food — no Tailwind for the public site |
| Syntax highlighting | Shiki (build-time) | Zero runtime cost |
| Search | Fuse.js (client-side, lazy-loaded) | Small, works on static hosting |
| Server | astro preview or a tiny Express static server on port 4321 |
Fronted by ngrok |
| Tunnel | ngrok with reserved domain www.svgarden.dev |
See §9 |
| Process manager | pm2 | Keeps server + tunnel alive on m3u96b |
If Astro is unavailable or problematic on the node, fall back to a hand-rolled Node build script (build.mjs) that reads snippets/** and emits static HTML from templates. The architecture below must work either way.
4. Repository structure
svgarden/
├── CLAUDE.md ← this file
├── README.md
├── package.json
├── astro.config.mjs
├── ecosystem.config.cjs ← pm2 config (site + ngrok)
├── scripts/
│ ├── new-snippet.mjs ← scaffolds a new snippet file interactively
│ ├── validate-snippets.mjs ← CI check: headers, metadata, self-containment
│ └── deploy.sh ← build + pm2 restart + ngrok health check
├── src/
│ ├── layouts/Base.astro
│ ├── pages/
│ │ ├── index.astro ← gallery home (all snippets, filterable)
│ │ ├── category/[cat].astro
│ │ ├── snippet/[slug].astro ← detail page: preview + code + customizer
│ │ └── about.astro
│ ├── components/
│ │ ├── SnippetCard.astro
│ │ ├── LivePreview.astro ← sandboxed iframe preview
│ │ ├── CodeBlock.astro ← Shiki-highlighted, copy button
│ │ ├── Customizer.astro ← color/speed/size controls → live re-render
│ │ ├── SearchBar.astro
│ │ └── TagFilter.astro
│ └── styles/global.css
├── snippets/ ← THE BANK. One file = one animation.
│ ├── loaders/
│ ├── hover/
│ ├── stroke-draw/
│ ├── gauges/
│ ├── text/
│ ├── morph/
│ ├── backgrounds/
│ └── buttons/
└── public/
├── favicon.svg
└── og/ ← auto-generated OG images per snippet (stretch goal)5. Snippet file format (the heart of the platform)
Every snippet is a single .html file inside snippets/{category}/. It contains a YAML-in-comment frontmatter block, followed by the raw self-contained snippet code.
<!--
============================================================
SVGarden — https://www.svgarden.dev
Author : Simon-Pierre Boucher
Contact: contact@spboucher.ai
File : snippets/loaders/spinner-dash.html
Desc : Rotating arc loader using animated stroke-dasharray
============================================================
-->
<!--svgarden
title: Dash spinner
slug: spinner-dash
category: loaders
tags: [loader, dasharray, keyframes, infinite]
difficulty: beginner
techniques: [stroke-dasharray, stroke-dashoffset, "@keyframes", transform-rotate]
how_it_works: >
The outer rotation is a simple 2s linear spin. The "chasing" effect
comes from animating stroke-dasharray so the visible arc grows and
shrinks while stroke-dashoffset shifts its starting point.
customizable:
- { var: "--sg-color", label: "Color", type: color, default: "#7F77DD" }
- { var: "--sg-size", label: "Size", type: range, min: 24, max: 120, default: 48, unit: px }
- { var: "--sg-duration", label: "Speed", type: range, min: 0.5, max: 4, step: 0.1, default: 1.5, unit: s }
created: 2026-08-10
-->
<div class="sg-spinner" style="--sg-color:#7F77DD; --sg-size:48px; --sg-duration:1.5s;">
<svg viewBox="0 0 50 50" width="var(--sg-size)" ...>...</svg>
</div>
<style>
/* scoped: every class is prefixed sg- and unique per snippet */
</style>Hard rules for snippets
- Self-contained: no external fonts, images, scripts, or CSS. Inline everything.
- Scoped: all class names prefixed with
sg-+ snippet slug context to avoid collisions when users paste multiple snippets in one page. - Customizable via CSS custom properties (
--sg-*) declared on the root element — this is what powers the live Customizer. - Dark/light safe: must look good on both
#ffffffand#111111backgrounds. Preview iframe offers a background toggle. - No JS unless essential (gauges/interactive snippets may use minimal vanilla JS, clearly marked with tag
js). - Accessible: decorative SVGs get
aria-hidden="true"; meaningful ones getrole="img"+<title>. - Max ~120 lines per snippet. Elegance over bloat.
The build pipeline must
- Parse the
<!--svgarden ... -->frontmatter of every file insnippets/**. - Generate: the gallery index, one detail page per snippet, per-category pages, a
search-index.jsonfor Fuse.js, and a tag cloud. - Fail the build if any snippet is missing the author header, frontmatter, or violates validation (
scripts/validate-snippets.mjs).
6. Site features (in priority order)
MVP (build ALL of this)
- Gallery home — responsive card grid, each card shows the live animation (lazy-rendered iframe or inline with IntersectionObserver), title, category badge, tags.
- Detail page per snippet — large live preview with light/dark background toggle, "How it works" section, full highlighted code, Copy code button (copies snippet WITH the attribution header), Download .html button.
- Customizer — auto-generated controls from the
customizablefrontmatter (color pickers, range sliders). Changes update the live preview instantly AND rewrite the code block + clipboard output with the chosen values. - Search & filters — instant client-side search (title, tags, techniques) + category filter + difficulty filter.
- Dark/light site theme — respects
prefers-color-schemewith a manual toggle.
V2 (build after MVP is deployed and validated)
- Keyboard navigation +
/to focus search. - "Random snippet" button.
- Per-snippet OG image generation at build time.
- RSS/JSON feed of newly added snippets.
- Simple analytics (self-hosted Plausible script placeholder — do NOT add third-party trackers).
7. Seed content — REQUIRED example snippets
Create at least 24 snippets at initial build, spread across categories. Each one fully compliant with §5. Required list:
loaders/ (6)
spinner-dash— rotating arc with animated dasharraydots-pulse— three SVG circles pulsing in sequencering-dual— two counter-rotating arcsbar-indeterminate— sliding indeterminate progress barorbit-dots— dots orbiting a center pointhourglass-flip— hourglass shape flipping with rotate keyframes
stroke-draw/ (4)
7. signature-draw — a scripted path "hand-drawing" itself (dashoffset)
8. checkmark-pop — animated checkmark draw + scale pop (success state)
9. circuit-trace — a circuit-like polyline tracing with staggered delays
10. underline-sketch — sketchy underline that draws on load
hover/ (4)
11. star-spin — star rotates + scales on hover
12. icon-morph-menu — hamburger → X on hover/click (line transforms)
13. card-lift-border — SVG border that draws itself around a card on hover
14. magnetic-arrow — arrow that nudges along its axis on hover
gauges/ (3)
15. gauge-circle — circular percentage gauge, JS slider driven (tag: js)
16. gauge-semicircle — semicircle speedometer style
17. battery-fill — battery icon with animated fill level
text/ (3)
18. text-on-path — text following a curved <textPath>, animated startOffset
19. text-stroke-reveal — outlined text that fills in via dashoffset
20. wave-text — letters bouncing in a wave (staggered animation-delay)
morph/ (2)
21. blob-morph — organic blob morphing between path shapes (CSS d: or SMIL fallback)
22. play-pause-morph — play ⇄ pause icon morph on click (tag: js)
backgrounds/ (2)
23. wave-divider — animated layered wave section divider
24. dots-drift — subtle drifting dot-grid pattern background
Each snippet's how_it_works must genuinely teach the technique in 2–4 sentences. Do not copy text between snippets.
8. Design system for the site itself
- Typography: system font stack; headings weight 600, body 400.
- Layout: max-width 1200px gallery, CSS grid
repeat(auto-fill, minmax(280px, 1fr)). - Palette: neutral background, ONE accent color (
#7F77DDviolet), semantic greens/reds only for status. - Cards: 1px hairline borders, 12px radius, no drop shadows, subtle hover lift via
transform: translateY(-2px). - The site must score ≥ 95 on Lighthouse performance & accessibility. Verify before deploying.
- Footer on every page:
© Simon-Pierre Boucher — contact@spboucher.ai — svgarden.dev.
9. Deployment — node m3u96b + ngrok → www.svgarden.dev
Target: the platform runs persistently on node m3u96b and is publicly reachable at https://www.svgarden.dev through an ngrok tunnel.
Steps to implement
- Build:
npm run build→ static output indist/. - Serve: minimal static server (
server.mjs, Express orserve) on127.0.0.1:4321, with correct cache headers (immutablefor hashed assets,no-cachefor HTML) and gzip/brotli. - ngrok:
- Assume ngrok is installed and authenticated on
m3u96b(ngrok config add-authtoken ...already done by the operator; if not, print clear instructions and stop — NEVER ask for or handle the authtoken value yourself). - The domain
www.svgarden.devmust be configured as a reserved custom domain in the ngrok dashboard, with the DNS CNAME pointed at ngrok as per their docs. Document this requirement in README; you cannot do the DNS step yourself. - Tunnel config in
~/.config/ngrok/ngrok.yml(create/extend via a documented block, not by overwriting):yamltunnels: svgarden: proto: http addr: 4321 domain: www.svgarden.dev - Start with
ngrok start svgarden.
- Assume ngrok is installed and authenticated on
- pm2 (
ecosystem.config.cjs): two apps —svgarden-web(the static server) andsvgarden-tunnel(ngrok start svgarden --log=stdout). Enablepm2 save+ startup so both survive reboots. scripts/deploy.sh:git pull→npm ci→npm run build→pm2 restart ecosystem.config.cjs→ curl health check onhttp://127.0.0.1:4321ANDhttps://www.svgarden.dev→ print status summary.- Health: add a
/healthzroute returning build timestamp + snippet count.
Deployment rules
- Never commit secrets, tokens, or the ngrok authtoken to the repo.
.gitignoremust coverngrok.yml,.env*,node_modules,dist. - If the tunnel fails (domain not reserved, auth missing), degrade gracefully: keep the local server running and print actionable instructions.
10. Quality gates & workflow
Before considering ANY task done:
node scripts/validate-snippets.mjspasses (headers ✔, frontmatter ✔, scoping ✔, size limit ✔).npm run buildcompletes with zero warnings.- Every new snippet was visually verified (describe what you checked).
- Copy button output pasted into a blank HTML file renders correctly — test at least 3 snippets this way per session.
- Git: conventional commits (
feat(snippets): add blob-morph,fix(site): ...). Commit in small, logical units.
Working style
- When asked to "add N snippets", follow §5 and §7 formats exactly, pick varied techniques, and update nothing else — the build handles the rest.
- Prefer editing the build system once over hand-editing generated pages ever.
- If a requirement in this file conflicts with a user instruction in chat, the chat instruction wins — but flag the conflict explicitly.
11. Roadmap summary
| Phase | Deliverable |
|---|---|
| 1 | Repo scaffold, build pipeline, validation script, base layout |
| 2 | 24 seed snippets (§7), gallery + detail pages |
| 3 | Search, filters, customizer, copy/download, dark mode |
| 4 | Deployment on m3u96b + ngrok + pm2 + deploy.sh |
| 5 | V2 features (§6), grow the bank toward 100+ snippets |
Build it beautifully. Every snippet is a small lesson; the site is the classroom.