SPB Git

spb/svgarden

Public

SVGarden — searchable bank of 74 self-contained SVG+CSS animation snippets (svgarden.dev)

feat(core): scaffold Astro platform — snippet parser, validation gate, server, pm2, deploy

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Aug 10, 2026) Browse files

11 changed files +7,226 −0

added .gitignore +15 −0
@@ -0,0 +1,15 @@
1 +# ============================================================
2 +# SVGarden — https://www.svgarden.dev
3 +# Author : Simon-Pierre Boucher
4 +# Contact: contact@spboucher.ai
5 +# File : .gitignore
6 +# Desc : Never commit secrets, build output, or dependencies
7 +# ============================================================
8 +node_modules/
9 +dist/
10 +.astro/
11 +.env
12 +.env.*
13 +ngrok.yml
14 +*.log
15 +.DS_Store
added README.md +96 −0
@@ -0,0 +1,96 @@
1 +# SVGarden 🌱
2 +
3 +> A massive, searchable bank of SVG + CSS animations with copy-ready code snippets.
4 +> **Production:** https://www.svgarden.dev — **Author:** Simon-Pierre Boucher — contact@spboucher.ai
5 +
6 +Every animation is one self-contained `.html` file: paste it into a blank page and it works.
7 +No frameworks, no CDNs, no build step required by the snippets themselves — and each one
8 +ships with a short "How it works" lesson.
9 +
10 +## Quick start
11 +
12 +```bash
13 +npm install
14 +npm run dev # local dev server on :4321
15 +npm run build # validate snippets + build static site into dist/
16 +npm run serve # serve dist/ with the production Express server (:4321, /healthz)
17 +```
18 +
19 +## Adding a snippet
20 +
21 +```bash
22 +npm run new # interactive scaffolder → snippets/<category>/<slug>.html
23 +npm run validate # the same gate the build runs
24 +```
25 +
26 +One file = one animation. The gallery, detail page, category pages, search index and
27 +tag filters all regenerate at build time — never edit generated pages.
28 +
29 +Snippet hard rules (enforced by `scripts/validate-snippets.mjs`, which **fails the build**):
30 +
31 +- SVGarden author header at the top (attribution travels with every copy/download);
32 +- `<!--svgarden ... -->` YAML frontmatter (title, slug, category, tags, difficulty,
33 + techniques, how_it_works, customizable, created);
34 +- self-contained (no external assets), all classes prefixed `sg-<slug>`,
35 +- customizable values as `--sg-*` custom properties declared on the root element,
36 +- ≤ 120 lines, `js` tag iff the snippet contains a `<script>`,
37 +- every `<svg>` is `aria-hidden="true"` or `role="img"` + `<title>`.
38 +
39 +## Architecture
40 +
41 +| Piece | Where |
42 +|---|---|
43 +| Snippet bank | `snippets/<category>/<slug>.html` |
44 +| Parser / single source of truth | `src/lib/snippets.mjs` |
45 +| Site (Astro, static output) | `src/pages`, `src/components`, `src/layouts` |
46 +| Search index endpoint | `src/pages/search-index.json.js` (Fuse.js, lazy-loaded client-side) |
47 +| Build info for `/healthz` | `src/pages/build-info.json.js` |
48 +| Production server | `server.mjs` (Express, gzip/brotli, immutable caching for hashed assets) |
49 +| Process manager | `ecosystem.config.cjs` (pm2: `svgarden-web` + `svgarden-tunnel`) |
50 +| Deploy | `scripts/deploy.sh` |
51 +
52 +## Deployment (node M3U96b + ngrok)
53 +
54 +The site runs on cluster node **M3U96b**, served at `127.0.0.1:4321` and exposed publicly
55 +through an ngrok tunnel at **https://www.svgarden.dev**.
56 +
57 +```bash
58 +./scripts/deploy.sh
59 +```
60 +
61 +The script syncs the source to the node (via `git pull` once a git remote exists on the
62 +node; via `rsync` until then), runs `npm ci` + `npm run build`, restarts both pm2 apps,
63 +then health-checks `http://127.0.0.1:4321/healthz` and `https://www.svgarden.dev/healthz`.
64 +
65 +### One-time ngrok requirements (operator steps)
66 +
67 +1. ngrok installed and authenticated on the node (`ngrok config add-authtoken <token>`).
68 + **Never** commit or share the token.
69 +2. `www.svgarden.dev` reserved as a **custom domain** in the ngrok dashboard
70 + (Universal Gateway → Domains).
71 +3. DNS: a `CNAME` for `www.svgarden.dev` pointing at the target ngrok gives you when
72 + reserving the domain. This step happens at your DNS provider; the deploy script
73 + degrades gracefully (local server stays up) until it propagates.
74 +4. Tunnel definition in the node's ngrok config (`~/Library/Application Support/ngrok/ngrok.yml`
75 + on macOS — add the block, don't overwrite the file):
76 +
77 +```yaml
78 +tunnels:
79 + svgarden:
80 + proto: http
81 + addr: 4321
82 + domain: www.svgarden.dev
83 +```
84 +
85 +pm2 then keeps both alive across reboots (`pm2 save` + `pm2 startup` already configured
86 +on the node): `svgarden-web` runs `server.mjs`, `svgarden-tunnel` runs
87 +`ngrok start svgarden --log=stdout`.
88 +
89 +## Quality gates
90 +
91 +1. `npm run validate` passes;
92 +2. `npm run build` completes with zero warnings;
93 +3. new snippets visually verified on light **and** dark preview backgrounds;
94 +4. copy-button output pastes into a blank `.html` file and renders correctly.
95 +
96 +© Simon-Pierre Boucher — contact@spboucher.ai — svgarden.dev
added astro.config.mjs +22 −0
@@ -0,0 +1,22 @@
1 +/**
2 + * ============================================================
3 + * SVGarden — https://www.svgarden.dev
4 + * Author : Simon-Pierre Boucher
5 + * Contact: contact@spboucher.ai
6 + * File : astro.config.mjs
7 + * Desc : Astro configuration — static output for svgarden.dev
8 + * ============================================================
9 + */
10 +import { defineConfig } from 'astro/config';
11 +
12 +export default defineConfig({
13 + site: 'https://www.svgarden.dev',
14 + output: 'static',
15 + trailingSlash: 'never',
16 + build: {
17 + format: 'file',
18 + },
19 + server: {
20 + port: 4321,
21 + },
22 +});
added ecosystem.config.cjs +29 −0
@@ -0,0 +1,29 @@
1 +/**
2 + * ============================================================
3 + * SVGarden — https://www.svgarden.dev
4 + * Author : Simon-Pierre Boucher
5 + * Contact: contact@spboucher.ai
6 + * File : ecosystem.config.cjs
7 + * Desc : pm2 config — static server (svgarden-web) + ngrok tunnel (svgarden-tunnel)
8 + * ============================================================
9 + */
10 +module.exports = {
11 + apps: [
12 + {
13 + name: 'svgarden-web',
14 + script: 'server.mjs',
15 + interpreter: 'node',
16 + env: { PORT: 4321, HOST: '127.0.0.1', NODE_ENV: 'production' },
17 + max_restarts: 10,
18 + restart_delay: 2000,
19 + },
20 + {
21 + name: 'svgarden-tunnel',
22 + script: '/opt/homebrew/bin/ngrok',
23 + interpreter: 'none',
24 + args: ['start', 'svgarden', '--log=stdout'],
25 + max_restarts: 10,
26 + restart_delay: 5000,
27 + },
28 + ],
29 +};
added package-lock.json +6533 −0
Large diff (6,533 lines) is not loaded by default to keep the page fast.Load diff
added package.json +27 −0
Large diff (27 lines) is not loaded by default to keep the page fast.Load diff
added scripts/deploy.sh +66 −0
Large diff (66 lines) is not loaded by default to keep the page fast.Load diff
added scripts/new-snippet.mjs +82 −0
Large diff (82 lines) is not loaded by default to keep the page fast.Load diff
added scripts/validate-snippets.mjs +122 −0
Large diff (122 lines) is not loaded by default to keep the page fast.Load diff
added server.mjs +65 −0
Large diff (65 lines) is not loaded by default to keep the page fast.Load diff
added src/lib/snippets.mjs +169 −0
Large diff (170 lines) is not loaded by default to keep the page fast.Load diff
Menu

Theme