docs: add project CLAUDE.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Showing 1 changed file with +326 and −0
added
CLAUDE.md
+326 −0
@@ -0,0 +1,326 @@ | ||
| 1 | +# CLAUDE.md — SVGarden Platform | |
| 2 | + | |
| 3 | +> **Project:** SVGarden — A massive, searchable bank of SVG + CSS animations with copy-ready code snippets. | |
| 4 | +> **Author:** Simon-Pierre Boucher — contact@spboucher.ai | |
| 5 | +> **Production URL:** https://www.svgarden.dev (served via ngrok from node `m3u96b`) | |
| 6 | + | |
| 7 | +--- | |
| 8 | + | |
| 9 | +## 1. Mission | |
| 10 | + | |
| 11 | +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. | |
| 12 | + | |
| 13 | +The platform must be: | |
| 14 | + | |
| 15 | +1. **Scalable by design** — adding a new animation means adding ONE file. The gallery, search index, tags, and category pages regenerate automatically at build time. | |
| 16 | +2. **Zero-dependency for visitors** — every copied snippet must work when pasted into a blank `.html` file. No frameworks, no build step, no external assets required by the snippets themselves. | |
| 17 | +3. **Pedagogical** — every snippet ships with a short "How it works" explanation. | |
| 18 | +4. **Beautiful** — the site itself should demonstrate the craft it teaches. | |
| 19 | + | |
| 20 | +--- | |
| 21 | + | |
| 22 | +## 2. Mandatory file header (NON-NEGOTIABLE) | |
| 23 | + | |
| 24 | +**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. | |
| 25 | + | |
| 26 | +### HTML / Snippet files | |
| 27 | +```html | |
| 28 | +<!-- | |
| 29 | + ============================================================ | |
| 30 | + SVGarden — https://www.svgarden.dev | |
| 31 | + Author : Simon-Pierre Boucher | |
| 32 | + Contact: contact@spboucher.ai | |
| 33 | + File : {relative/path/to/file} | |
| 34 | + Desc : {one-line description} | |
| 35 | + ============================================================ | |
| 36 | +--> | |
| 37 | +``` | |
| 38 | + | |
| 39 | +### CSS files | |
| 40 | +```css | |
| 41 | +/* | |
| 42 | + ============================================================ | |
| 43 | + SVGarden — https://www.svgarden.dev | |
| 44 | + Author : Simon-Pierre Boucher | |
| 45 | + Contact: contact@spboucher.ai | |
| 46 | + File : {relative/path/to/file} | |
| 47 | + Desc : {one-line description} | |
| 48 | + ============================================================ | |
| 49 | +*/ | |
| 50 | +``` | |
| 51 | + | |
| 52 | +### JavaScript / Node files | |
| 53 | +```js | |
| 54 | +/** | |
| 55 | + * ============================================================ | |
| 56 | + * SVGarden — https://www.svgarden.dev | |
| 57 | + * Author : Simon-Pierre Boucher | |
| 58 | + * Contact: contact@spboucher.ai | |
| 59 | + * File : {relative/path/to/file} | |
| 60 | + * Desc : {one-line description} | |
| 61 | + * ============================================================ | |
| 62 | + */ | |
| 63 | +``` | |
| 64 | + | |
| 65 | +### Shell scripts / YAML / config | |
| 66 | +```bash | |
| 67 | +# ============================================================ | |
| 68 | +# SVGarden — https://www.svgarden.dev | |
| 69 | +# Author : Simon-Pierre Boucher | |
| 70 | +# Contact: contact@spboucher.ai | |
| 71 | +# File : {relative/path/to/file} | |
| 72 | +# Desc : {one-line description} | |
| 73 | +# ============================================================ | |
| 74 | +``` | |
| 75 | + | |
| 76 | +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. | |
| 77 | + | |
| 78 | +--- | |
| 79 | + | |
| 80 | +## 3. Tech stack | |
| 81 | + | |
| 82 | +| Layer | Choice | Rationale | | |
| 83 | +|---|---|---| | |
| 84 | +| Runtime | Node.js ≥ 20 (LTS) | Runs on node `m3u96b` | | |
| 85 | +| Framework | **Astro** (latest stable) | Static output, content collections, zero client JS by default | | |
| 86 | +| Styling | Vanilla CSS with custom properties | The site must eat its own dog food — no Tailwind for the public site | | |
| 87 | +| Syntax highlighting | **Shiki** (build-time) | Zero runtime cost | | |
| 88 | +| Search | **Fuse.js** (client-side, lazy-loaded) | Small, works on static hosting | | |
| 89 | +| Server | `astro preview` or a tiny Express static server on port **4321** | Fronted by ngrok | | |
| 90 | +| Tunnel | **ngrok** with reserved domain `www.svgarden.dev` | See §9 | | |
| 91 | +| Process manager | **pm2** | Keeps server + tunnel alive on `m3u96b` | | |
| 92 | + | |
| 93 | +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. | |
| 94 | + | |
| 95 | +--- | |
| 96 | + | |
| 97 | +## 4. Repository structure | |
| 98 | + | |
| 99 | +``` | |
| 100 | +svgarden/ | |
| 101 | +├── CLAUDE.md ← this file | |
| 102 | +├── README.md | |
| 103 | +├── package.json | |
| 104 | +├── astro.config.mjs | |
| 105 | +├── ecosystem.config.cjs ← pm2 config (site + ngrok) | |
| 106 | +├── scripts/ | |
| 107 | +│ ├── new-snippet.mjs ← scaffolds a new snippet file interactively | |
| 108 | +│ ├── validate-snippets.mjs ← CI check: headers, metadata, self-containment | |
| 109 | +│ └── deploy.sh ← build + pm2 restart + ngrok health check | |
| 110 | +├── src/ | |
| 111 | +│ ├── layouts/Base.astro | |
| 112 | +│ ├── pages/ | |
| 113 | +│ │ ├── index.astro ← gallery home (all snippets, filterable) | |
| 114 | +│ │ ├── category/[cat].astro | |
| 115 | +│ │ ├── snippet/[slug].astro ← detail page: preview + code + customizer | |
| 116 | +│ │ └── about.astro | |
| 117 | +│ ├── components/ | |
| 118 | +│ │ ├── SnippetCard.astro | |
| 119 | +│ │ ├── LivePreview.astro ← sandboxed iframe preview | |
| 120 | +│ │ ├── CodeBlock.astro ← Shiki-highlighted, copy button | |
| 121 | +│ │ ├── Customizer.astro ← color/speed/size controls → live re-render | |
| 122 | +│ │ ├── SearchBar.astro | |
| 123 | +│ │ └── TagFilter.astro | |
| 124 | +│ └── styles/global.css | |
| 125 | +├── snippets/ ← THE BANK. One file = one animation. | |
| 126 | +│ ├── loaders/ | |
| 127 | +│ ├── hover/ | |
| 128 | +│ ├── stroke-draw/ | |
| 129 | +│ ├── gauges/ | |
| 130 | +│ ├── text/ | |
| 131 | +│ ├── morph/ | |
| 132 | +│ ├── backgrounds/ | |
| 133 | +│ └── buttons/ | |
| 134 | +└── public/ | |
| 135 | + ├── favicon.svg | |
| 136 | + └── og/ ← auto-generated OG images per snippet (stretch goal) | |
| 137 | +``` | |
| 138 | + | |
| 139 | +--- | |
| 140 | + | |
| 141 | +## 5. Snippet file format (the heart of the platform) | |
| 142 | + | |
| 143 | +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. | |
| 144 | + | |
| 145 | +```html | |
| 146 | +<!-- | |
| 147 | + ============================================================ | |
| 148 | + SVGarden — https://www.svgarden.dev | |
| 149 | + Author : Simon-Pierre Boucher | |
| 150 | + Contact: contact@spboucher.ai | |
| 151 | + File : snippets/loaders/spinner-dash.html | |
| 152 | + Desc : Rotating arc loader using animated stroke-dasharray | |
| 153 | + ============================================================ | |
| 154 | +--> | |
| 155 | +<!--svgarden | |
| 156 | +title: Dash spinner | |
| 157 | +slug: spinner-dash | |
| 158 | +category: loaders | |
| 159 | +tags: [loader, dasharray, keyframes, infinite] | |
| 160 | +difficulty: beginner | |
| 161 | +techniques: [stroke-dasharray, stroke-dashoffset, "@keyframes", transform-rotate] | |
| 162 | +how_it_works: > | |
| 163 | + The outer rotation is a simple 2s linear spin. The "chasing" effect | |
| 164 | + comes from animating stroke-dasharray so the visible arc grows and | |
| 165 | + shrinks while stroke-dashoffset shifts its starting point. | |
| 166 | +customizable: | |
| 167 | + - { var: "--sg-color", label: "Color", type: color, default: "#7F77DD" } | |
| 168 | + - { var: "--sg-size", label: "Size", type: range, min: 24, max: 120, default: 48, unit: px } | |
| 169 | + - { var: "--sg-duration", label: "Speed", type: range, min: 0.5, max: 4, step: 0.1, default: 1.5, unit: s } | |
| 170 | +created: 2026-08-10 | |
| 171 | +--> | |
| 172 | +<div class="sg-spinner" style="--sg-color:#7F77DD; --sg-size:48px; --sg-duration:1.5s;"> | |
| 173 | + <svg viewBox="0 0 50 50" width="var(--sg-size)" ...>...</svg> | |
| 174 | +</div> | |
| 175 | +<style> | |
| 176 | + /* scoped: every class is prefixed sg- and unique per snippet */ | |
| 177 | +</style> | |
| 178 | +``` | |
| 179 | + | |
| 180 | +### Hard rules for snippets | |
| 181 | +1. **Self-contained**: no external fonts, images, scripts, or CSS. Inline everything. | |
| 182 | +2. **Scoped**: all class names prefixed with `sg-` + snippet slug context to avoid collisions when users paste multiple snippets in one page. | |
| 183 | +3. **Customizable via CSS custom properties** (`--sg-*`) declared on the root element — this is what powers the live Customizer. | |
| 184 | +4. **Dark/light safe**: must look good on both `#ffffff` and `#111111` backgrounds. Preview iframe offers a background toggle. | |
| 185 | +5. **No JS unless essential** (gauges/interactive snippets may use minimal vanilla JS, clearly marked with tag `js`). | |
| 186 | +6. **Accessible**: decorative SVGs get `aria-hidden="true"`; meaningful ones get `role="img"` + `<title>`. | |
| 187 | +7. **Max ~120 lines** per snippet. Elegance over bloat. | |
| 188 | + | |
| 189 | +### The build pipeline must | |
| 190 | +- Parse the `<!--svgarden ... -->` frontmatter of every file in `snippets/**`. | |
| 191 | +- Generate: the gallery index, one detail page per snippet, per-category pages, a `search-index.json` for Fuse.js, and a tag cloud. | |
| 192 | +- **Fail the build** if any snippet is missing the author header, frontmatter, or violates validation (`scripts/validate-snippets.mjs`). | |
| 193 | + | |
| 194 | +--- | |
| 195 | + | |
| 196 | +## 6. Site features (in priority order) | |
| 197 | + | |
| 198 | +### MVP (build ALL of this) | |
| 199 | +1. **Gallery home** — responsive card grid, each card shows the live animation (lazy-rendered iframe or inline with IntersectionObserver), title, category badge, tags. | |
| 200 | +2. **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. | |
| 201 | +3. **Customizer** — auto-generated controls from the `customizable` frontmatter (color pickers, range sliders). Changes update the live preview instantly AND rewrite the code block + clipboard output with the chosen values. | |
| 202 | +4. **Search & filters** — instant client-side search (title, tags, techniques) + category filter + difficulty filter. | |
| 203 | +5. **Dark/light site theme** — respects `prefers-color-scheme` with a manual toggle. | |
| 204 | + | |
| 205 | +### V2 (build after MVP is deployed and validated) | |
| 206 | +6. Keyboard navigation + `/` to focus search. | |
| 207 | +7. "Random snippet" button. | |
| 208 | +8. Per-snippet OG image generation at build time. | |
| 209 | +9. RSS/JSON feed of newly added snippets. | |
| 210 | +10. Simple analytics (self-hosted Plausible script placeholder — do NOT add third-party trackers). | |
| 211 | + | |
| 212 | +--- | |
| 213 | + | |
| 214 | +## 7. Seed content — REQUIRED example snippets | |
| 215 | + | |
| 216 | +Create **at least 24 snippets** at initial build, spread across categories. Each one fully compliant with §5. Required list: | |
| 217 | + | |
| 218 | +**loaders/** (6) | |
| 219 | +1. `spinner-dash` — rotating arc with animated dasharray | |
| 220 | +2. `dots-pulse` — three SVG circles pulsing in sequence | |
| 221 | +3. `ring-dual` — two counter-rotating arcs | |
| 222 | +4. `bar-indeterminate` — sliding indeterminate progress bar | |
| 223 | +5. `orbit-dots` — dots orbiting a center point | |
| 224 | +6. `hourglass-flip` — hourglass shape flipping with rotate keyframes | |
| 225 | + | |
| 226 | +**stroke-draw/** (4) | |
| 227 | +7. `signature-draw` — a scripted path "hand-drawing" itself (dashoffset) | |
| 228 | +8. `checkmark-pop` — animated checkmark draw + scale pop (success state) | |
| 229 | +9. `circuit-trace` — a circuit-like polyline tracing with staggered delays | |
| 230 | +10. `underline-sketch` — sketchy underline that draws on load | |
| 231 | + | |
| 232 | +**hover/** (4) | |
| 233 | +11. `star-spin` — star rotates + scales on hover | |
| 234 | +12. `icon-morph-menu` — hamburger → X on hover/click (line transforms) | |
| 235 | +13. `card-lift-border` — SVG border that draws itself around a card on hover | |
| 236 | +14. `magnetic-arrow` — arrow that nudges along its axis on hover | |
| 237 | + | |
| 238 | +**gauges/** (3) | |
| 239 | +15. `gauge-circle` — circular percentage gauge, JS slider driven (tag: js) | |
| 240 | +16. `gauge-semicircle` — semicircle speedometer style | |
| 241 | +17. `battery-fill` — battery icon with animated fill level | |
| 242 | + | |
| 243 | +**text/** (3) | |
| 244 | +18. `text-on-path` — text following a curved `<textPath>`, animated startOffset | |
| 245 | +19. `text-stroke-reveal` — outlined text that fills in via dashoffset | |
| 246 | +20. `wave-text` — letters bouncing in a wave (staggered animation-delay) | |
| 247 | + | |
| 248 | +**morph/** (2) | |
| 249 | +21. `blob-morph` — organic blob morphing between path shapes (CSS `d:` or SMIL fallback) | |
| 250 | +22. `play-pause-morph` — play ⇄ pause icon morph on click (tag: js) | |
| 251 | + | |
| 252 | +**backgrounds/** (2) | |
| 253 | +23. `wave-divider` — animated layered wave section divider | |
| 254 | +24. `dots-drift` — subtle drifting dot-grid pattern background | |
| 255 | + | |
| 256 | +Each snippet's `how_it_works` must genuinely teach the technique in 2–4 sentences. Do not copy text between snippets. | |
| 257 | + | |
| 258 | +--- | |
| 259 | + | |
| 260 | +## 8. Design system for the site itself | |
| 261 | + | |
| 262 | +- Typography: system font stack; headings weight 600, body 400. | |
| 263 | +- Layout: max-width 1200px gallery, CSS grid `repeat(auto-fill, minmax(280px, 1fr))`. | |
| 264 | +- Palette: neutral background, ONE accent color (`#7F77DD` violet), semantic greens/reds only for status. | |
| 265 | +- Cards: 1px hairline borders, 12px radius, no drop shadows, subtle hover lift via `transform: translateY(-2px)`. | |
| 266 | +- The site must score ≥ 95 on Lighthouse performance & accessibility. Verify before deploying. | |
| 267 | +- Footer on every page: `© Simon-Pierre Boucher — contact@spboucher.ai — svgarden.dev`. | |
| 268 | + | |
| 269 | +--- | |
| 270 | + | |
| 271 | +## 9. Deployment — node `m3u96b` + ngrok → www.svgarden.dev | |
| 272 | + | |
| 273 | +Target: the platform runs persistently on node **`m3u96b`** and is publicly reachable at **https://www.svgarden.dev** through an ngrok tunnel. | |
| 274 | + | |
| 275 | +### Steps to implement | |
| 276 | +1. **Build**: `npm run build` → static output in `dist/`. | |
| 277 | +2. **Serve**: minimal static server (`server.mjs`, Express or `serve`) on `127.0.0.1:4321`, with correct cache headers (`immutable` for hashed assets, `no-cache` for HTML) and gzip/brotli. | |
| 278 | +3. **ngrok**: | |
| 279 | + - 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). | |
| 280 | + - The domain `www.svgarden.dev` must 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. | |
| 281 | + - Tunnel config in `~/.config/ngrok/ngrok.yml` (create/extend via a documented block, not by overwriting): | |
| 282 | + ```yaml | |
| 283 | + tunnels: | |
| 284 | + svgarden: | |
| 285 | + proto: http | |
| 286 | + addr: 4321 | |
| 287 | + domain: www.svgarden.dev | |
| 288 | + ``` | |
| 289 | + - Start with `ngrok start svgarden`. | |
| 290 | +4. **pm2** (`ecosystem.config.cjs`): two apps — `svgarden-web` (the static server) and `svgarden-tunnel` (`ngrok start svgarden --log=stdout`). Enable `pm2 save` + startup so both survive reboots. | |
| 291 | +5. **`scripts/deploy.sh`**: `git pull` → `npm ci` → `npm run build` → `pm2 restart ecosystem.config.cjs` → curl health check on `http://127.0.0.1:4321` AND `https://www.svgarden.dev` → print status summary. | |
| 292 | +6. **Health**: add a `/healthz` route returning build timestamp + snippet count. | |
| 293 | + | |
| 294 | +### Deployment rules | |
| 295 | +- Never commit secrets, tokens, or the ngrok authtoken to the repo. `.gitignore` must cover `ngrok.yml`, `.env*`, `node_modules`, `dist`. | |
| 296 | +- If the tunnel fails (domain not reserved, auth missing), degrade gracefully: keep the local server running and print actionable instructions. | |
| 297 | + | |
| 298 | +--- | |
| 299 | + | |
| 300 | +## 10. Quality gates & workflow | |
| 301 | + | |
| 302 | +Before considering ANY task done: | |
| 303 | +1. `node scripts/validate-snippets.mjs` passes (headers ✔, frontmatter ✔, scoping ✔, size limit ✔). | |
| 304 | +2. `npm run build` completes with zero warnings. | |
| 305 | +3. Every new snippet was visually verified (describe what you checked). | |
| 306 | +4. Copy button output pasted into a blank HTML file renders correctly — test at least 3 snippets this way per session. | |
| 307 | +5. Git: conventional commits (`feat(snippets): add blob-morph`, `fix(site): ...`). Commit in small, logical units. | |
| 308 | + | |
| 309 | +### Working style | |
| 310 | +- When asked to "add N snippets", follow §5 and §7 formats exactly, pick varied techniques, and update nothing else — the build handles the rest. | |
| 311 | +- Prefer editing the build system once over hand-editing generated pages ever. | |
| 312 | +- If a requirement in this file conflicts with a user instruction in chat, the chat instruction wins — but flag the conflict explicitly. | |
| 313 | + | |
| 314 | +--- | |
| 315 | + | |
| 316 | +## 11. Roadmap summary | |
| 317 | + | |
| 318 | +| Phase | Deliverable | | |
| 319 | +|---|---| | |
| 320 | +| 1 | Repo scaffold, build pipeline, validation script, base layout | | |
| 321 | +| 2 | 24 seed snippets (§7), gallery + detail pages | | |
| 322 | +| 3 | Search, filters, customizer, copy/download, dark mode | | |
| 323 | +| 4 | Deployment on `m3u96b` + ngrok + pm2 + deploy.sh | | |
| 324 | +| 5 | V2 features (§6), grow the bank toward 100+ snippets | | |
| 325 | + | |
| 326 | +Build it beautifully. Every snippet is a small lesson; the site is the classroom. | |
| 327 | ||