readme-aura 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +192 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/components/MockupPhone.d.ts +11 -0
  7. package/dist/components/MockupPhone.js +155 -0
  8. package/dist/components/MockupPhone.js.map +1 -0
  9. package/dist/components/StatsCard.d.ts +11 -0
  10. package/dist/components/StatsCard.js +91 -0
  11. package/dist/components/StatsCard.js.map +1 -0
  12. package/dist/components/index.d.ts +2 -0
  13. package/dist/components/index.js +3 -0
  14. package/dist/components/index.js.map +1 -0
  15. package/dist/fonts.d.ts +3 -0
  16. package/dist/fonts.js +58 -0
  17. package/dist/fonts.js.map +1 -0
  18. package/dist/github.d.ts +12 -0
  19. package/dist/github.js +271 -0
  20. package/dist/github.js.map +1 -0
  21. package/dist/init.d.ts +14 -0
  22. package/dist/init.js +105 -0
  23. package/dist/init.js.map +1 -0
  24. package/dist/parser.d.ts +2 -0
  25. package/dist/parser.js +49 -0
  26. package/dist/parser.js.map +1 -0
  27. package/dist/renderer.d.ts +5 -0
  28. package/dist/renderer.js +124 -0
  29. package/dist/renderer.js.map +1 -0
  30. package/dist/templates/source-profile.d.ts +5 -0
  31. package/dist/templates/source-profile.js +30 -0
  32. package/dist/templates/source-profile.js.map +1 -0
  33. package/dist/templates/source-project.d.ts +5 -0
  34. package/dist/templates/source-project.js +27 -0
  35. package/dist/templates/source-project.js.map +1 -0
  36. package/dist/templates/workflow.d.ts +1 -0
  37. package/dist/templates/workflow.js +27 -0
  38. package/dist/templates/workflow.js.map +1 -0
  39. package/dist/types.d.ts +71 -0
  40. package/dist/types.js +2 -0
  41. package/dist/types.js.map +1 -0
  42. package/package.json +57 -0
  43. package/readme.source.md +498 -0
@@ -0,0 +1,498 @@
1
+ ```aura width=800 height=210
2
+ <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%', background: '#08080c', borderRadius: 20, padding: 30, fontFamily: 'Inter, sans-serif', position: 'relative', overflow: 'hidden', border: '1px solid rgba(110,80,220,0.18)' }}>
3
+ <style>
4
+ {`
5
+ @keyframes bn-drift-r { 0% { transform: translate(0, 0); opacity: 0.8; } 100% { transform: translate(1056px, 0); opacity: 1.05; } }
6
+ @keyframes bn-drift-l { 0% { transform: translate(0, 0); opacity: 0.75; } 100% { transform: translate(-1003px, 0); opacity: 1; } }
7
+ @keyframes bn-drift-u { 0% { transform: translate(0, 0); opacity: 0.85; } 100% { transform: translate(1000px, 0); opacity: 1; } }
8
+ @keyframes bn-pulse { 0% { transform: scale(1); opacity: 0.8; } 100% { transform: translate(-1044px, 0); opacity: 0.5; } }
9
+ #bn-tl1 { animation: bn-drift-r 0.8s infinite; }
10
+ #bn-tl2 { animation: bn-drift-l 1.0s infinite; }
11
+ #bn-tr1 { animation: bn-drift-l 0.9s infinite 0.3s; }
12
+ #bn-tr2 { animation: bn-drift-r 1.0s infinite 0.1s; }
13
+ #bn-br1 { animation: bn-drift-r 1.2s infinite 0.5s; }
14
+ #bn-br2 { animation: bn-pulse 1s infinite; }
15
+ #bn-bl1 { animation: bn-drift-l 1.3s infinite 0.2s; }
16
+ #bn-bl2 { animation: bn-drift-u 0.7s infinite 0.4s; }
17
+ `}
18
+ </style>
19
+ <svg width="800" height="180" style={{ position: 'absolute', top: 0, left: 0 }}>
20
+ <defs>
21
+ <radialGradient id="bng1" cx="50%" cy="50%" r="50%">
22
+ <stop offset="0%" stopColor="rgba(110,20,210,0.65)" />
23
+ <stop offset="70%" stopColor="rgba(110,20,210,0)" />
24
+ </radialGradient>
25
+ <radialGradient id="bng2" cx="50%" cy="50%" r="50%">
26
+ <stop offset="0%" stopColor="rgba(40,70,255,0.55)" />
27
+ <stop offset="70%" stopColor="rgba(40,70,255,0)" />
28
+ </radialGradient>
29
+ <radialGradient id="bng3" cx="50%" cy="50%" r="50%">
30
+ <stop offset="0%" stopColor="rgba(255,50,180,0.5)" />
31
+ <stop offset="70%" stopColor="rgba(255,50,180,0)" />
32
+ </radialGradient>
33
+ <radialGradient id="bng4" cx="50%" cy="50%" r="50%">
34
+ <stop offset="0%" stopColor="rgba(160,30,255,0.5)" />
35
+ <stop offset="70%" stopColor="rgba(160,30,255,0)" />
36
+ </radialGradient>
37
+ <radialGradient id="bng5" cx="50%" cy="50%" r="50%">
38
+ <stop offset="0%" stopColor="rgba(0,140,255,0.45)" />
39
+ <stop offset="70%" stopColor="rgba(0,140,255,0)" />
40
+ </radialGradient>
41
+ <radialGradient id="bng6" cx="50%" cy="50%" r="50%">
42
+ <stop offset="0%" stopColor="rgba(200,30,200,0.45)" />
43
+ <stop offset="70%" stopColor="rgba(200,30,200,0)" />
44
+ </radialGradient>
45
+ <radialGradient id="bng7" cx="50%" cy="50%" r="50%">
46
+ <stop offset="0%" stopColor="rgba(100,25,205,0.42)" />
47
+ <stop offset="70%" stopColor="rgba(100,25,205,0)" />
48
+ </radialGradient>
49
+ <radialGradient id="bng8" cx="50%" cy="50%" r="50%">
50
+ <stop offset="0%" stopColor="rgba(60,80,255,0.38)" />
51
+ <stop offset="70%" stopColor="rgba(60,80,255,0)" />
52
+ </radialGradient>
53
+ </defs>
54
+ <ellipse id="bn-tl1" cx="-100" cy="60" rx="140" ry="10" fill="url(#bng1)" />
55
+ <ellipse id="bn-tl2" cx="840" cy="50" rx="120" ry="20" fill="url(#bng2)" />
56
+ <ellipse id="bn-tr1" cx="720" cy="55" rx="130" ry="25" fill="url(#bng3)" />
57
+ <ellipse id="bn-tr2" cx="660" cy="65" rx="110" ry="15" fill="url(#bng4)" />
58
+ <ellipse id="bn-br1" cx="700" cy="130" rx="140" ry="10" fill="url(#bng5)" />
59
+ <ellipse id="bn-br2" cx="740" cy="140" rx="100" ry="10" fill="url(#bng6)" />
60
+ <ellipse id="bn-bl1" cx="100" cy="125" rx="130" ry="15" fill="url(#bng7)" />
61
+ <ellipse id="bn-bl2" cx="60" cy="135" rx="110" ry="15" fill="url(#bng8)" />
62
+ <ellipse id="bn-tl1" cx="-50" cy="60" rx="140" ry="10" fill="url(#bng1)" />
63
+ <ellipse id="bn-tl2" cx="800" cy="50" rx="120" ry="10" fill="url(#bng2)" />
64
+ <ellipse id="bn-tr1" cx="230" cy="55" rx="130" ry="25" fill="url(#bng3)" />
65
+ <ellipse id="bn-tr2" cx="400" cy="65" rx="110" ry="15" fill="url(#bng4)" />
66
+ <ellipse id="bn-br1" cx="500" cy="130" rx="140" ry="20" fill="url(#bng5)" />
67
+ <ellipse id="bn-br2" cx="40" cy="140" rx="100" ry="10" fill="url(#bng6)" />
68
+ <ellipse id="bn-bl1" cx="100" cy="125" rx="130" ry="15" fill="url(#bng7)" />
69
+ <ellipse id="bn-bl2" cx="240" cy="135" rx="110" ry="15" fill="url(#bng8)" />
70
+ </svg>
71
+ <div style={{ display: 'flex', alignItems: 'center', gap: 16, zIndex: 10 }}>
72
+ <div style={{ display: 'flex', flexDirection: 'column' }}>
73
+ <span style={{ fontSize: 32, fontWeight: 700, color: 'white', margin: 0 }}>readme-aura</span>
74
+ <span style={{ fontSize: 16, color: '#a0a0b8', marginTop: 4 }}>Next-Gen README generator for GitHub</span>
75
+ </div>
76
+ </div>
77
+ <div style={{ display: 'flex', gap: 8, marginTop: 20 }}>
78
+ <span style={{ padding: '4px 12px', background: 'rgba(12,10,20,0.6)', color: '#7eb8ff', borderRadius: 12, fontSize: 12, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>React/JSX</span>
79
+ <span style={{ padding: '4px 12px', background: 'rgba(12,10,20,0.6)', color: '#e8c8ff', borderRadius: 12, fontSize: 12, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>SVG Rendering</span>
80
+ <span style={{ padding: '4px 12px', background: 'rgba(12,10,20,0.6)', color: '#a0a0ff', borderRadius: 12, fontSize: 12, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>GitHub Actions</span>
81
+ </div>
82
+ {repo && (
83
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 14, marginTop: 16, zIndex: 10, lineHeight: 1 }}>
84
+ <span style={{ fontSize: 13, fontWeight: 300, color: '#b8860b' }}>{repo.stars} stars</span>
85
+ <span style={{ fontSize: 12, color: '#3d3d5c', fontWeight: 500 }}> | </span>
86
+ <span style={{ fontSize: 13, fontWeight: 300, color: '#8b7ec8' }}>{repo.forks} forks</span>
87
+ <span style={{ fontSize: 12, color: '#3d3d5c', fontWeight: 500 }}> | </span>
88
+ <span style={{ fontSize: 13, fontWeight: 300, color: '#5a9ca8' }}>{repo.commits} commits</span>
89
+ <span style={{ fontSize: 12, color: '#3d3d5c', fontWeight: 500 }}> | </span>
90
+ {repo.language && (
91
+ <>
92
+ <span style={{ fontSize: 13, fontWeight: 300, color: '#9ca3af', padding: '3px 10px', background: 'rgba(12,10,20,0.5)', borderRadius: 6, border: '1px solid rgba(100,80,180,0.15)' }}>{repo.language}</span>
93
+ </>
94
+ )}
95
+ </div>
96
+ )}
97
+ </div>
98
+ ```
99
+
100
+ Write custom **React/JSX components** directly inside your Markdown, and readme-aura will render them into beautiful SVGs that work on GitHub.
101
+
102
+ GitHub strips all JS and CSS from README files. This tool lets you bypass that limitation by compiling your designs into static SVG images at build time.
103
+
104
+ ## How It Works
105
+
106
+ 1. Run `npx readme-aura init` in your repo — creates workflow, source template, and audits `.gitignore`
107
+ 2. Edit `readme.source.md` — add JSX components inside `` ```aura `` code blocks
108
+ 3. Preview locally with `npx readme-aura build` — JSX gets rendered to SVG via [Vercel Satori](https://github.com/vercel/satori)
109
+ 4. Push to `main` — the GitHub Action auto-generates your `README.md`
110
+
111
+ ```aura width=800 height=220
112
+ <div style={{ display: 'flex', width: '100%', height: '100%', gap: 12, fontFamily: 'Inter, sans-serif', position: 'relative', overflow: 'hidden', background: '#08080c', borderRadius: 16, padding: 24, border: '1px solid rgba(110,80,220,0.18)' }}>
113
+ <style>
114
+ {`
115
+ @keyframes hiw-drift-r { 0%, 100% { transform: translate(0, 0); opacity: 0.8; } 50% { transform: translate(30px, -15px); opacity: 1.05; } }
116
+ @keyframes hiw-drift-l { 0%, 100% { transform: translate(0, 0); opacity: 0.75; } 50% { transform: translate(-22px, 12px); opacity: 1; } }
117
+ @keyframes hiw-drift-u { 0%, 100% { transform: translate(0, 0); opacity: 0.85; } 50% { transform: translate(18px, -18px); opacity: 1; } }
118
+ @keyframes hiw-pulse { 0%, 100% { transform: scale(1); opacity: 0.8; } 50% { transform: scale(1.18); opacity: 0.5; } }
119
+ #hiw-g1 { animation: hiw-drift-r 6.7s ease-in-out infinite; }
120
+ #hiw-g2 { animation: hiw-drift-l 8.3s ease-in-out infinite; }
121
+ #hiw-g3 { animation: hiw-drift-r 7.5s ease-in-out infinite 0.3s; }
122
+ #hiw-g4 { animation: hiw-drift-u 9.2s ease-in-out infinite 0.1s; }
123
+ #hiw-g5 { animation: hiw-drift-l 5.8s ease-in-out infinite 0.5s; }
124
+ #hiw-g6 { animation: hiw-pulse 5s ease-in-out infinite; }
125
+ #hiw-g7 { animation: hiw-drift-r 8.3s ease-in-out infinite 0.2s; }
126
+ #hiw-g8 { animation: hiw-drift-l 6.7s ease-in-out infinite 0.4s; }
127
+ `}
128
+ </style>
129
+ <svg width="800" height="220" style={{ position: 'absolute', top: 0, left: 0 }}>
130
+ <defs>
131
+ <radialGradient id="hiwg1" cx="50%" cy="50%" r="50%">
132
+ <stop offset="0%" stopColor="rgba(0,180,200,0.6)" />
133
+ <stop offset="70%" stopColor="rgba(0,180,200,0)" />
134
+ </radialGradient>
135
+ <radialGradient id="hiwg2" cx="50%" cy="50%" r="50%">
136
+ <stop offset="0%" stopColor="rgba(40,120,220,0.55)" />
137
+ <stop offset="70%" stopColor="rgba(40,120,220,0)" />
138
+ </radialGradient>
139
+ <radialGradient id="hiwg3" cx="50%" cy="50%" r="50%">
140
+ <stop offset="0%" stopColor="rgba(0,200,180,0.45)" />
141
+ <stop offset="70%" stopColor="rgba(0,200,180,0)" />
142
+ </radialGradient>
143
+ <radialGradient id="hiwg4" cx="50%" cy="50%" r="50%">
144
+ <stop offset="0%" stopColor="rgba(100,200,255,0.5)" />
145
+ <stop offset="70%" stopColor="rgba(100,200,255,0)" />
146
+ </radialGradient>
147
+ <radialGradient id="hiwg5" cx="50%" cy="50%" r="50%">
148
+ <stop offset="0%" stopColor="rgba(255,180,100,0.4)" />
149
+ <stop offset="70%" stopColor="rgba(255,180,100,0)" />
150
+ </radialGradient>
151
+ <radialGradient id="hiwg6" cx="50%" cy="50%" r="50%">
152
+ <stop offset="0%" stopColor="rgba(180,140,255,0.45)" />
153
+ <stop offset="70%" stopColor="rgba(180,140,255,0)" />
154
+ </radialGradient>
155
+ <radialGradient id="hiwg7" cx="50%" cy="50%" r="50%">
156
+ <stop offset="0%" stopColor="rgba(80,220,200,0.4)" />
157
+ <stop offset="70%" stopColor="rgba(80,220,200,0)" />
158
+ </radialGradient>
159
+ <radialGradient id="hiwg8" cx="50%" cy="50%" r="50%">
160
+ <stop offset="0%" stopColor="rgba(255,150,120,0.35)" />
161
+ <stop offset="70%" stopColor="rgba(255,150,120,0)" />
162
+ </radialGradient>
163
+ </defs>
164
+ <ellipse id="hiw-g1" cx="120" cy="180" rx="160" ry="120" fill="url(#hiwg1)" />
165
+ <ellipse id="hiw-g2" cx="180" cy="200" rx="140" ry="110" fill="url(#hiwg2)" />
166
+ <ellipse id="hiw-g3" cx="90" cy="160" rx="150" ry="100" fill="url(#hiwg3)" />
167
+ <ellipse id="hiw-g4" cx="150" cy="170" rx="110" ry="90" fill="url(#hiwg4)" />
168
+ <ellipse id="hiw-g5" cx="70" cy="190" rx="100" ry="80" fill="url(#hiwg5)" />
169
+ <ellipse id="hiw-g6" cx="200" cy="185" rx="80" ry="70" fill="url(#hiwg6)" />
170
+ <ellipse id="hiw-g7" cx="130" cy="210" rx="90" ry="75" fill="url(#hiwg7)" />
171
+ <ellipse id="hiw-g8" cx="160" cy="150" rx="70" ry="60" fill="url(#hiwg8)" />
172
+ </svg>
173
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, background: 'rgba(12,10,20,0.6)', borderRadius: 14, padding: 20, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10, justifyContent: 'center' }}>
174
+ <span style={{ fontSize: 11, color: 'rgba(220,210,255,0.7)', marginBottom: 8, textTransform: 'uppercase', letterSpacing: 1 }}>Step 1</span>
175
+ <span style={{ fontSize: 16, fontWeight: 700, color: '#ffffff', marginBottom: 8 }}>Write JSX</span>
176
+ <span style={{ fontSize: 12, color: '#7ee7ff', fontFamily: 'monospace' }}>{'```aura'}</span>
177
+ <span style={{ fontSize: 12, color: 'rgba(220,210,255,0.85)', fontFamily: 'monospace' }}>{'<Card />'}</span>
178
+ <span style={{ fontSize: 12, color: '#7ee7ff', fontFamily: 'monospace' }}>{'```'}</span>
179
+ </div>
180
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, background: 'rgba(12,10,20,0.6)', borderRadius: 14, padding: 20, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10, justifyContent: 'center' }}>
181
+ <span style={{ fontSize: 11, color: 'rgba(220,210,255,0.7)', marginBottom: 8, textTransform: 'uppercase', letterSpacing: 1 }}>Step 2</span>
182
+ <span style={{ fontSize: 16, fontWeight: 700, color: '#ffffff', marginBottom: 8 }}>Build</span>
183
+ <span style={{ fontSize: 13, color: '#9ee79e', fontFamily: 'monospace' }}>$ readme-aura build</span>
184
+ <span style={{ fontSize: 12, color: 'rgba(220,210,255,0.85)', marginTop: 8 }}>Satori renders JSX to SVG</span>
185
+ </div>
186
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, background: 'rgba(12,10,20,0.6)', borderRadius: 14, padding: 20, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10, justifyContent: 'center' }}>
187
+ <span style={{ fontSize: 11, color: 'rgba(220,210,255,0.7)', marginBottom: 8, textTransform: 'uppercase', letterSpacing: 1 }}>Step 3</span>
188
+ <span style={{ fontSize: 16, fontWeight: 700, color: '#ffffff', marginBottom: 8 }}>Deploy</span>
189
+ <span style={{ fontSize: 13, color: '#ffffff' }}>README.md with images</span>
190
+ <span style={{ fontSize: 12, color: 'rgba(220,210,255,0.85)', marginTop: 8 }}>Works on any GitHub repo</span>
191
+ </div>
192
+ </div>
193
+ ```
194
+
195
+ ## Quick Start
196
+
197
+ Run one command in your repo — it creates the GitHub Actions workflow, a starter `readme.source.md`, and ensures `.gitignore` won't block generated files:
198
+
199
+ ```bash
200
+ npx readme-aura init
201
+ ```
202
+
203
+ Then preview locally:
204
+
205
+ ```bash
206
+ npx readme-aura build
207
+ ```
208
+
209
+ That's it. Push to `main` and the workflow will auto-generate your `README.md` on every push.
210
+
211
+ > `init` auto-detects profile repos (`username/username`) and picks the right template.
212
+
213
+ ### Commands
214
+
215
+ | Command | Description |
216
+ |---------|-------------|
217
+ | `npx readme-aura init` | Scaffold workflow, source template, audit `.gitignore` |
218
+ | `npx readme-aura build` | Render `` ```aura `` blocks to SVG and generate `README.md` |
219
+
220
+ ### Build Options
221
+
222
+ | Option | Default | Description |
223
+ |--------|---------|-------------|
224
+ | `-s, --source` | `readme.source.md` | Source markdown file |
225
+ | `-o, --output` | `README.md` | Output markdown file |
226
+ | `-a, --assets` | `.github/assets` | Directory for generated SVGs |
227
+ | `-f, --fonts-dir` | — | Custom fonts directory |
228
+ | `-g, --github-user` | auto-detect | GitHub username for stats |
229
+ | `-t, --github-token` | `$GITHUB_TOKEN` | Token for GitHub API |
230
+
231
+ ### Init Options
232
+
233
+ | Option | Default | Description |
234
+ |--------|---------|-------------|
235
+ | `--template` | auto-detect | Template: `profile` or `project` |
236
+ | `--force` | `false` | Overwrite existing files |
237
+
238
+ ## What `init` Creates
239
+
240
+ The `init` command sets up everything you need:
241
+
242
+ **`.github/workflows/readme-aura.yml`** — GitHub Action that rebuilds your README on every push to `main` and on a daily schedule (to keep GitHub stats fresh):
243
+
244
+ ```yaml
245
+ name: Generate README
246
+ on:
247
+ push:
248
+ branches: [main]
249
+ paths: ['readme.source.md']
250
+ schedule:
251
+ - cron: '0 6 * * *'
252
+ workflow_dispatch:
253
+
254
+ permissions:
255
+ contents: write
256
+
257
+ jobs:
258
+ generate:
259
+ runs-on: ubuntu-latest
260
+ steps:
261
+ - uses: actions/checkout@v4
262
+
263
+ - name: Generate README
264
+ uses: collectioneur/readme-aura@main
265
+ with:
266
+ github_token: ${{ secrets.GITHUB_TOKEN }}
267
+ ```
268
+
269
+ **`readme.source.md`** — Starter template with example `` ```aura `` blocks, customized for your repo type.
270
+
271
+ **`.gitignore` audit** — Ensures `.github/assets/`, `.github/workflows/`, `README.md`, and `readme.source.md` are not ignored.
272
+
273
+ ## Features
274
+
275
+ ```aura width=800 height=160
276
+ <div style={{ display: 'flex', width: '100%', height: '100%', gap: 12, fontFamily: 'Inter, sans-serif', position: 'relative', overflow: 'hidden', background: '#08080c', borderRadius: 16, padding: 20, border: '1px solid rgba(110,80,220,0.18)' }}>
277
+ <style>
278
+ {`
279
+ @keyframes br-drift-right { 0%, 100% { transform: translate(0, 0); opacity: 0.8; } 50% { transform: translate(37px, -18px); opacity: 1.1; } }
280
+ @keyframes br-drift-left { 0%, 100% { transform: translate(0, 0); opacity: 0.75; } 50% { transform: translate(-30px, 15px); opacity: 1.05; } }
281
+ @keyframes br-drift-up { 0%, 100% { transform: translate(0, 0); opacity: 0.85; } 50% { transform: translate(22px, -22px); opacity: 1; } }
282
+ @keyframes br-pulse { 0%, 100% { transform: scale(1); opacity: 0.8; } 50% { transform: scale(1.27); opacity: 0.5; } }
283
+ #feat-glow-1 { animation: br-drift-right 6.7s ease-in-out infinite; }
284
+ #feat-glow-2 { animation: br-drift-left 8.3s ease-in-out infinite; }
285
+ #feat-glow-3 { animation: br-drift-right 7.5s ease-in-out infinite 0.3s; }
286
+ #feat-glow-4 { animation: br-drift-up 9.2s ease-in-out infinite 0.1s; }
287
+ #feat-glow-5 { animation: br-drift-left 5.8s ease-in-out infinite 0.5s; }
288
+ #feat-glow-6 { animation: br-pulse 5s ease-in-out infinite; }
289
+ #feat-glow-7 { animation: br-drift-right 8.3s ease-in-out infinite 0.2s; }
290
+ #feat-glow-8 { animation: br-drift-left 6.7s ease-in-out infinite 0.4s; }
291
+ #feat-glow-9 { animation: br-drift-up 7.5s ease-in-out infinite 0.15s; }
292
+ #feat-glow-10 { animation: br-pulse 5.8s ease-in-out infinite 0.35s; }
293
+ `}
294
+ </style>
295
+ <svg width="800" height="160" style={{ position: 'absolute', top: 0, left: 0 }}>
296
+ <defs>
297
+ <radialGradient id="fg1" cx="50%" cy="50%" r="50%">
298
+ <stop offset="0%" stopColor="rgba(110,20,210,0.65)" />
299
+ <stop offset="45%" stopColor="rgba(80,15,170,0.28)" />
300
+ <stop offset="70%" stopColor="rgba(80,15,170,0)" />
301
+ </radialGradient>
302
+ <radialGradient id="fg2" cx="50%" cy="50%" r="50%">
303
+ <stop offset="0%" stopColor="rgba(40,70,255,0.55)" />
304
+ <stop offset="45%" stopColor="rgba(20,50,200,0.22)" />
305
+ <stop offset="70%" stopColor="rgba(20,50,200,0)" />
306
+ </radialGradient>
307
+ <radialGradient id="fg3" cx="50%" cy="50%" r="50%">
308
+ <stop offset="0%" stopColor="rgba(0,140,255,0.42)" />
309
+ <stop offset="70%" stopColor="rgba(0,140,255,0)" />
310
+ </radialGradient>
311
+ <radialGradient id="fg4" cx="50%" cy="50%" r="50%">
312
+ <stop offset="0%" stopColor="rgba(0,190,230,0.35)" />
313
+ <stop offset="70%" stopColor="rgba(0,190,230,0)" />
314
+ </radialGradient>
315
+ <radialGradient id="fg5" cx="50%" cy="50%" r="50%">
316
+ <stop offset="0%" stopColor="rgba(160,30,255,0.5)" />
317
+ <stop offset="70%" stopColor="rgba(160,30,255,0)" />
318
+ </radialGradient>
319
+ <radialGradient id="fg6" cx="50%" cy="50%" r="50%">
320
+ <stop offset="0%" stopColor="rgba(255,50,180,0.5)" />
321
+ <stop offset="45%" stopColor="rgba(200,30,200,0.25)" />
322
+ <stop offset="70%" stopColor="rgba(200,30,200,0)" />
323
+ </radialGradient>
324
+ <radialGradient id="fg7" cx="50%" cy="50%" r="50%">
325
+ <stop offset="0%" stopColor="rgba(200,30,200,0.45)" />
326
+ <stop offset="70%" stopColor="rgba(200,30,200,0)" />
327
+ </radialGradient>
328
+ <radialGradient id="fg8" cx="50%" cy="50%" r="50%">
329
+ <stop offset="0%" stopColor="rgba(255,60,200,0.35)" />
330
+ <stop offset="70%" stopColor="rgba(255,60,200,0)" />
331
+ </radialGradient>
332
+ <radialGradient id="fg9" cx="50%" cy="50%" r="50%">
333
+ <stop offset="0%" stopColor="rgba(100,25,205,0.42)" />
334
+ <stop offset="70%" stopColor="rgba(100,25,205,0)" />
335
+ </radialGradient>
336
+ <radialGradient id="fg10" cx="50%" cy="50%" r="50%">
337
+ <stop offset="0%" stopColor="rgba(60,80,255,0.38)" />
338
+ <stop offset="70%" stopColor="rgba(60,80,255,0)" />
339
+ </radialGradient>
340
+ </defs>
341
+ <ellipse id="feat-glow-1" cx="620" cy="160" rx="180" ry="120" fill="url(#fg1)" />
342
+ <ellipse id="feat-glow-2" cx="720" cy="170" rx="160" ry="110" fill="url(#fg2)" />
343
+ <ellipse id="feat-glow-3" cx="580" cy="150" rx="170" ry="115" fill="url(#fg3)" />
344
+ <ellipse id="feat-glow-4" cx="650" cy="140" rx="120" ry="90" fill="url(#fg4)" />
345
+ <ellipse id="feat-glow-5" cx="750" cy="155" rx="100" ry="80" fill="url(#fg5)" />
346
+ <ellipse id="feat-glow-6" cx="680" cy="165" rx="80" ry="70" fill="url(#fg6)" />
347
+ <ellipse id="feat-glow-7" cx="600" cy="135" rx="90" ry="75" fill="url(#fg7)" />
348
+ <ellipse id="feat-glow-8" cx="690" cy="145" rx="110" ry="85" fill="url(#fg8)" />
349
+ <ellipse id="feat-glow-9" cx="640" cy="170" rx="70" ry="60" fill="url(#fg9)" />
350
+ <ellipse id="feat-glow-10" cx="730" cy="130" rx="75" ry="65" fill="url(#fg10)" />
351
+ </svg>
352
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, background: 'rgba(12,10,20,0.6)', borderRadius: 14, padding: 20, justifyContent: 'center', border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>
353
+ <span style={{ fontSize: 24, marginBottom: 8, color: '#ffffff' }}>JSX</span>
354
+ <span style={{ fontSize: 15, fontWeight: 700, color: '#ffffff' }}>React Components</span>
355
+ <span style={{ fontSize: 12, color: 'rgba(220,210,255,0.85)', marginTop: 4 }}>Write real JSX with style objects</span>
356
+ </div>
357
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, background: 'rgba(12,10,20,0.6)', borderRadius: 14, padding: 20, justifyContent: 'center', border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>
358
+ <span style={{ fontSize: 24, marginBottom: 8, color: '#ffffff' }}>SVG</span>
359
+ <span style={{ fontSize: 15, fontWeight: 700, color: '#ffffff' }}>Pixel-Perfect Output</span>
360
+ <span style={{ fontSize: 12, color: 'rgba(220,210,255,0.85)', marginTop: 4 }}>Gradients, shadows, rounded corners</span>
361
+ </div>
362
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, background: 'rgba(12,10,20,0.6)', borderRadius: 14, padding: 20, justifyContent: 'center', border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>
363
+ <span style={{ fontSize: 24, marginBottom: 8, color: '#ffffff' }}>CI</span>
364
+ <span style={{ fontSize: 15, fontWeight: 700, color: '#ffffff' }}>GitHub Actions</span>
365
+ <span style={{ fontSize: 12, color: 'rgba(220,210,255,0.85)', marginTop: 4 }}>Auto-rebuild on push</span>
366
+ </div>
367
+ </div>
368
+ ```
369
+
370
+ - **Write React/JSX** — Use familiar `style={{ }}` syntax with flexbox, gradients, shadows
371
+ - **Powered by Satori** — Vercel's engine converts JSX to SVG without a browser
372
+ - **Custom Fonts** — Inter bundled by default, bring your own via `--fonts-dir`
373
+ - **Meta Syntax** — Control dimensions: `` ```aura width=800 height=400 ``
374
+ - **GitHub-Compatible** — Output is pure Markdown + SVG, works everywhere
375
+
376
+ ## Animations
377
+
378
+ You can add **CSS-based SVG animations** using the `<style>` injection mechanism. Satori renders a static frame at build time; the browser animates the SVG when it is displayed (e.g. on GitHub).
379
+
380
+ **How it works:** Add a `<style>` block in your JSX. Define `@keyframes` and apply them to elements by `#id` or `.class`. The renderer extracts and injects the CSS into the final SVG.
381
+
382
+ **Animatable properties:** `transform` (translate, scale, rotate), `opacity`, `fill`, and `stroke-dasharray`/`stroke-dashoffset`. Layout properties (`width`, `height`, `margin`) are unreliable.
383
+
384
+ **Targeting:** Use `id` on SVG elements (`<ellipse id="glow">`, `<g id="group">`) and reference them in CSS: `#glow { animation: pulse 2s infinite; }`. Raw SVG elements preserve `id`; Satori-rendered HTML may not always preserve `className`.
385
+
386
+ ```aura width=400 height=140
387
+ <div style={{ position: 'relative', overflow: 'hidden', width: '100%', height: '100%', background: '#0a0a12', borderRadius: 12, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'Inter, sans-serif' }}>
388
+ <style>
389
+ {`
390
+ @keyframes a-pulse { 0%, 100% { opacity: 0.6; transform: scale(1); } 50% { opacity: 1; transform: scale(1.3); } }
391
+ @keyframes a-float { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(22px, -15px); } }
392
+ @keyframes a-rotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
393
+ @keyframes a-dash { 0% { stroke-dashoffset: 240; } 100% { stroke-dashoffset: 0; } }
394
+ @keyframes a-float2 { 0%, 100% { transform: translate(-18px, 12px); } 50% { transform: translate(0, 0); } }
395
+ @keyframes a-scale { 0%, 100% { opacity: 0.5; transform: scale(0.9); } 50% { opacity: 1; transform: scale(1.2); } }
396
+ #a-orb1 { animation: a-pulse 1.7s ease-in-out infinite; }
397
+ #a-orb2 { animation: a-float 2.5s ease-in-out infinite; }
398
+ #a-rect { animation: a-rotate 4s linear infinite; }
399
+ #a-path { animation: a-dash 1.5s ease-in-out infinite; }
400
+ #a-poly { animation: a-float2 3s ease-in-out infinite 0.5s; }
401
+ #a-rect2 { animation: a-scale 2.2s ease-in-out infinite 0.3s; }
402
+ `}
403
+ </style>
404
+ <svg width="400" height="140" style={{ position: 'absolute', top: 0, left: 0 }}>
405
+ <defs>
406
+ <radialGradient id="ag1" cx="50%" cy="50%" r="50%">
407
+ <stop offset="0%" stopColor="rgba(102,126,234,0.8)" />
408
+ <stop offset="70%" stopColor="rgba(102,126,234,0)" />
409
+ </radialGradient>
410
+ <radialGradient id="ag2" cx="50%" cy="50%" r="50%">
411
+ <stop offset="0%" stopColor="rgba(118,75,162,0.6)" />
412
+ <stop offset="70%" stopColor="rgba(118,75,162,0)" />
413
+ </radialGradient>
414
+ <linearGradient id="ag3" x1="0%" y1="0%" x2="100%" y2="100%">
415
+ <stop offset="0%" stopColor="rgba(102,126,234,0.5)" />
416
+ <stop offset="100%" stopColor="rgba(118,75,162,0.5)" />
417
+ </linearGradient>
418
+ </defs>
419
+ <ellipse id="a-orb1" cx="100" cy="70" rx="55" ry="40" fill="url(#ag1)" />
420
+ <ellipse id="a-orb2" cx="300" cy="70" rx="50" ry="38" fill="url(#ag2)" />
421
+ <rect id="a-rect" x="185" y="45" width="30" height="30" rx="6" fill="url(#ag3)" transform="rotate(0 200 60)" transformOrigin="200px 60px" />
422
+ <path id="a-path" d="M 60 110 Q 200 30 340 110" fill="none" stroke="rgba(150,100,255,0.6)" strokeWidth="2" strokeDasharray="120 120" strokeLinecap="round" />
423
+ <polygon id="a-poly" points="50,90 70,70 90,90 70,110" fill="rgba(255,180,220,0.4)" />
424
+ <rect id="a-rect2" x="320" y="95" width="24" height="24" rx="8" fill="rgba(100,200,255,0.5)" />
425
+ </svg>
426
+ <span style={{ fontSize: 15, fontWeight: 700, background: 'linear-gradient(90deg, #ffffff, #ffe4ec, #ffb6c1)', backgroundClip: 'text', WebkitBackgroundClip: 'text', color: 'transparent', fontFamily: 'Inter, sans-serif' }}>CSS @keyframes + SVG</span>
427
+ </div>
428
+ ```
429
+
430
+ **Limitations:** No JavaScript, no SMIL. GitHub strips scripts but supports CSS animations. Prefer `transform` and `opacity` for best compatibility.
431
+
432
+ ## Tech Stack
433
+
434
+ ```aura width=700 height=60
435
+ <div style={{ display: 'flex', gap: 10, padding: '12px 20px', width: '100%', height: '100%', background: '#08080c', borderRadius: 30, alignItems: 'center', justifyContent: 'center', fontFamily: 'Inter, sans-serif', position: 'relative', overflow: 'hidden', border: '1px solid rgba(110,80,220,0.18)' }}>
436
+ <style>
437
+ {`
438
+ @keyframes ts-drift-right { 0%, 100% { transform: translate(0, 0); opacity: 0.8; } 50% { transform: translate(27px, -9px); opacity: 1.1; } }
439
+ @keyframes ts-drift-left { 0%, 100% { transform: translate(0, 0); opacity: 0.75; } 50% { transform: translate(-22px, 12px); opacity: 1; } }
440
+ @keyframes ts-pulse { 0%, 100% { transform: scale(1); opacity: 0.8; } 50% { transform: scale(1.18); opacity: 0.5; } }
441
+ #ts-glow-1 { animation: ts-drift-right 7.5s ease-in-out infinite; }
442
+ #ts-glow-2 { animation: ts-drift-left 8.3s ease-in-out infinite; }
443
+ #ts-glow-3 { animation: ts-pulse 5.8s ease-in-out infinite; }
444
+ #ts-glow-4 { animation: ts-drift-right 6.7s ease-in-out infinite 0.2s; }
445
+ #ts-glow-5 { animation: ts-drift-left 9.2s ease-in-out infinite 0.3s; }
446
+ #ts-glow-6 { animation: ts-pulse 5s ease-in-out infinite 0.4s; }
447
+ `}
448
+ </style>
449
+ <svg width="700" height="60" style={{ position: 'absolute', top: 0, left: 0 }}>
450
+ <defs>
451
+ <radialGradient id="tsg1" cx="50%" cy="50%" r="50%">
452
+ <stop offset="0%" stopColor="rgba(110,20,210,0.6)" />
453
+ <stop offset="70%" stopColor="rgba(110,20,210,0)" />
454
+ </radialGradient>
455
+ <radialGradient id="tsg2" cx="50%" cy="50%" r="50%">
456
+ <stop offset="0%" stopColor="rgba(40,70,255,0.5)" />
457
+ <stop offset="70%" stopColor="rgba(40,70,255,0)" />
458
+ </radialGradient>
459
+ <radialGradient id="tsg3" cx="50%" cy="50%" r="50%">
460
+ <stop offset="0%" stopColor="rgba(255,50,180,0.45)" />
461
+ <stop offset="70%" stopColor="rgba(255,50,180,0)" />
462
+ </radialGradient>
463
+ <radialGradient id="tsg4" cx="50%" cy="50%" r="50%">
464
+ <stop offset="0%" stopColor="rgba(0,150,255,0.4)" />
465
+ <stop offset="70%" stopColor="rgba(0,150,255,0)" />
466
+ </radialGradient>
467
+ <radialGradient id="tsg5" cx="50%" cy="50%" r="50%">
468
+ <stop offset="0%" stopColor="rgba(160,30,255,0.45)" />
469
+ <stop offset="70%" stopColor="rgba(160,30,255,0)" />
470
+ </radialGradient>
471
+ <radialGradient id="tsg6" cx="50%" cy="50%" r="50%">
472
+ <stop offset="0%" stopColor="rgba(200,30,200,0.4)" />
473
+ <stop offset="70%" stopColor="rgba(200,30,200,0)" />
474
+ </radialGradient>
475
+ </defs>
476
+ <ellipse id="ts-glow-1" cx="450" cy="80" rx="140" ry="70" fill="url(#tsg1)" />
477
+ <ellipse id="ts-glow-2" cx="550" cy="75" rx="130" ry="65" fill="url(#tsg2)" />
478
+ <ellipse id="ts-glow-3" cx="620" cy="85" rx="120" ry="60" fill="url(#tsg3)" />
479
+ <ellipse id="ts-glow-4" cx="480" cy="65" rx="80" ry="50" fill="url(#tsg4)" />
480
+ <ellipse id="ts-glow-5" cx="590" cy="95" rx="90" ry="55" fill="url(#tsg5)" />
481
+ <ellipse id="ts-glow-6" cx="530" cy="70" rx="70" ry="45" fill="url(#tsg6)" />
482
+ </svg>
483
+ <span style={{ padding: '4px 14px', background: 'rgba(12,10,20,0.6)', color: '#7eb8ff', borderRadius: 16, fontSize: 13, fontWeight: 600, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>TypeScript</span>
484
+ <span style={{ padding: '4px 14px', background: 'rgba(12,10,20,0.6)', color: '#9ee79e', borderRadius: 16, fontSize: 13, fontWeight: 600, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>Node.js</span>
485
+ <span style={{ padding: '4px 14px', background: 'rgba(12,10,20,0.6)', color: '#e8c8ff', borderRadius: 16, fontSize: 13, fontWeight: 600, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>Satori</span>
486
+ <span style={{ padding: '4px 14px', background: 'rgba(12,10,20,0.6)', color: '#7ee7ff', borderRadius: 16, fontSize: 13, fontWeight: 600, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>React JSX</span>
487
+ <span style={{ padding: '4px 14px', background: 'rgba(12,10,20,0.6)', color: '#ffb088', borderRadius: 16, fontSize: 13, fontWeight: 600, border: '1px solid rgba(120,80,220,0.2)', zIndex: 10 }}>Unified/Remark</span>
488
+ </div>
489
+ ```
490
+
491
+ ## If You Use readme-aura
492
+
493
+ - **Add the topic:** Consider adding the [readme-aura](https://github.com/topics/readme-aura) topic to your repository so others can discover it.
494
+ - **Keep the footer:** I would appreciate it if you keep the "powered by readme-aura" footer in your README. It helps others find and try the tool.
495
+
496
+ ## License
497
+
498
+ MIT