loopwind 0.23.0 → 0.24.1

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 (497) hide show
  1. package/README.md +13 -117
  2. package/dist/cli.js +16 -10
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/add.d.ts.map +1 -1
  5. package/dist/commands/add.js +6 -10
  6. package/dist/commands/add.js.map +1 -1
  7. package/dist/commands/agent.d.ts +8 -0
  8. package/dist/commands/agent.d.ts.map +1 -0
  9. package/dist/commands/agent.js +101 -0
  10. package/dist/commands/agent.js.map +1 -0
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/init.js +17 -7
  13. package/dist/commands/init.js.map +1 -1
  14. package/dist/commands/render.d.ts.map +1 -1
  15. package/dist/commands/render.js +17 -19
  16. package/dist/commands/render.js.map +1 -1
  17. package/dist/default-templates/AGENTS.md +10 -24
  18. package/dist/lib/config.d.ts +2 -3
  19. package/dist/lib/config.d.ts.map +1 -1
  20. package/dist/lib/config.js +4 -9
  21. package/dist/lib/config.js.map +1 -1
  22. package/dist/lib/constants.d.ts +1 -3
  23. package/dist/lib/constants.d.ts.map +1 -1
  24. package/dist/lib/constants.js +2 -4
  25. package/dist/lib/constants.js.map +1 -1
  26. package/dist/lib/installer.d.ts +2 -0
  27. package/dist/lib/installer.d.ts.map +1 -1
  28. package/dist/lib/installer.js +6 -7
  29. package/dist/lib/installer.js.map +1 -1
  30. package/dist/lib/renderer.d.ts.map +1 -1
  31. package/dist/lib/renderer.js +4 -1
  32. package/dist/lib/renderer.js.map +1 -1
  33. package/dist/lib/utils.d.ts +13 -29
  34. package/dist/lib/utils.d.ts.map +1 -1
  35. package/dist/lib/utils.js +45 -155
  36. package/dist/lib/utils.js.map +1 -1
  37. package/dist/types/config.d.ts +0 -2
  38. package/dist/types/config.d.ts.map +1 -1
  39. package/dist/types/config.js.map +1 -1
  40. package/package.json +1 -15
  41. package/REGISTRY_SETUP.md +0 -363
  42. package/dist/default-templates/image/template.d.ts +0 -20
  43. package/dist/default-templates/image/template.d.ts.map +0 -1
  44. package/dist/default-templates/image/template.js +0 -18
  45. package/dist/default-templates/image/template.js.map +0 -1
  46. package/dist/default-templates/image/template.tsx +0 -20
  47. package/dist/default-templates/kitchen-sink/template.tsx +0 -64
  48. package/dist/default-templates/page/template.tsx +0 -37
  49. package/dist/default-templates/video/template.d.ts +0 -26
  50. package/dist/default-templates/video/template.d.ts.map +0 -1
  51. package/dist/default-templates/video/template.js +0 -33
  52. package/dist/default-templates/video/template.js.map +0 -1
  53. package/dist/default-templates/video/template.tsx +0 -37
  54. package/dist/default-templates/website/pages/home.tsx +0 -17
  55. package/dist/default-templates/website/parts/footer.tsx +0 -17
  56. package/dist/default-templates/website/parts/header.tsx +0 -17
  57. package/dist/default-templates/website/template.tsx +0 -17
  58. package/dist/default-templates/website-template/pages/home.tsx +0 -13
  59. package/dist/default-templates/website-template/parts/footer.tsx +0 -15
  60. package/dist/default-templates/website-template/parts/header.tsx +0 -15
  61. package/dist/default-templates/website-template/template.tsx +0 -32
  62. package/dist/lib/encode-worker.d.ts +0 -2
  63. package/dist/lib/encode-worker.d.ts.map +0 -1
  64. package/dist/lib/encode-worker.js +0 -29
  65. package/dist/lib/encode-worker.js.map +0 -1
  66. package/dist/lib/mjpeg-muxer.d.ts +0 -46
  67. package/dist/lib/mjpeg-muxer.d.ts.map +0 -1
  68. package/dist/lib/mjpeg-muxer.js +0 -513
  69. package/dist/lib/mjpeg-muxer.js.map +0 -1
  70. package/dist/lib/render-core.d.ts +0 -63
  71. package/dist/lib/render-core.d.ts.map +0 -1
  72. package/dist/lib/render-core.js +0 -65
  73. package/dist/lib/render-core.js.map +0 -1
  74. package/dist/lib/tailwind-browser.d.ts +0 -27
  75. package/dist/lib/tailwind-browser.d.ts.map +0 -1
  76. package/dist/lib/tailwind-browser.js +0 -853
  77. package/dist/lib/tailwind-browser.js.map +0 -1
  78. package/dist/lib/video-player.d.ts +0 -25
  79. package/dist/lib/video-player.d.ts.map +0 -1
  80. package/dist/lib/video-player.js +0 -392
  81. package/dist/lib/video-player.js.map +0 -1
  82. package/dist/sdk/compiler.d.ts +0 -94
  83. package/dist/sdk/compiler.d.ts.map +0 -1
  84. package/dist/sdk/compiler.js +0 -122
  85. package/dist/sdk/compiler.js.map +0 -1
  86. package/dist/sdk/edge/index.d.ts +0 -91
  87. package/dist/sdk/edge/index.d.ts.map +0 -1
  88. package/dist/sdk/edge/index.js +0 -187
  89. package/dist/sdk/edge/index.js.map +0 -1
  90. package/dist/sdk/index.d.ts +0 -62
  91. package/dist/sdk/index.d.ts.map +0 -1
  92. package/dist/sdk/index.js +0 -141
  93. package/dist/sdk/index.js.map +0 -1
  94. package/dist/sdk/preview.d.ts +0 -65
  95. package/dist/sdk/preview.d.ts.map +0 -1
  96. package/dist/sdk/preview.js +0 -262
  97. package/dist/sdk/preview.js.map +0 -1
  98. package/dist/sdk/template.d.ts +0 -162
  99. package/dist/sdk/template.d.ts.map +0 -1
  100. package/dist/sdk/template.js +0 -231
  101. package/dist/sdk/template.js.map +0 -1
  102. package/dist/sdk/workers/index.d.ts +0 -135
  103. package/dist/sdk/workers/index.d.ts.map +0 -1
  104. package/dist/sdk/workers/index.js +0 -271
  105. package/dist/sdk/workers/index.js.map +0 -1
  106. package/dist/sdk/workers/tailwind-config.d.ts +0 -48
  107. package/dist/sdk/workers/tailwind-config.d.ts.map +0 -1
  108. package/dist/sdk/workers/tailwind-config.js +0 -187
  109. package/dist/sdk/workers/tailwind-config.js.map +0 -1
  110. package/dist/sdk/workers/tailwind.d.ts +0 -9
  111. package/dist/sdk/workers/tailwind.d.ts.map +0 -1
  112. package/dist/sdk/workers/tailwind.js +0 -8
  113. package/dist/sdk/workers/tailwind.js.map +0 -1
  114. package/examples/code-editor-templates.ts +0 -173
  115. package/examples/nextjs-api/README.md +0 -180
  116. package/examples/nextjs-api/package.json +0 -21
  117. package/examples/nextjs-api/pages/api/intro-video.ts +0 -53
  118. package/examples/nextjs-api/pages/api/og-image.ts +0 -50
  119. package/examples/nextjs-template-import.ts +0 -58
  120. package/examples/sdk-video-preview.tsx +0 -120
  121. package/examples/template-compiler-workflow.ts +0 -251
  122. package/examples/visual-builder-templates.ts +0 -336
  123. package/render-examples-600x400.mjs +0 -161
  124. package/render-spring-variants-fixed.mjs +0 -60
  125. package/render-staggered-text.mjs +0 -56
  126. package/test-cloudflare-worker/README.md +0 -64
  127. package/test-cloudflare-worker/dist/README.md +0 -1
  128. package/test-cloudflare-worker/dist/index.js +0 -23743
  129. package/test-cloudflare-worker/dist/index.js.map +0 -8
  130. package/test-cloudflare-worker/package-lock.json +0 -1773
  131. package/test-cloudflare-worker/package.json +0 -25
  132. package/test-cloudflare-worker/test-sdk.mjs +0 -75
  133. package/test-cloudflare-worker/wrangler.toml +0 -14
  134. package/test-font-files.mjs +0 -72
  135. package/test-jsx-support.mjs +0 -146
  136. package/test-sdk-config.mjs +0 -454
  137. package/test-sdk-source-config.mjs +0 -427
  138. package/test-sdk-user-templates.mjs +0 -469
  139. package/test-static-debug.tsx +0 -19
  140. package/test-templates/TESTS.md +0 -63
  141. package/test-templates/config-test.mjs +0 -17
  142. package/test-templates/demo-intro-props.json +0 -4
  143. package/test-templates/run-tests.sh +0 -44
  144. package/test-templates/test-sdk.mjs +0 -139
  145. package/test-video-720p.mjs +0 -96
  146. package/test-video-breakdown.mjs +0 -98
  147. package/test-video-perf-1080.mjs +0 -67
  148. package/test-video-perf.mjs +0 -56
  149. package/test-video-props.json +0 -3
  150. package/test-worker-1080p.mjs +0 -103
  151. package/test-worker-viability.mjs +0 -140
  152. package/website/.astro/content.db +0 -0
  153. package/website/.astro/integrations/_inox-tools_astro-when/types.d.ts +0 -1
  154. package/website/.astro/integrations/astro_db/db.d.ts +0 -15
  155. package/website/.astro/settings.json +0 -5
  156. package/website/.astro/types.d.ts +0 -2
  157. package/website/DEPLOYMENT.md +0 -143
  158. package/website/OG_IMAGES.md +0 -142
  159. package/website/README.md +0 -158
  160. package/website/astro.config.mjs +0 -41
  161. package/website/dist/.gitkeep +0 -5
  162. package/website/dist/_astro/PlaygroundEditor.DzFavsm8.js +0 -26
  163. package/website/dist/_astro/VideoPreviewClient.BrajhYmh.js +0 -1
  164. package/website/dist/_astro/agents.CZXv4DCM.css +0 -1
  165. package/website/dist/_astro/client.BHSq4mdQ.js +0 -33
  166. package/website/dist/_astro/index.CTbGshLK.js +0 -9
  167. package/website/dist/_astro/jsx-runtime.BjG_zV1W.js +0 -9
  168. package/website/dist/_routes.json +0 -31
  169. package/website/dist/_worker.js/_@astrojs-ssr-adapter.mjs +0 -1033
  170. package/website/dist/_worker.js/_astro-internal_middleware.mjs +0 -40
  171. package/website/dist/_worker.js/chunks/Logo_Cud5QvBJ.mjs +0 -22
  172. package/website/dist/_worker.js/chunks/_@astro-renderers_-YVK7NHa.mjs +0 -15015
  173. package/website/dist/_worker.js/chunks/abap_BTmsHiP5.mjs +0 -1
  174. package/website/dist/_worker.js/chunks/actionscript-3_DmBelb1E.mjs +0 -1
  175. package/website/dist/_worker.js/chunks/ada_8-E0ahCN.mjs +0 -1
  176. package/website/dist/_worker.js/chunks/andromeeda_XI-CXx50.mjs +0 -1
  177. package/website/dist/_worker.js/chunks/angular-html_DKGh3gGH.mjs +0 -1
  178. package/website/dist/_worker.js/chunks/angular-ts_-qZGsJoA.mjs +0 -1
  179. package/website/dist/_worker.js/chunks/apache_ijTUt0Ee.mjs +0 -1
  180. package/website/dist/_worker.js/chunks/apex_agu1c6Sh.mjs +0 -1
  181. package/website/dist/_worker.js/chunks/apl_Bj2f7Art.mjs +0 -1
  182. package/website/dist/_worker.js/chunks/applescript_B_vXrOh3.mjs +0 -1
  183. package/website/dist/_worker.js/chunks/ara_DCEQ2rnh.mjs +0 -1
  184. package/website/dist/_worker.js/chunks/asciidoc_CGN_EkYS.mjs +0 -1
  185. package/website/dist/_worker.js/chunks/asm_BBWZgnDp.mjs +0 -1
  186. package/website/dist/_worker.js/chunks/astro/assets-service_j52rQLzU.mjs +0 -721
  187. package/website/dist/_worker.js/chunks/astro/server_CsUrSZgd.mjs +0 -2512
  188. package/website/dist/_worker.js/chunks/astro-designed-error-pages_1ELXm5Tt.mjs +0 -542
  189. package/website/dist/_worker.js/chunks/astro_Dr_hht3h.mjs +0 -1
  190. package/website/dist/_worker.js/chunks/aurora-x_9GHG8nSq.mjs +0 -1
  191. package/website/dist/_worker.js/chunks/awk_DHRvhXot.mjs +0 -1
  192. package/website/dist/_worker.js/chunks/ayu-dark_CcvqmEHE.mjs +0 -1
  193. package/website/dist/_worker.js/chunks/ballerina_C7SdeSZb.mjs +0 -1
  194. package/website/dist/_worker.js/chunks/bat_Dv4A3u45.mjs +0 -1
  195. package/website/dist/_worker.js/chunks/beancount_BfPf9Luv.mjs +0 -1
  196. package/website/dist/_worker.js/chunks/berry_B8rfM3lL.mjs +0 -1
  197. package/website/dist/_worker.js/chunks/bibtex_TcjYgtJM.mjs +0 -1
  198. package/website/dist/_worker.js/chunks/bicep_CrlFWCdN.mjs +0 -1
  199. package/website/dist/_worker.js/chunks/blade_lanKVYID.mjs +0 -1
  200. package/website/dist/_worker.js/chunks/bsl_BhppzXMB.mjs +0 -1
  201. package/website/dist/_worker.js/chunks/c_6FBALJTK.mjs +0 -1
  202. package/website/dist/_worker.js/chunks/cadence_2txU9LVE.mjs +0 -1
  203. package/website/dist/_worker.js/chunks/cairo_BkrFAIlP.mjs +0 -1
  204. package/website/dist/_worker.js/chunks/catppuccin-frappe_CkEqIYhU.mjs +0 -1
  205. package/website/dist/_worker.js/chunks/catppuccin-latte_DG4Gx_-v.mjs +0 -1
  206. package/website/dist/_worker.js/chunks/catppuccin-macchiato_Cwi3vCXf.mjs +0 -1
  207. package/website/dist/_worker.js/chunks/catppuccin-mocha_L9_OPlFX.mjs +0 -1
  208. package/website/dist/_worker.js/chunks/clarity_BEAe4Ulu.mjs +0 -1
  209. package/website/dist/_worker.js/chunks/clojure_VnUX6p2g.mjs +0 -1
  210. package/website/dist/_worker.js/chunks/cmake_0-SGkZEj.mjs +0 -1
  211. package/website/dist/_worker.js/chunks/cobol_92M_KGaE.mjs +0 -1
  212. package/website/dist/_worker.js/chunks/codeowners_CzMwskBv.mjs +0 -1
  213. package/website/dist/_worker.js/chunks/codeql_DWJZNHv1.mjs +0 -1
  214. package/website/dist/_worker.js/chunks/coffee_CQjKU2fh.mjs +0 -1
  215. package/website/dist/_worker.js/chunks/common-lisp_BBLWDpS5.mjs +0 -1
  216. package/website/dist/_worker.js/chunks/coq_hedRFV3D.mjs +0 -1
  217. package/website/dist/_worker.js/chunks/cpp_DlS1i6Zs.mjs +0 -1
  218. package/website/dist/_worker.js/chunks/crystal_D6n65fKV.mjs +0 -1
  219. package/website/dist/_worker.js/chunks/csharp_C6FCVFzc.mjs +0 -1
  220. package/website/dist/_worker.js/chunks/css_C5uJEgmJ.mjs +0 -1
  221. package/website/dist/_worker.js/chunks/csv_CtMYuuJl.mjs +0 -1
  222. package/website/dist/_worker.js/chunks/cue_BsPexqx6.mjs +0 -1
  223. package/website/dist/_worker.js/chunks/cypher_apzf6OBi.mjs +0 -1
  224. package/website/dist/_worker.js/chunks/d_DcvIRcgm.mjs +0 -1
  225. package/website/dist/_worker.js/chunks/dark-plus_C01ONtzj.mjs +0 -1
  226. package/website/dist/_worker.js/chunks/dart_WkzM5WrV.mjs +0 -1
  227. package/website/dist/_worker.js/chunks/dax_DjXAO5V4.mjs +0 -1
  228. package/website/dist/_worker.js/chunks/desktop_C92LCxdc.mjs +0 -1
  229. package/website/dist/_worker.js/chunks/diff_CVwM_9XJ.mjs +0 -1
  230. package/website/dist/_worker.js/chunks/docker_DPzgJf6Z.mjs +0 -1
  231. package/website/dist/_worker.js/chunks/dotenv_D_vgANvA.mjs +0 -1
  232. package/website/dist/_worker.js/chunks/dracula-soft_CLnUBwFm.mjs +0 -1
  233. package/website/dist/_worker.js/chunks/dracula_lBVpb6Lb.mjs +0 -1
  234. package/website/dist/_worker.js/chunks/dream-maker_DTLbzd_J.mjs +0 -1
  235. package/website/dist/_worker.js/chunks/edge_i54JYm3_.mjs +0 -1
  236. package/website/dist/_worker.js/chunks/elixir_BJCIjTu4.mjs +0 -1
  237. package/website/dist/_worker.js/chunks/elm_BbXD39-_.mjs +0 -1
  238. package/website/dist/_worker.js/chunks/emacs-lisp_pxa5cXaN.mjs +0 -1
  239. package/website/dist/_worker.js/chunks/erb_Ccjijeee.mjs +0 -1
  240. package/website/dist/_worker.js/chunks/erlang_B2VM_hi7.mjs +0 -1
  241. package/website/dist/_worker.js/chunks/everforest-dark_BxvIPBim.mjs +0 -1
  242. package/website/dist/_worker.js/chunks/everforest-light_B7VoyaJM.mjs +0 -1
  243. package/website/dist/_worker.js/chunks/fennel_D-uo7X6c.mjs +0 -1
  244. package/website/dist/_worker.js/chunks/fish_BjePoK3m.mjs +0 -1
  245. package/website/dist/_worker.js/chunks/fluent_C8fgkzLX.mjs +0 -1
  246. package/website/dist/_worker.js/chunks/fortran-fixed-form_D1pu5zrc.mjs +0 -1
  247. package/website/dist/_worker.js/chunks/fortran-free-form_CSGOhJD6.mjs +0 -1
  248. package/website/dist/_worker.js/chunks/fsharp_B0xy-A4Y.mjs +0 -1
  249. package/website/dist/_worker.js/chunks/gdresource_CWppjlHq.mjs +0 -1
  250. package/website/dist/_worker.js/chunks/gdscript_eQCHchcS.mjs +0 -1
  251. package/website/dist/_worker.js/chunks/gdshader_C4kxepX7.mjs +0 -1
  252. package/website/dist/_worker.js/chunks/genie_ACtQLcDW.mjs +0 -1
  253. package/website/dist/_worker.js/chunks/gherkin_BFp2uKUd.mjs +0 -1
  254. package/website/dist/_worker.js/chunks/git-commit_CLg9ZwMV.mjs +0 -1
  255. package/website/dist/_worker.js/chunks/git-rebase_DG8A80Nt.mjs +0 -1
  256. package/website/dist/_worker.js/chunks/github-dark-default_BI0EP2Kv.mjs +0 -1
  257. package/website/dist/_worker.js/chunks/github-dark-dimmed_a_NIC0Xb.mjs +0 -1
  258. package/website/dist/_worker.js/chunks/github-dark-high-contrast_jZGqT7hk.mjs +0 -1
  259. package/website/dist/_worker.js/chunks/github-dark_CHCDNd2O.mjs +0 -1
  260. package/website/dist/_worker.js/chunks/github-light-default_DRbOW5RG.mjs +0 -1
  261. package/website/dist/_worker.js/chunks/github-light-high-contrast_tn_kWutM.mjs +0 -1
  262. package/website/dist/_worker.js/chunks/github-light_D9brYzot.mjs +0 -1
  263. package/website/dist/_worker.js/chunks/gleam_Dmhu1oxW.mjs +0 -1
  264. package/website/dist/_worker.js/chunks/glimmer-js_BfZbXy8A.mjs +0 -1
  265. package/website/dist/_worker.js/chunks/glimmer-ts_B9QVICrD.mjs +0 -1
  266. package/website/dist/_worker.js/chunks/glsl_DD2PPwOs.mjs +0 -1
  267. package/website/dist/_worker.js/chunks/gnuplot_D2OYChUX.mjs +0 -1
  268. package/website/dist/_worker.js/chunks/go_DYGFTe3h.mjs +0 -1
  269. package/website/dist/_worker.js/chunks/graphql_B7XsT3nH.mjs +0 -1
  270. package/website/dist/_worker.js/chunks/groovy_BO12Uwkl.mjs +0 -1
  271. package/website/dist/_worker.js/chunks/hack_CB2_ztCP.mjs +0 -1
  272. package/website/dist/_worker.js/chunks/haml_CyfDcDD3.mjs +0 -1
  273. package/website/dist/_worker.js/chunks/handlebars_CfpxpWm2.mjs +0 -1
  274. package/website/dist/_worker.js/chunks/haskell_jUeC5uN5.mjs +0 -1
  275. package/website/dist/_worker.js/chunks/haxe_B6GxP1WB.mjs +0 -1
  276. package/website/dist/_worker.js/chunks/hcl_DwoHV2oh.mjs +0 -1
  277. package/website/dist/_worker.js/chunks/hjson_DV7cJRk4.mjs +0 -1
  278. package/website/dist/_worker.js/chunks/hlsl_BlFCscPI.mjs +0 -1
  279. package/website/dist/_worker.js/chunks/houston_COBFG1Mx.mjs +0 -1
  280. package/website/dist/_worker.js/chunks/html-derivative_C9pJ337h.mjs +0 -1
  281. package/website/dist/_worker.js/chunks/html_D1OkrZS5.mjs +0 -1
  282. package/website/dist/_worker.js/chunks/http_DIGXRqvJ.mjs +0 -1
  283. package/website/dist/_worker.js/chunks/hxml_DEwh9i-c.mjs +0 -1
  284. package/website/dist/_worker.js/chunks/hy_DDoIgW1K.mjs +0 -1
  285. package/website/dist/_worker.js/chunks/imba_B00zbHo4.mjs +0 -1
  286. package/website/dist/_worker.js/chunks/index_BDWR1Q-q.mjs +0 -1861
  287. package/website/dist/_worker.js/chunks/ini_D7XQA_p8.mjs +0 -1
  288. package/website/dist/_worker.js/chunks/java_B9wdFd8K.mjs +0 -1
  289. package/website/dist/_worker.js/chunks/javascript_CLsPGOON.mjs +0 -1
  290. package/website/dist/_worker.js/chunks/jinja_jarBCAN1.mjs +0 -1
  291. package/website/dist/_worker.js/chunks/jison_oGg3J708.mjs +0 -1
  292. package/website/dist/_worker.js/chunks/json5_DlZ1Kyaa.mjs +0 -1
  293. package/website/dist/_worker.js/chunks/json_DaYk_FMp.mjs +0 -1
  294. package/website/dist/_worker.js/chunks/jsonc_DlwgfSDs.mjs +0 -1
  295. package/website/dist/_worker.js/chunks/jsonl_BbCCVaZF.mjs +0 -1
  296. package/website/dist/_worker.js/chunks/jsonnet_Dt-G75xe.mjs +0 -1
  297. package/website/dist/_worker.js/chunks/jssm_BtKFTj2A.mjs +0 -1
  298. package/website/dist/_worker.js/chunks/jsx_DDx_xAZ8.mjs +0 -1
  299. package/website/dist/_worker.js/chunks/julia_CK0lv68l.mjs +0 -1
  300. package/website/dist/_worker.js/chunks/kanagawa-dragon_BldAK3Oo.mjs +0 -1
  301. package/website/dist/_worker.js/chunks/kanagawa-lotus_DVM8FX9_.mjs +0 -1
  302. package/website/dist/_worker.js/chunks/kanagawa-wave_Dpih0AKP.mjs +0 -1
  303. package/website/dist/_worker.js/chunks/kotlin_kWneB9V_.mjs +0 -1
  304. package/website/dist/_worker.js/chunks/kusto_BKVATd95.mjs +0 -1
  305. package/website/dist/_worker.js/chunks/laserwave_BqatxsVl.mjs +0 -1
  306. package/website/dist/_worker.js/chunks/latex_LVDcGBbc.mjs +0 -1
  307. package/website/dist/_worker.js/chunks/lean_W7qo-5M2.mjs +0 -1
  308. package/website/dist/_worker.js/chunks/less_DFNwJnBH.mjs +0 -1
  309. package/website/dist/_worker.js/chunks/light-plus_Dp0AoWsO.mjs +0 -1
  310. package/website/dist/_worker.js/chunks/liquid_D24qs0pc.mjs +0 -1
  311. package/website/dist/_worker.js/chunks/log_IPWMXriF.mjs +0 -1
  312. package/website/dist/_worker.js/chunks/logo_C6KaatrQ.mjs +0 -1
  313. package/website/dist/_worker.js/chunks/lua_CwnEf-T7.mjs +0 -1
  314. package/website/dist/_worker.js/chunks/luau_Br3-CXjS.mjs +0 -1
  315. package/website/dist/_worker.js/chunks/make_UBNG-kOo.mjs +0 -1
  316. package/website/dist/_worker.js/chunks/markdown_C7mhJFCm.mjs +0 -1
  317. package/website/dist/_worker.js/chunks/marko_4tchUvI7.mjs +0 -1
  318. package/website/dist/_worker.js/chunks/material-theme-darker_SKtaNEPn.mjs +0 -1
  319. package/website/dist/_worker.js/chunks/material-theme-lighter_zOX_DZCH.mjs +0 -1
  320. package/website/dist/_worker.js/chunks/material-theme-ocean_BN9WbhdC.mjs +0 -1
  321. package/website/dist/_worker.js/chunks/material-theme-palenight_DT_covjH.mjs +0 -1
  322. package/website/dist/_worker.js/chunks/material-theme_6RpeM3kc.mjs +0 -1
  323. package/website/dist/_worker.js/chunks/matlab_DCOXsPKR.mjs +0 -1
  324. package/website/dist/_worker.js/chunks/mdc_B9gb2UFP.mjs +0 -1
  325. package/website/dist/_worker.js/chunks/mdx_DGU7Nu9u.mjs +0 -1
  326. package/website/dist/_worker.js/chunks/mermaid_B69URzsZ.mjs +0 -1
  327. package/website/dist/_worker.js/chunks/min-dark_BgxifOMI.mjs +0 -1
  328. package/website/dist/_worker.js/chunks/min-light_BrPjXxUp.mjs +0 -1
  329. package/website/dist/_worker.js/chunks/mipsasm_9U-4_t7k.mjs +0 -1
  330. package/website/dist/_worker.js/chunks/mojo_B0wt7ug3.mjs +0 -1
  331. package/website/dist/_worker.js/chunks/monokai_B6Pxpoyi.mjs +0 -1
  332. package/website/dist/_worker.js/chunks/move_1eid4CyR.mjs +0 -1
  333. package/website/dist/_worker.js/chunks/narrat_Ds6-p5JZ.mjs +0 -1
  334. package/website/dist/_worker.js/chunks/nextflow_v2N1Qlqa.mjs +0 -1
  335. package/website/dist/_worker.js/chunks/nginx_Bp9Ab2NH.mjs +0 -1
  336. package/website/dist/_worker.js/chunks/night-owl_CdwOw_sc.mjs +0 -1
  337. package/website/dist/_worker.js/chunks/nim_BXGDUe53.mjs +0 -1
  338. package/website/dist/_worker.js/chunks/nix_CUig1nJH.mjs +0 -1
  339. package/website/dist/_worker.js/chunks/noop-middleware_B8fH5jha.mjs +0 -10
  340. package/website/dist/_worker.js/chunks/nord_SPoG1iae.mjs +0 -1
  341. package/website/dist/_worker.js/chunks/nushell_DJw1Lca8.mjs +0 -1
  342. package/website/dist/_worker.js/chunks/objective-c_Bktzl_CO.mjs +0 -1
  343. package/website/dist/_worker.js/chunks/objective-cpp_CP4DWdDp.mjs +0 -1
  344. package/website/dist/_worker.js/chunks/ocaml_CeEAs7bZ.mjs +0 -1
  345. package/website/dist/_worker.js/chunks/one-dark-pro_-hIwCNMi.mjs +0 -1
  346. package/website/dist/_worker.js/chunks/one-light_DSmYvJ05.mjs +0 -1
  347. package/website/dist/_worker.js/chunks/pascal_C-S_Ms_o.mjs +0 -1
  348. package/website/dist/_worker.js/chunks/perl_CKamvo15.mjs +0 -1
  349. package/website/dist/_worker.js/chunks/php_BlmcX_F3.mjs +0 -1
  350. package/website/dist/_worker.js/chunks/plastic_Ryt8tVoA.mjs +0 -1
  351. package/website/dist/_worker.js/chunks/plsql_Cb3v7cBj.mjs +0 -1
  352. package/website/dist/_worker.js/chunks/po_DZbdNRlo.mjs +0 -1
  353. package/website/dist/_worker.js/chunks/poimandres_bYmE3_5d.mjs +0 -1
  354. package/website/dist/_worker.js/chunks/polar_pJkMGwoW.mjs +0 -1
  355. package/website/dist/_worker.js/chunks/postcss_BAXSOKgk.mjs +0 -1
  356. package/website/dist/_worker.js/chunks/powerquery_oITMGN4x.mjs +0 -1
  357. package/website/dist/_worker.js/chunks/powershell_6306-xIF.mjs +0 -1
  358. package/website/dist/_worker.js/chunks/prisma_DSDxnZGz.mjs +0 -1
  359. package/website/dist/_worker.js/chunks/prolog_CxG7tjZR.mjs +0 -1
  360. package/website/dist/_worker.js/chunks/proto_CS9ByXm1.mjs +0 -1
  361. package/website/dist/_worker.js/chunks/pug_BMtLJo6U.mjs +0 -1
  362. package/website/dist/_worker.js/chunks/puppet_BfeeSzee.mjs +0 -1
  363. package/website/dist/_worker.js/chunks/purescript_BFfueNaH.mjs +0 -1
  364. package/website/dist/_worker.js/chunks/python_Cc4Faapv.mjs +0 -1
  365. package/website/dist/_worker.js/chunks/qml_C1CTJTK8.mjs +0 -1
  366. package/website/dist/_worker.js/chunks/qmldir_nG1KaqKR.mjs +0 -1
  367. package/website/dist/_worker.js/chunks/qss_Cncxk263.mjs +0 -1
  368. package/website/dist/_worker.js/chunks/r_ChR54Ihi.mjs +0 -1
  369. package/website/dist/_worker.js/chunks/racket_BDrhptDs.mjs +0 -1
  370. package/website/dist/_worker.js/chunks/raku_07OUHa0P.mjs +0 -1
  371. package/website/dist/_worker.js/chunks/razor_DIP3INLa.mjs +0 -1
  372. package/website/dist/_worker.js/chunks/red_DOPXfj-6.mjs +0 -1
  373. package/website/dist/_worker.js/chunks/reg_B64SwEDj.mjs +0 -1
  374. package/website/dist/_worker.js/chunks/regexp_ButFGoB5.mjs +0 -1
  375. package/website/dist/_worker.js/chunks/rel_BWJAWqZD.mjs +0 -1
  376. package/website/dist/_worker.js/chunks/riscv_79gXlbsF.mjs +0 -1
  377. package/website/dist/_worker.js/chunks/rose-pine-dawn_DHIjVGd3.mjs +0 -1
  378. package/website/dist/_worker.js/chunks/rose-pine-moon_t86aEbs0.mjs +0 -1
  379. package/website/dist/_worker.js/chunks/rose-pine_BHgrcDCs.mjs +0 -1
  380. package/website/dist/_worker.js/chunks/rst_D3F4Fcpj.mjs +0 -1
  381. package/website/dist/_worker.js/chunks/ruby_Cs7vM9iv.mjs +0 -1
  382. package/website/dist/_worker.js/chunks/rust_DpyRVatH.mjs +0 -1
  383. package/website/dist/_worker.js/chunks/sas_DW45xZXN.mjs +0 -1
  384. package/website/dist/_worker.js/chunks/sass_C6SiMwN_.mjs +0 -1
  385. package/website/dist/_worker.js/chunks/scala_DlZOjNZk.mjs +0 -1
  386. package/website/dist/_worker.js/chunks/scheme_D2ezSJXu.mjs +0 -1
  387. package/website/dist/_worker.js/chunks/scss_DG5Spjqu.mjs +0 -1
  388. package/website/dist/_worker.js/chunks/sdbl_ZCYaj4VN.mjs +0 -1
  389. package/website/dist/_worker.js/chunks/shaderlab_CAcRkg1_.mjs +0 -1
  390. package/website/dist/_worker.js/chunks/shellscript_BWwhkDVh.mjs +0 -1
  391. package/website/dist/_worker.js/chunks/shellsession_BfEA3juK.mjs +0 -1
  392. package/website/dist/_worker.js/chunks/slack-dark_CL3lSpCc.mjs +0 -1
  393. package/website/dist/_worker.js/chunks/slack-ochin_DdZKOQVh.mjs +0 -1
  394. package/website/dist/_worker.js/chunks/smalltalk_DgilzSui.mjs +0 -1
  395. package/website/dist/_worker.js/chunks/snazzy-light_eJU08Pz_.mjs +0 -1
  396. package/website/dist/_worker.js/chunks/solarized-dark_Dg_YQywx.mjs +0 -1
  397. package/website/dist/_worker.js/chunks/solarized-light_BnIsrA6p.mjs +0 -1
  398. package/website/dist/_worker.js/chunks/solidity_DkseH8pQ.mjs +0 -1
  399. package/website/dist/_worker.js/chunks/soy_DU7bOYoG.mjs +0 -1
  400. package/website/dist/_worker.js/chunks/sparql_BuI1DBDH.mjs +0 -1
  401. package/website/dist/_worker.js/chunks/splunk_B8Ha9Pkg.mjs +0 -1
  402. package/website/dist/_worker.js/chunks/sql_BniHwea5.mjs +0 -1
  403. package/website/dist/_worker.js/chunks/ssh-config_CkE1GuVe.mjs +0 -1
  404. package/website/dist/_worker.js/chunks/stata_Dtqpbd_l.mjs +0 -1
  405. package/website/dist/_worker.js/chunks/stylus_CXTtglzO.mjs +0 -1
  406. package/website/dist/_worker.js/chunks/svelte_BjWYcUCN.mjs +0 -1
  407. package/website/dist/_worker.js/chunks/swift_BzHql_rM.mjs +0 -1
  408. package/website/dist/_worker.js/chunks/synthwave-84_DLRNhxNA.mjs +0 -1
  409. package/website/dist/_worker.js/chunks/system-verilog_ChyInPph.mjs +0 -1
  410. package/website/dist/_worker.js/chunks/systemd_Bi9Qa2qD.mjs +0 -1
  411. package/website/dist/_worker.js/chunks/talonscript_B3sH_Y-V.mjs +0 -1
  412. package/website/dist/_worker.js/chunks/tasl_BJ5yipRs.mjs +0 -1
  413. package/website/dist/_worker.js/chunks/tcl_CoJQjNoP.mjs +0 -1
  414. package/website/dist/_worker.js/chunks/templ_CrU7Ffil.mjs +0 -1
  415. package/website/dist/_worker.js/chunks/terraform_DT9JSFpC.mjs +0 -1
  416. package/website/dist/_worker.js/chunks/tex_5PKu2yA0.mjs +0 -1
  417. package/website/dist/_worker.js/chunks/tokyo-night_Buo8OK7-.mjs +0 -1
  418. package/website/dist/_worker.js/chunks/toml_CPuXX3oc.mjs +0 -1
  419. package/website/dist/_worker.js/chunks/ts-tags_D0M_1VSH.mjs +0 -1
  420. package/website/dist/_worker.js/chunks/tsv_CuivVNot.mjs +0 -1
  421. package/website/dist/_worker.js/chunks/tsx_MkuGr8MY.mjs +0 -1
  422. package/website/dist/_worker.js/chunks/turtle_BqgEPK7f.mjs +0 -1
  423. package/website/dist/_worker.js/chunks/twig_r1G9rpYJ.mjs +0 -1
  424. package/website/dist/_worker.js/chunks/typescript_Au5buqzM.mjs +0 -1
  425. package/website/dist/_worker.js/chunks/typespec_47rhBK_z.mjs +0 -1
  426. package/website/dist/_worker.js/chunks/typst_BAtuQLh-.mjs +0 -1
  427. package/website/dist/_worker.js/chunks/v_BIvWImHg.mjs +0 -1
  428. package/website/dist/_worker.js/chunks/vala_DYEacj30.mjs +0 -1
  429. package/website/dist/_worker.js/chunks/vb_CikQuqGJ.mjs +0 -1
  430. package/website/dist/_worker.js/chunks/verilog_BQRENwI-.mjs +0 -1
  431. package/website/dist/_worker.js/chunks/vesper_DA0kvTmj.mjs +0 -1
  432. package/website/dist/_worker.js/chunks/vhdl_DHscJIyg.mjs +0 -1
  433. package/website/dist/_worker.js/chunks/viml_F2pvMwvG.mjs +0 -1
  434. package/website/dist/_worker.js/chunks/vitesse-black_D9tjNzd0.mjs +0 -1
  435. package/website/dist/_worker.js/chunks/vitesse-dark_Bnm5d0hd.mjs +0 -1
  436. package/website/dist/_worker.js/chunks/vitesse-light_CHwbyjNR.mjs +0 -1
  437. package/website/dist/_worker.js/chunks/vue-html_DyYtbbMK.mjs +0 -1
  438. package/website/dist/_worker.js/chunks/vue_DofN6juy.mjs +0 -1
  439. package/website/dist/_worker.js/chunks/vyper_CiR0m-OV.mjs +0 -1
  440. package/website/dist/_worker.js/chunks/wasm_CwIGgRGf.mjs +0 -1
  441. package/website/dist/_worker.js/chunks/wasm_jKWhg0J0.mjs +0 -1
  442. package/website/dist/_worker.js/chunks/wenyan_DKvVZKXW.mjs +0 -1
  443. package/website/dist/_worker.js/chunks/wgsl_BOWZY7yw.mjs +0 -1
  444. package/website/dist/_worker.js/chunks/wikitext_CXDhhHPy.mjs +0 -1
  445. package/website/dist/_worker.js/chunks/wolfram_ChkmGnW0.mjs +0 -1
  446. package/website/dist/_worker.js/chunks/xml_DXH3hHIu.mjs +0 -1
  447. package/website/dist/_worker.js/chunks/xsl_DuP2mFjg.mjs +0 -1
  448. package/website/dist/_worker.js/chunks/yaml_IGiEkTge.mjs +0 -1
  449. package/website/dist/_worker.js/chunks/zenscript_59iXGyNw.mjs +0 -1
  450. package/website/dist/_worker.js/chunks/zig_DKzb0zdT.mjs +0 -1
  451. package/website/dist/_worker.js/index.js +0 -61
  452. package/website/dist/_worker.js/manifest_Bk6136-u.mjs +0 -98
  453. package/website/dist/_worker.js/pages/_image.astro.mjs +0 -24
  454. package/website/dist/_worker.js/pages/agents.astro.mjs +0 -1
  455. package/website/dist/_worker.js/pages/animation.astro.mjs +0 -1
  456. package/website/dist/_worker.js/pages/api/playground/render.astro.mjs +0 -25562
  457. package/website/dist/_worker.js/pages/api/playground/templates.astro.mjs +0 -92
  458. package/website/dist/_worker.js/pages/api/raw-markdown/_---path_.astro.mjs +0 -44
  459. package/website/dist/_worker.js/pages/config.astro.mjs +0 -1
  460. package/website/dist/_worker.js/pages/fonts.astro.mjs +0 -1
  461. package/website/dist/_worker.js/pages/getting-started.astro.mjs +0 -1
  462. package/website/dist/_worker.js/pages/helpers.astro.mjs +0 -1
  463. package/website/dist/_worker.js/pages/images.astro.mjs +0 -1
  464. package/website/dist/_worker.js/pages/index.astro.mjs +0 -1
  465. package/website/dist/_worker.js/pages/llm.txt.astro.mjs +0 -1
  466. package/website/dist/_worker.js/pages/playground/_example_.astro.mjs +0 -95
  467. package/website/dist/_worker.js/pages/playground.astro.mjs +0 -1
  468. package/website/dist/_worker.js/pages/preview.astro.mjs +0 -1
  469. package/website/dist/_worker.js/pages/sdk.astro.mjs +0 -1
  470. package/website/dist/_worker.js/pages/sitemap.xml.astro.mjs +0 -1
  471. package/website/dist/_worker.js/pages/styling.astro.mjs +0 -1
  472. package/website/dist/_worker.js/pages/templates.astro.mjs +0 -1
  473. package/website/dist/_worker.js/pages/video.astro.mjs +0 -1
  474. package/website/dist/_worker.js/renderers.mjs +0 -2
  475. package/website/dist/agents/index.html +0 -54
  476. package/website/dist/animation/index.html +0 -1505
  477. package/website/dist/config/index.html +0 -186
  478. package/website/dist/fonts/index.html +0 -200
  479. package/website/dist/getting-started/index.html +0 -109
  480. package/website/dist/helpers/index.html +0 -350
  481. package/website/dist/images/index.html +0 -337
  482. package/website/dist/index.html +0 -141
  483. package/website/dist/llm.txt +0 -3626
  484. package/website/dist/playground/index.html +0 -6
  485. package/website/dist/preview/index.html +0 -113
  486. package/website/dist/robots.txt +0 -40
  487. package/website/dist/sdk/index.html +0 -1588
  488. package/website/dist/sitemap.xml +0 -76
  489. package/website/dist/styling/index.html +0 -368
  490. package/website/dist/templates/index.html +0 -76
  491. package/website/dist/video/index.html +0 -354
  492. package/website/package-lock.json +0 -8099
  493. package/website/package.json +0 -43
  494. package/website/public/.gitkeep +0 -5
  495. package/website/public/robots.txt +0 -40
  496. package/website/templates/og-image.tsx +0 -60
  497. package/website/wrangler.toml +0 -9
@@ -1,3626 +0,0 @@
1
- # loopwind - Complete Documentation for LLMs
2
-
3
- This is a comprehensive, single-file documentation for loopwind, optimized for LLM consumption.
4
- Generated: 2025-12-15T21:23:12.229Z
5
-
6
- ---
7
-
8
-
9
-
10
- ================================================================================
11
- FILE: index.mdx
12
- ================================================================================
13
-
14
- import CodeVideoDemo from '../components/CodeVideoDemo.astro';
15
-
16
- export const videoCode = `// _loopwind/templates/video-intro.tsx
17
- export const meta = {
18
- name: 'video-intro',
19
- type: 'video',
20
- size: { width: 1200, height: 676 },
21
- video: { fps: 30, duration: 3 },
22
- props: {
23
- title: 'string',
24
- subtitle: 'string'
25
- }
26
- };
27
-
28
- export default function VideoIntro({ tw, title, subtitle }) {
29
- return (
30
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 gap-4')}>
31
- <div style={tw('relative flex flex-row items-center gap-2 ease-out')}>
32
- {/* loop-ping/500: scale up + fade out every 500ms (radar effect) */}
33
- <div style={tw('w-3 h-3 bg-white rounded-full loop-ping/500')} />
34
- <div style={tw('text-white')}>Label</div>
35
- </div>
36
- {/* enter-fade-in/0/1200: fade in starting at 0ms, lasting 1200ms */}
37
- <h1 style={tw('text-7xl font-bold text-white enter-fade-in/0/1200')}>
38
- {title}
39
- </h1>
40
- {/* enter-fade-in/300/1200: fade in starting at 300ms, lasting 1200ms */}
41
- <p style={tw('text-2xl text-white/80 ease-out enter-fade-in/300/1200')}>
42
- {subtitle}
43
- </p>
44
- </div>
45
- );
46
- }
47
- `;
48
-
49
- export const imageCode = `// _loopwind/templates/og-image.tsx
50
- export const meta = {
51
- name: 'og-image',
52
- type: 'image',
53
- size: { width: 1200, height: 630 },
54
- props: {
55
- title: 'string',
56
- description: 'string'
57
- }
58
- };
59
-
60
- export default function OGImage({ tw, title, description }) {
61
- return (
62
- <div style={tw('flex w-full h-full bg-card p-16')}>
63
- <div style={tw('flex flex-col justify-between flex-1')}>
64
- <div style={tw('text-sm text-muted-foreground')}>
65
- loopwind.dev
66
- </div>
67
-
68
- <div>
69
- <h1 style={tw('text-6xl font-bold text-foreground mb-6')}>
70
- {title}
71
- </h1>
72
- <p style={tw('text-2xl text-muted-foreground')}>
73
- {description}
74
- </p>
75
- </div>
76
-
77
- <div style={tw('flex items-center gap-3')}>
78
- <span style={tw('text-5xl')}>↫</span>
79
- <span style={tw('text-3xl font-semibold bg-linear-to-r from-brand-from to-brand-to bg-clip-text text-transparent')}>
80
- loopwind
81
- </span>
82
- </div>
83
- </div>
84
- </div>
85
- );
86
- }
87
- `;
88
-
89
- ## Generate Images and Videos with Code
90
-
91
- Write React components, render them as images or videos. No design tools needed.
92
-
93
- <CodeVideoDemo
94
- code={videoCode}
95
- videoSrc="/demo-intro.mp4"
96
- title="Video Templates"
97
- />
98
-
99
- **Render it in one command:**
100
-
101
- ```bash
102
- loopwind render video-intro '{"title":"Welcome!","subtitle":"Built with loopwind"}'
103
- ```
104
-
105
- **Perfect for:** Social media intros, animated logos, product demos, tutorial overlays.
106
-
107
- ---
108
-
109
- ## Image Templates
110
-
111
- Generate Open Graph images, Twitter cards, and more:
112
-
113
- ```tsx
114
- {imageCode}
115
- ```
116
-
117
- **Render it:**
118
-
119
- ```bash
120
- loopwind render og-image '{"title":"Hello World","description":"My awesome post"}'
121
- ```
122
-
123
- **Output:** `_loopwind/outputs/og-image.png` (1200×630px)
124
-
125
- ---
126
-
127
- ## Why loopwind?
128
-
129
- ### 🎨 Beautiful by Default
130
-
131
- Built on shadcn/ui design system with semantic colors (`text-primary`, `bg-card`) and Tailwind CSS utilities. Your templates look professional out of the box.
132
-
133
- ### ⚡ Blazing Fast Rendering
134
-
135
- - **Serverless-ready**: Pure JavaScript rendering with Satori - works on Vercel, Netlify, Cloudflare Workers
136
- - **WASM-powered video**: MP4 encoding that's 12x faster than traditional approaches
137
- - **No dependencies**: No FFmpeg, no Chrome, no Docker. Just JavaScript.
138
-
139
- ### 📦 Template Marketplace (coming soon)
140
-
141
- Install templates like UI components. Browse, add, and customize in seconds:
142
-
143
- ```bash
144
- loopwind add banner-hero
145
- loopwind add video-intro
146
- loopwind add product-card
147
- ```
148
-
149
- ### 🤖 Built for AI Agents
150
-
151
- Machine-readable metadata and simple CLI make it perfect for automation:
152
-
153
- ```bash
154
- # Generate social media content programmatically
155
- loopwind render og-image '{"title":"'$TITLE'","description":"'$DESC'"}'
156
- ```
157
-
158
- ### 🚀 Framework Agnostic
159
-
160
- Works everywhere: Node.js, Bun, Deno, Python, PHP, Go. If it can run a CLI command, it can use loopwind.
161
-
162
- ---
163
-
164
- ## Key Features
165
-
166
- - ✨ **Template-based workflow** - Install and customize design templates
167
- - 🎬 **Animation classes** - `enter-fade-in/0/500`, `loop-spin/1000` - no manual calculations
168
- - 📱 **Built-in helpers** - QR codes, image embedding, video backgrounds, template composition
169
- - ✅ **Smart validation** - Automatic prop and template validation with helpful errors
170
- - 🎨 **Full Tailwind support** - Including opacity modifiers (`bg-primary/50`)
171
- - 🔧 **Zero config** - Works out of the box, customize when you need to
172
- - 📝 **Type-safe** - Full TypeScript support with prop validation
173
- - 🌐 **Edge-ready** - Runs anywhere JavaScript runs
174
-
175
- ---
176
-
177
- ## Use Cases
178
-
179
- **Marketing Automation**
180
- - Generate personalized email headers
181
- - Create dynamic social media posts
182
- - Automate Open Graph images
183
-
184
- **Content Creation**
185
- - Produce video intros and outros
186
- - Generate thumbnail variations
187
- - Create branded graphics at scale
188
-
189
- **Developer Tools**
190
- - Build dynamic README badges
191
- - Generate documentation images
192
- - Create automated reports
193
-
194
- **E-commerce**
195
- - Product card generation
196
- - Sale announcement videos
197
- - Dynamic pricing graphics
198
-
199
- ---
200
-
201
- ## Get Started
202
-
203
- ```bash
204
- npm install -g loopwind
205
- loopwind init
206
- loopwind add video-intro
207
- loopwind render video-intro '{"title":"Hello World"}'
208
- ```
209
-
210
- [View Full Documentation](/getting-started) · [Browse Templates](/templates) · [See Examples](/images)
211
-
212
- ---
213
-
214
- ## Learn More
215
-
216
- - [Getting Started Guide](/getting-started) - Installation and first steps
217
- - [Templates](/templates) - Create and customize templates
218
- - [Images](/images) - Static image generation
219
- - [Video](/video) - Animated video rendering
220
- - [Styling](/styling) - Tailwind & shadcn/ui integration
221
- - [SDK](/sdk) - Programmatic usage
222
- - [AI Agents](/agents) - Integration guide for LLMs
223
-
224
-
225
-
226
-
227
-
228
- ================================================================================
229
- FILE: templates.mdx
230
- ================================================================================
231
-
232
- # Installing Templates
233
-
234
- ## Overview
235
- loopwind supports installing templates from multiple sources:
236
-
237
- 1. **Official templates**
238
- 2. **Direct URLs**
239
-
240
- ## 1. Default templates
241
-
242
- ```bash
243
- loopwind add image-template
244
- loopwind add video-template
245
- ```
246
-
247
- ## 2. Direct URLs
248
- Install a template from any publicly accessible URL:
249
-
250
- ```bash
251
- loopwind add https://example.com/templates/my-template.json
252
- loopwind add https://cdn.example.com/templates/awesome-banner.json
253
- ```
254
-
255
- **Requirements:**
256
- - URL must return JSON in the registry template format
257
- - Must be publicly accessible (no authentication)
258
-
259
- **Example template JSON:**
260
-
261
- ```json
262
- {
263
- "name": "my-template",
264
- "version": "1.0.0",
265
- "description": "My custom template",
266
- "files": [
267
- {
268
- "path": "template.tsx",
269
- "content": "export const meta = {...};\n\nexport default function..."
270
- }
271
- ]
272
- }
273
- ```
274
-
275
- ## 3. Local Filesystem
276
-
277
- Install templates from your local filesystem:
278
-
279
- ```bash
280
- # Relative path
281
- loopwind add ./my-templates/banner-hero
282
- loopwind add ../shared-templates/product-card
283
-
284
- # Absolute path
285
- loopwind add /Users/you/templates/social-card
286
- ```
287
-
288
- **Use cases:**
289
- - Development and testing
290
- - Private templates
291
- - Shared team templates (monorepo)
292
- - Before publishing to registry
293
-
294
- ## Template Installation Directory
295
-
296
- Templates are installed to `_loopwind/templates/<template-name>/` by default.
297
-
298
- Customize this in `_loopwind/loopwind.json`:
299
-
300
- ```json title="_loopwind/loopwind.json"
301
- {
302
- "templatesDir": "my-custom-templates"
303
- }
304
- ```
305
-
306
- ## Benefits of Local Installation
307
-
308
- - **Version Control**: Templates are part of your project
309
- - **Offline**: Works without internet once installed
310
- - **Team Sharing**: Everyone on the team has the same templates
311
- - **Customization**: Fork and modify templates per project
312
- - **Consistency**: Different projects can use different template versions
313
-
314
- ## Next Steps
315
-
316
- - [Learn about Helpers](/helpers)
317
- - [Styling with Tailwind & shadcn/ui](/styling)
318
- - [Custom Fonts](/fonts)
319
- - [Image Rendering](/images)
320
- - [Video Rendering](/video)
321
-
322
-
323
-
324
-
325
-
326
- ================================================================================
327
- FILE: images.mdx
328
- ================================================================================
329
-
330
- # Image Rendering
331
-
332
- Generate beautiful images from React components using loopwind's powerful image rendering engine powered by Satori.
333
-
334
- ## Quick Start
335
-
336
- ```bash
337
- # Render an image template with inline props
338
- loopwind render banner-hero '{"title":"Hello World","subtitle":"Welcome"}'
339
-
340
- # Custom output name
341
- loopwind render banner-hero '{"title":"Hello"}' --out custom-name.png
342
-
343
- # Different format
344
- loopwind render banner-hero '{"title":"Hello"}' --format jpeg --quality 95
345
-
346
- # Or use a props file
347
- loopwind render banner-hero props.json
348
- ```
349
-
350
- ## Image Template Structure
351
-
352
- ### Basic Template
353
-
354
- ```tsx
355
- // banner-hero.tsx
356
- export const meta = {
357
- name: "banner-hero",
358
- type: "image",
359
- description: "Hero banner with gradient background",
360
- size: { width: 1600, height: 900 },
361
- props: { title: "string", subtitle: "string" }
362
- };
363
-
364
- export default function BannerHero({ title, subtitle, tw }) {
365
- return (
366
- <div style={tw('flex flex-col justify-center items-center w-full h-full bg-gradient-to-br from-purple-600 to-blue-500 p-12')}>
367
- <h1 style={tw('text-7xl font-bold text-white mb-4')}>
368
- {title}
369
- </h1>
370
- <p style={tw('text-2xl text-white/80')}>
371
- {subtitle}
372
- </p>
373
- </div>
374
- );
375
- }
376
- ```
377
-
378
- ### Props File
379
-
380
- ```json
381
- {
382
- "title": "Welcome to loopwind",
383
- "subtitle": "Generate beautiful images from React"
384
- }
385
- ```
386
-
387
- ## Output Formats
388
-
389
- ### PNG (Default)
390
-
391
- ```bash
392
- loopwind render my-template '{"title":"Hello"}'
393
- # Output: _loopwind/outputs/my-template.png
394
- ```
395
-
396
- **Best for:**
397
- - Designs with transparency
398
- - High-quality graphics
399
- - Sharp text and logos
400
- - Web graphics
401
-
402
- ### JPEG
403
-
404
- ```bash
405
- loopwind render my-template '{"title":"Hello"}' --format jpeg --quality 92
406
- ```
407
-
408
- **Options:**
409
- - `--quality` - 1-100 (default: 92)
410
-
411
- **Best for:**
412
- - Photographs
413
- - Gradients
414
- - Smaller file sizes
415
- - Social media images
416
-
417
- ### SVG
418
-
419
- ```bash
420
- loopwind render my-template '{"title":"Hello"}' --format svg
421
- ```
422
-
423
- **Best for:**
424
- - Vector graphics
425
- - Scalable designs
426
- - Print-ready artwork
427
- - Logos and icons
428
-
429
- ## Embedding Images
430
-
431
- Use the `image()` helper to embed images in your templates. It supports two modes:
432
-
433
- 1. **Prop-based** - Load from a prop value: `image('logo')`
434
- 2. **Direct file** - Load from template directory: `image('icon.svg')`
435
-
436
- ### Prop-based Images
437
-
438
- Pass the prop name to load an image path from props:
439
-
440
- ```tsx
441
- export default function ProductCard({ tw, image, title, background }) {
442
- return (
443
- <div style={tw('relative w-full h-full')}>
444
- {/* Load image from 'background' prop */}
445
- <img
446
- src={image('background')}
447
- style={tw('absolute inset-0 w-full h-full object-cover')}
448
- />
449
-
450
- {/* Content overlay */}
451
- <div style={tw('relative z-10 p-12')}>
452
- <h1 style={tw('text-6xl font-bold text-white')}>{title}</h1>
453
- </div>
454
- </div>
455
- );
456
- }
457
- ```
458
-
459
- **Props:**
460
- ```json
461
- {
462
- "title": "New Product",
463
- "background": "./images/background.jpg"
464
- }
465
- ```
466
-
467
- ### Direct File Images
468
-
469
- Load images directly from your template directory by including the file extension:
470
-
471
- ```tsx
472
- export default function ChangelogItem({ tw, image, text }) {
473
- return (
474
- <div style={tw('flex items-center gap-4')}>
475
- {/* Load check.svg from template directory */}
476
- <img
477
- src={image('check.svg')}
478
- style={tw('w-6 h-6')}
479
- />
480
- <span style={tw('text-lg')}>{text}</span>
481
- </div>
482
- );
483
- }
484
- ```
485
-
486
- This loads `check.svg` from the same directory as your template file. You can also use subdirectories:
487
-
488
- ```tsx
489
- // Load from assets subdirectory
490
- <img src={image('assets/icons/star.svg')} />
491
-
492
- // Load from shared folder
493
- <img src={image('shared/logo.png')} />
494
- ```
495
-
496
- **Template directory structure:**
497
- ```
498
- _loopwind/templates/my-template/
499
- ├── template.tsx
500
- ├── check.svg ← image('check.svg')
501
- └── assets/
502
- └── icons/
503
- └── star.svg ← image('assets/icons/star.svg')
504
- ```
505
-
506
- ### URLs
507
-
508
- The `image()` helper also supports loading images from URLs:
509
-
510
- ```json
511
- {
512
- "background": "https://example.com/image.jpg"
513
- }
514
- ```
515
-
516
- ### Supported Image Formats
517
-
518
- - ✅ **JPEG** (`.jpg`, `.jpeg`)
519
- - ✅ **PNG** (`.png`)
520
- - ✅ **GIF** (`.gif`)
521
- - ✅ **WebP** (`.webp`)
522
- - ✅ **SVG** (`.svg`)
523
-
524
- ### Image Positioning
525
-
526
- ```tsx
527
- export default function ImageGrid({ tw, image, img1, img2, img3 }) {
528
- return (
529
- <div style={tw('grid grid-cols-3 gap-4 w-full h-full p-8 bg-gray-100')}>
530
- {/* Cover - fills entire area */}
531
- <img
532
- src={image(img1)}
533
- style={tw('w-full h-full object-cover rounded-lg')}
534
- />
535
-
536
- {/* Contain - fits within area */}
537
- <img
538
- src={image(img2)}
539
- style={tw('w-full h-full object-contain')}
540
- />
541
-
542
- {/* Fill - stretches to fill */}
543
- <img
544
- src={image(img3)}
545
- style={tw('w-full h-full object-fill')}
546
- />
547
- </div>
548
- );
549
- }
550
- ```
551
-
552
- ## Common Image Templates
553
-
554
- ### Open Graph / Social Cards
555
-
556
- ```tsx
557
- // og-image.tsx
558
- export default function OGImage({ tw, title, description, image, logo }) {
559
- return (
560
- <div style={tw('flex w-full h-full bg-white')}>
561
- {/* Left side - Content */}
562
- <div style={tw('flex-1 flex flex-col justify-between p-12')}>
563
- <img src={image(logo)} style={tw('h-12 w-auto')} />
564
-
565
- <div>
566
- <h1 style={tw('text-5xl font-bold text-gray-900 mb-4')}>
567
- {title}
568
- </h1>
569
- <p style={tw('text-xl text-gray-600')}>
570
- {description}
571
- </p>
572
- </div>
573
-
574
- <p style={tw('text-gray-400')}>yoursite.com</p>
575
- </div>
576
-
577
- {/* Right side - Image */}
578
- <div style={tw('flex-1')}>
579
- <img
580
- src={image(featuredImage)}
581
- style={tw('w-full h-full object-cover')}
582
- />
583
- </div>
584
- </div>
585
- );
586
- }
587
- ```
588
-
589
- **Size:** 1200x630 (standard OG image size)
590
-
591
- ### Product Cards
592
-
593
- ```tsx
594
- export default function ProductCard({ tw, image, product, price, badge }) {
595
- return (
596
- <div style={tw('flex flex-col w-full h-full bg-white rounded-2xl overflow-hidden')}>
597
- {/* Product image */}
598
- <div style={tw('relative h-2/3')}>
599
- <img
600
- src={image(product)}
601
- style={tw('w-full h-full object-cover')}
602
- />
603
- {badge && (
604
- <div style={tw('absolute top-4 right-4 bg-red-500 text-white px-4 py-2 rounded-full text-sm font-bold')}>
605
- {badge}
606
- </div>
607
- )}
608
- </div>
609
-
610
- {/* Product info */}
611
- <div style={tw('flex-1 p-6 flex flex-col justify-between')}>
612
- <h2 style={tw('text-2xl font-bold text-gray-900')}>
613
- {title}
614
- </h2>
615
- <div style={tw('flex justify-between items-center')}>
616
- <span style={tw('text-3xl font-bold text-gray-900')}>
617
- ${price}
618
- </span>
619
- </div>
620
- </div>
621
- </div>
622
- );
623
- }
624
- ```
625
-
626
- ### Quote Cards
627
-
628
- ```tsx
629
- export default function QuoteCard({ tw, image, quote, author, avatar }) {
630
- return (
631
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-indigo-500 to-purple-600 p-16')}>
632
- {avatar && (
633
- <img
634
- src={image(avatar)}
635
- style={tw('w-24 h-24 rounded-full border-4 border-white mb-8')}
636
- />
637
- )}
638
-
639
- <blockquote style={tw('text-3xl text-white text-center font-light mb-8 max-w-3xl')}>
640
- "{quote}"
641
- </blockquote>
642
-
643
- <p style={tw('text-xl text-white/80')}>
644
- — {author}
645
- </p>
646
- </div>
647
- );
648
- }
649
- ```
650
-
651
- ## Performance Tips
652
-
653
- ### 1. Optimize Image Sizes
654
-
655
- Use appropriately sized images before embedding:
656
-
657
- ```bash
658
- # Resize large images before using them
659
- convert large-image.jpg -resize 1600x900 optimized.jpg
660
- ```
661
-
662
- ### 2. Use Web-Optimized Formats
663
-
664
- - **WebP** for modern browsers (smaller file sizes)
665
- - **JPEG** for photos with controlled quality
666
- - **PNG** only when transparency is needed
667
-
668
- ### 3. Batch Rendering
669
-
670
- Render multiple images at once:
671
-
672
- ```bash
673
- # Using a script with inline props
674
- loopwind render my-template '{"title":"Image 1"}'
675
- loopwind render my-template '{"title":"Image 2"}'
676
- loopwind render my-template '{"title":"Image 3"}'
677
-
678
- # Or loop through props files
679
- for props in props/*.json; do
680
- loopwind render my-template "$props"
681
- done
682
- ```
683
-
684
- ### 4. Cache Templates
685
-
686
- Templates are cached after first load for faster subsequent renders.
687
-
688
- ## Common Sizes
689
-
690
- ### Social Media
691
-
692
- - **Twitter/X Card**: 1200x675
693
- - **Facebook Post**: 1200x630
694
- - **Instagram Post**: 1080x1080
695
- - **LinkedIn Post**: 1200x627
696
- - **Pinterest Pin**: 1000x1500
697
-
698
- ### Web Graphics
699
-
700
- - **Hero Banner**: 1920x1080
701
- - **Blog Header**: 1600x900
702
- - **Thumbnail**: 640x360
703
- - **Avatar**: 400x400
704
-
705
- ### Print
706
-
707
- - **Flyer (A4)**: 2480x3508 (300 DPI)
708
- - **Business Card**: 1050x600 (300 DPI)
709
- - **Poster (A3)**: 3508x4961 (300 DPI)
710
-
711
- ## Troubleshooting
712
-
713
- ### Images Not Loading
714
-
715
- Check file paths are relative to the props file:
716
-
717
- ```json
718
- {
719
- "background": "./images/bg.jpg" // ✅ Relative path
720
- "background": "/images/bg.jpg" // ❌ Absolute path won't work
721
- }
722
- ```
723
-
724
- ### Blurry Text
725
-
726
- Ensure you're using appropriate dimensions. Satori renders at exact pixel dimensions.
727
-
728
- ## Next Steps
729
-
730
- - [Learn about Video Rendering](/video)
731
- - [QR Codes and Template Composition](/helpers)
732
- - [Styling with Tailwind & shadcn/ui](/styling)
733
- - [Using Custom Fonts](/fonts)
734
-
735
-
736
-
737
-
738
-
739
- ================================================================================
740
- FILE: video.mdx
741
- ================================================================================
742
-
743
- # Video Rendering
744
-
745
- Create animated videos programmatically using React components. Perfect for automated video generation, social media content, and dynamic animations.
746
-
747
- ## Quick Start
748
-
749
- ```bash
750
- # Render a video template with inline props
751
- loopwind render video-intro '{"title":"Welcome!"}' --out intro.mp4
752
-
753
- # Faster encoding with FFmpeg (requires FFmpeg installed)
754
- loopwind render video-intro '{"title":"Welcome!"}' --ffmpeg
755
-
756
- # Or use a props file
757
- loopwind render video-intro props.json --out intro.mp4
758
- ```
759
-
760
- ## Video Template Structure
761
-
762
- ### Basic Template
763
-
764
- ```tsx
765
- // video-intro.tsx
766
- export const meta = {
767
- name: "video-intro",
768
- type: "video",
769
- description: "Animated intro with bounce-in title",
770
- size: { width: 1920, height: 1080 },
771
- video: { fps: 30, duration: 3 },
772
- props: { title: "string" }
773
- };
774
-
775
- export default function VideoIntro({ tw, title }) {
776
- return (
777
- <div style={tw('flex items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
778
- {/* Bounce in from below at start */}
779
- <h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/600')}>
780
- {title}
781
- </h1>
782
- </div>
783
- );
784
- }
785
- ```
786
-
787
- ### Props File
788
-
789
- ```json
790
- {
791
- "title": "Welcome!"
792
- }
793
- ```
794
-
795
- ## Animation
796
-
797
- loopwind provides **Tailwind-style animation classes** for creating smooth video animations without writing custom code.
798
-
799
- Use declarative animation classes with millisecond-based timing:
800
-
801
- ```tsx
802
- export default function AnimatedIntro({ tw, title, subtitle }) {
803
- return (
804
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
805
- {/* Bounce in from below: starts at 0ms, lasts 400ms */}
806
- <h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/400')}>
807
- {title}
808
- </h1>
809
-
810
- {/* Fade in with upward motion: starts at 300ms, lasts 400ms */}
811
- <p style={tw('text-2xl text-white/80 mt-4 ease-out enter-fade-in-up/300/400')}>
812
- {subtitle}
813
- </p>
814
- </div>
815
- );
816
- }
817
- ```
818
-
819
- ### Common Animation Patterns
820
-
821
- ```tsx
822
- // Fade animations
823
- <div style={tw('enter-fade-in/0/500')}>Fade in</div>
824
- <div style={tw('enter-fade-in-up/0/600')}>Fade in with slide</div>
825
-
826
- // Slide animations
827
- <div style={tw('enter-slide-left/0/500')}>Slide from left</div>
828
- <div style={tw('exit-slide-up/2500/500')}>Slide out up</div>
829
-
830
- // Bounce and scale
831
- <div style={tw('enter-bounce-in/0/500')}>Bouncy entrance</div>
832
- <div style={tw('enter-scale-in/0/400')}>Scale up</div>
833
-
834
- // Loop animations (continuous)
835
- <div style={tw('loop-float/1000')}>Continuous floating</div>
836
- <div style={tw('loop-pulse/800')}>Pulsing effect</div>
837
- ```
838
-
839
- ### Utility-Based Animations
840
-
841
- Animate any transform or opacity property directly:
842
-
843
- ```tsx
844
- // Translate, rotate, scale
845
- <div style={tw('enter-translate-x-5/0/500')}>Slide 20px from left</div>
846
- <div style={tw('enter-rotate-90/0/800')}>Rotate 90 degrees</div>
847
- <div style={tw('enter-scale-100/0/500')}>Scale from 0 to 100%</div>
848
-
849
- // Combine multiple animations
850
- <div style={tw('enter-translate-y-10/0/500 enter-rotate-45/0/500')}>
851
- Fly and spin
852
- </div>
853
- ```
854
-
855
- **See the full [Animation documentation](/animation)** for all animation classes, easing functions, staggered animations, and advanced patterns.
856
-
857
- ### Programmatic Animations
858
-
859
- For complex custom animations, use the `progress` prop directly:
860
-
861
- ```tsx
862
- export default function CustomAnimation({ progress, title, tw }) {
863
- const opacity = progress < 0.3 ? progress / 0.3 : 1;
864
- const scale = 0.8 + (progress * 0.2);
865
-
866
- return (
867
- <h1 style={{
868
- ...tw('text-8xl font-bold text-white'),
869
- opacity,
870
- transform: `scale(${scale})`
871
- }}>
872
- {title}
873
- </h1>
874
- );
875
- }
876
- ```
877
-
878
- Most use cases are better served by animation classes, but `progress` and `frame` props give you complete control when needed.
879
-
880
- ## FPS and Duration
881
-
882
- ### Common Frame Rates
883
-
884
- ```tsx
885
- // Film standard
886
- export const meta = {
887
- // ...
888
- video: { fps: 24, duration: 5 }
889
- };
890
- ```
891
-
892
- ```tsx
893
- // YouTube/web standard
894
- export const meta = {
895
- // ...
896
- video: { fps: 30, duration: 3 }
897
- };
898
- ```
899
-
900
- ```tsx
901
- // Smooth animations
902
- export const meta = {
903
- // ...
904
- video: { fps: 60, duration: 2 }
905
- };
906
- ```
907
-
908
- **Total frames = fps × duration**
909
- - 24fps × 5s = 120 frames
910
- - 30fps × 3s = 90 frames
911
- - 60fps × 2s = 120 frames
912
-
913
- ### Choosing FPS
914
-
915
- - **24fps**: Cinematic look, smaller files
916
- - **30fps**: Standard web video, good balance
917
- - **60fps**: Smooth animations, larger files
918
-
919
- ## Encoding Options
920
-
921
- loopwind supports two encoding backends:
922
-
923
- ### WASM Encoder (Default)
924
-
925
- ```bash
926
- loopwind render video-intro '{"title":"Welcome"}'
927
- ```
928
-
929
- Pure JavaScript/WASM H.264 encoder. **Serverless-compatible** - works on Vercel, Cloudflare Workers, AWS Lambda, and anywhere JavaScript runs.
930
-
931
- ### FFmpeg Encoder (Faster)
932
-
933
- ```bash
934
- loopwind render video-intro '{"title":"Welcome"}' --ffmpeg
935
- ```
936
-
937
- Uses FFmpeg for **2x faster encoding** and smaller file sizes. Requires FFmpeg installed on your system.
938
-
939
- **Install FFmpeg:**
940
- ```bash
941
- # macOS
942
- brew install ffmpeg
943
-
944
- # Ubuntu/Debian
945
- sudo apt install ffmpeg
946
-
947
- # Windows
948
- winget install ffmpeg
949
- ```
950
-
951
- ### Quality Settings
952
-
953
- ```bash
954
- # Higher quality (lower CRF = better quality, default: 23)
955
- loopwind render video-intro '{"title":"Welcome"}' --crf 18
956
-
957
- # Lower quality, smaller files
958
- loopwind render video-intro '{"title":"Welcome"}' --crf 28
959
- ```
960
-
961
- ### When to Use Each
962
-
963
- | Encoder | Use Case |
964
- |---------|----------|
965
- | **WASM (default)** | Serverless, CI/CD, no dependencies needed |
966
- | **FFmpeg (`--ffmpeg`)** | Local development, faster iteration, smaller files |
967
-
968
- ## Performance
969
-
970
- ### Rendering Speed
971
-
972
- On an M1 Mac:
973
- - **30fps, 3s video** (90 frames): ~15-20 seconds
974
- - **60fps, 3s video** (180 frames): ~30-40 seconds
975
-
976
- Speed depends on:
977
- - Template complexity
978
- - Image/video embedding
979
- - CPU performance
980
- - Frame count
981
-
982
- ### Optimization Tips
983
-
984
- 1. **Lower FPS for previews**: Test at 15fps, render final at 30fps
985
- 2. **Shorter duration**: Preview with 1s, expand for final
986
- 3. **Simplify templates**: Complex layouts take longer
987
- 4. **Pre-optimize media**: Resize images/videos before embedding
988
-
989
- ### Memory Usage
990
-
991
- Video frames are cached in memory during rendering:
992
- - **1920x1080 @ 30fps for 3s**: ~500MB RAM
993
- - **1920x1080 @ 60fps for 5s**: ~1.7GB RAM
994
-
995
- For long videos, consider rendering in segments.
996
-
997
- ## Common Video Templates
998
-
999
- ### Loading Spinner
1000
-
1001
- ```tsx
1002
- export default function LoadingVideo({ tw, title }) {
1003
- return (
1004
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gray-900')}>
1005
- {/* Spinner fades out at 60% (1800ms for 3s video) */}
1006
- <div style={tw('exit-fade-out/1800/400')}>
1007
- <div style={tw('w-32 h-32 border-8 border-blue-500 border-t-transparent rounded-full loop-spin/1000')} />
1008
- </div>
1009
-
1010
- {/* Title fades in after spinner */}
1011
- <h1 style={tw('text-6xl font-bold text-white mt-12 ease-out enter-fade-in-up/1800/400')}>
1012
- {title}
1013
- </h1>
1014
- </div>
1015
- );
1016
- }
1017
- ```
1018
-
1019
- ### Progress Bar
1020
-
1021
- ```tsx
1022
- export default function ProgressVideo({ tw, title, subtitle }) {
1023
- return (
1024
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gray-900 p-12')}>
1025
- {/* Title bounces in */}
1026
- <h2 style={tw('text-5xl font-bold text-white mb-12 ease-out enter-bounce-in/0/500')}>
1027
- {title}
1028
- </h2>
1029
-
1030
- {/* Progress bar container fades in */}
1031
- <div style={tw('w-full max-w-2xl h-4 bg-gray-700 rounded-full overflow-hidden ease-out enter-fade-in/300/400')}>
1032
- <div style={tw('h-full bg-gradient-to-r from-blue-500 to-green-500 w-full')} />
1033
- </div>
1034
-
1035
- {/* Subtitle fades in last */}
1036
- <p style={tw('text-2xl text-gray-400 mt-8 ease-out enter-fade-in/600/400')}>
1037
- {subtitle}
1038
- </p>
1039
- </div>
1040
- );
1041
- }
1042
- ```
1043
-
1044
- ### Countdown Timer
1045
-
1046
- ```tsx
1047
- export default function CountdownVideo({ tw, frame, message }) {
1048
- const fps = 30;
1049
- const secondsRemaining = Math.ceil((90 - frame) / fps); // 3s countdown
1050
-
1051
- return (
1052
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-black')}>
1053
- {/* Number scales in and pulses continuously */}
1054
- <div style={tw('text-[200px] font-black text-white ease-out enter-scale-in/0/400 loop-pulse/1000')}>
1055
- {secondsRemaining}
1056
- </div>
1057
-
1058
- {/* Message fades in */}
1059
- <p style={tw('text-3xl text-gray-400 mt-8 ease-out enter-fade-in/400/500')}>
1060
- {message}
1061
- </p>
1062
- </div>
1063
- );
1064
- }
1065
- ```
1066
-
1067
- ## Output Formats
1068
-
1069
- Videos can be rendered in two formats:
1070
-
1071
- ### MP4 (Default)
1072
-
1073
- ```bash
1074
- # MP4 - H.264 codec, smaller file size, best quality
1075
- loopwind render video-intro '{"title":"Welcome"}' --out intro.mp4
1076
-
1077
- # With custom quality (lower CRF = better quality)
1078
- loopwind render video-intro '{"title":"Welcome"}' --crf 18
1079
- ```
1080
-
1081
- **Benefits:**
1082
- - Smaller file sizes
1083
- - Better color reproduction
1084
- - Universal playback support
1085
- - Best for social media, websites
1086
-
1087
- ### GIF
1088
-
1089
- ```bash
1090
- # GIF - animated, works everywhere
1091
- loopwind render video-intro '{"title":"Welcome"}' --format gif --out intro.gif
1092
-
1093
- # Or just use .gif extension
1094
- loopwind render video-intro '{"title":"Welcome"}' --out intro.gif
1095
- ```
1096
-
1097
- **Benefits:**
1098
- - Works in emails, GitHub READMEs, Slack
1099
- - No video player needed
1100
- - Auto-loops by default
1101
- - Great for short animations
1102
-
1103
- **Limitations:**
1104
- - Limited to 256 colors per frame
1105
- - Larger file sizes than MP4
1106
- - Some color banding with gradients
1107
-
1108
- **When to use GIF:**
1109
- - Short loops (< 5 seconds)
1110
- - Simple animations with solid colors
1111
- - Platforms that don't support video (email, GitHub)
1112
-
1113
- **When to use MP4:**
1114
- - Longer videos
1115
- - Complex gradients or photos
1116
- - Best quality and smallest size
1117
-
1118
- ## Video-Specific Props
1119
-
1120
- Every video template receives these additional props:
1121
-
1122
- ### `frame`
1123
- Current frame number (0 to totalFrames - 1)
1124
-
1125
- ```tsx
1126
- export default function MyVideo({ frame }) {
1127
- // Frame 0, 1, 2, 3, ... 89 (for 3s @ 30fps)
1128
- return <div>Frame: {frame}</div>;
1129
- }
1130
- ```
1131
-
1132
- ### `progress`
1133
- Animation progress from 0 to 1
1134
-
1135
- ```tsx
1136
- export default function MyVideo({ progress }) {
1137
- // 0.0 at start, 0.5 at middle, 1.0 at end
1138
- const x = progress * 100; // Move from 0 to 100
1139
-
1140
- return (
1141
- <div style={{ transform: `translateX(${x}px)` }}>
1142
- Moving element
1143
- </div>
1144
- );
1145
- }
1146
- ```
1147
-
1148
- ## Next Steps
1149
-
1150
- - [Animation - Complete animation guide](/animation)
1151
- - [Image Rendering](/images)
1152
- - [Helpers - QR Codes and Template Composition](/helpers)
1153
- - [Styling with Tailwind & shadcn/ui](/styling)
1154
- - [Custom Fonts in Videos](/fonts)
1155
-
1156
-
1157
-
1158
-
1159
-
1160
- ================================================================================
1161
- FILE: animation.mdx
1162
- ================================================================================
1163
-
1164
- # Animation
1165
-
1166
- loopwind provides **Tailwind-style animation classes** that work with time to create smooth video animations without writing custom code.
1167
-
1168
- > **Note:** Animation classes only work with **video templates** and **GIFs**. For static images, animations will have no effect since there's no time context.
1169
-
1170
- ## Quick Start
1171
-
1172
- ```tsx
1173
- export default function MyVideo({ tw, title, subtitle }) {
1174
- return (
1175
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-black')}>
1176
- {/* Bounce in from below: starts at 0, lasts 400ms */}
1177
- <h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/400')}>
1178
- {title}
1179
- </h1>
1180
-
1181
- {/* Fade in with upward motion: starts at 300ms, lasts 400ms */}
1182
- <p style={tw('text-2xl text-white/80 mt-4 ease-out enter-fade-in-up/300/400')}>
1183
- {subtitle}
1184
- </p>
1185
-
1186
- {/* Continuous floating animation: repeats every 1s (1000ms) */}
1187
- <div style={tw('mt-8 text-4xl loop-float/1000')}>
1188
- ⬇️
1189
- </div>
1190
- </div>
1191
- );
1192
- }
1193
- ```
1194
-
1195
- ## Animation Format
1196
-
1197
- loopwind uses three types of animations with **millisecond timing**:
1198
-
1199
- | Type | Format | Description |
1200
- |------|--------|-------------|
1201
- | Enter | `enter-{type}/{start}/{duration}` | Animations that play when entering |
1202
- | Exit | `exit-{type}/{start}/{duration}` | Animations that play when exiting |
1203
- | Loop | `loop-{type}/{duration}` | Continuous looping animations |
1204
-
1205
- All timing values are in **milliseconds** (1000ms = 1 second).
1206
-
1207
- ## Utility-Based Animations
1208
-
1209
- In addition to predefined animations, loopwind supports **Tailwind utility-based animations** that let you animate any transform or opacity property directly:
1210
-
1211
- ```tsx
1212
- // Slide in 20px from the left
1213
- <div style={tw('enter-translate-x-5/0/1000')}>Content</div>
1214
-
1215
- // Rotate 90 degrees on entrance
1216
- <div style={tw('enter-rotate-90/0/500')}>Spinning</div>
1217
-
1218
- // Fade to 50% opacity in a loop
1219
- <div style={tw('loop-opacity-50/1000')}>Pulsing</div>
1220
-
1221
- // Scale down with negative value
1222
- <div style={tw('enter--scale-50/0/800')}>Shrinking</div>
1223
- ```
1224
-
1225
- ### Supported Utilities
1226
-
1227
- | Utility | Format | Description | Example |
1228
- |---------|--------|-------------|---------|
1229
- | **translate-x** | `enter-translate-x-{value}` | Translate horizontally | `enter-translate-x-5` = 20px<br/>`enter-translate-x-full` = 100%<br/>`enter-translate-x-[20px]` = 20px |
1230
- | **translate-y** | `enter-translate-y-{value}` | Translate vertically | `loop-translate-y-10` = 40px<br/>`enter-translate-y-1/2` = 50%<br/>`enter-translate-y-[5rem]` = 80px |
1231
- | **opacity** | `enter-opacity-{n}` | Set opacity (0-100) | `enter-opacity-50` = 50% |
1232
- | **scale** | `enter-scale-{n}` | Scale element (0-200) | `enter-scale-100` = 1.0x |
1233
- | **rotate** | `enter-rotate-{n}` | Rotate in degrees | `enter-rotate-45` = 45° |
1234
- | **skew-x** | `enter-skew-x-{n}` | Skew on X axis in degrees | `enter-skew-x-12` = 12° |
1235
- | **skew-y** | `enter-skew-y-{n}` | Skew on Y axis in degrees | `exit-skew-y-6` = 6° |
1236
-
1237
- **Translate value formats:**
1238
- - **Numeric**: `5` = 20px (Tailwind spacing scale: 1 unit = 4px)
1239
- - **Keywords**: `full` = 100%
1240
- - **Fractions**: `1/2` = 50%, `1/3` = 33.333%, `2/3` = 66.666%, etc.
1241
- - **Arbitrary values**: `[20px]`, `[5rem]`, `[10%]` (rem converts to px: 1rem = 16px)
1242
-
1243
- All utilities work with:
1244
- - **All prefixes**: `enter-`, `exit-`, `loop-`, `animate-`
1245
- - **Negative values**: Prefix with `-` (e.g., `-translate-x-5`, `-rotate-45`)
1246
- - **Timing syntax**: Add `/start/duration` (e.g., `enter-translate-x-5/0/800`)
1247
-
1248
- ### Translate Animations
1249
-
1250
- ```tsx
1251
- // Numeric (Tailwind spacing): 20px (5 * 4px)
1252
- <div style={tw('enter-translate-x-5/0/500')}>Content</div>
1253
-
1254
- // Keyword: Full width (100%)
1255
- <div style={tw('enter-translate-y-full/0/800')}>Dropping full height</div>
1256
-
1257
- // Fraction: Half width (50%)
1258
- <div style={tw('enter-translate-x-1/2/0/600')}>Slide in halfway</div>
1259
-
1260
- // Arbitrary values: Exact px or rem
1261
- <div style={tw('enter-translate-y-[20px]/0/500')}>Slide 20px</div>
1262
- <div style={tw('enter-translate-x-[5rem]/0/800')}>Slide 5rem (80px)</div>
1263
-
1264
- // Loop with fractions
1265
- <div style={tw('loop-translate-y-1/4/1000')}>Oscillate 25%</div>
1266
-
1267
- // Negative values
1268
- <div style={tw('exit--translate-y-8/2000/500')}>Rising</div>
1269
- ```
1270
-
1271
- ### Opacity Animations
1272
-
1273
- ```tsx
1274
- // Fade to 100% opacity
1275
- <div style={tw('enter-opacity-100/0/500')}>Fading In</div>
1276
-
1277
- // Fade to 50% opacity
1278
- <div style={tw('enter-opacity-50/0/800')}>Half Opacity</div>
1279
-
1280
- // Pulse between 50% and 100%
1281
- <div style={tw('loop-opacity-50/1000')}>Pulsing</div>
1282
-
1283
- // Fade out to 0%
1284
- <div style={tw('exit-opacity-0/2500/500')}>Vanishing</div>
1285
- ```
1286
-
1287
- ### Scale Animations
1288
-
1289
- ```tsx
1290
- // Scale from 0 to 100% (1.0x)
1291
- <div style={tw('enter-scale-100/0/500')}>Growing</div>
1292
-
1293
- // Scale to 150% (1.5x)
1294
- <div style={tw('enter-scale-150/0/800')}>Enlarging</div>
1295
-
1296
- // Pulse scale in a loop
1297
- <div style={tw('loop-scale-110/1000')}>Breathing</div>
1298
-
1299
- // Scale down to 50%
1300
- <div style={tw('exit-scale-50/2000/500')}>Shrinking</div>
1301
- ```
1302
-
1303
- ### Rotate Animations
1304
-
1305
- ```tsx
1306
- // Rotate 90 degrees
1307
- <div style={tw('enter-rotate-90/0/500')}>Quarter Turn</div>
1308
-
1309
- // Rotate 180 degrees
1310
- <div style={tw('enter-rotate-180/0/1000')}>Half Turn</div>
1311
-
1312
- // Continuous rotation in loop (360 degrees per cycle)
1313
- <div style={tw('loop-rotate-360/2000')}>Spinning</div>
1314
-
1315
- // Rotate backwards with negative value
1316
- <div style={tw('enter--rotate-45/0/500')}>Counter Rotation</div>
1317
- ```
1318
-
1319
- ### Skew Animations
1320
-
1321
- ```tsx
1322
- // Skew on X axis
1323
- <div style={tw('enter-skew-x-12/0/500')}>Slanted</div>
1324
-
1325
- // Skew on Y axis
1326
- <div style={tw('enter-skew-y-6/0/800')}>Tilted</div>
1327
-
1328
- // Oscillating skew in loop
1329
- <div style={tw('loop-skew-x-6/1000')}>Wobbling</div>
1330
-
1331
- // Negative skew
1332
- <div style={tw('exit--skew-x-12/2000/500')}>Reverse Slant</div>
1333
- ```
1334
-
1335
- ### Combining Utilities
1336
-
1337
- You can combine multiple utility animations on the same element:
1338
-
1339
- ```tsx
1340
- // Translate and rotate together
1341
- <div style={tw('enter-translate-y-10/0/500 enter-rotate-45/0/500')}>
1342
- Flying In
1343
- </div>
1344
-
1345
- // Fade and scale
1346
- <div style={tw('enter-opacity-100/0/800 enter-scale-100/0/800')}>
1347
- Appearing
1348
- </div>
1349
-
1350
- // Enter with translate, exit with rotation
1351
- <div style={tw('enter-translate-x-5/0/500 exit-rotate-180/2500/500')}>
1352
- Slide and Spin
1353
- </div>
1354
- ```
1355
-
1356
- ### Bracket Notation
1357
-
1358
- For more CSS-like syntax, you can use brackets with units:
1359
-
1360
- ```tsx
1361
- // Using bracket notation with seconds
1362
- <h1 style={tw('enter-slide-up/[0.6s]/[1.5s]')}>Hello</h1>
1363
-
1364
- // Using bracket notation with milliseconds
1365
- <h1 style={tw('enter-fade-in/[300ms]/[800ms]')}>World</h1>
1366
-
1367
- // Mix and match - plain numbers are milliseconds
1368
- <h1 style={tw('enter-bounce-in/0/[1.2s]')}>Mixed</h1>
1369
- ```
1370
-
1371
- ## Enter Animations
1372
-
1373
- Format: `enter-{type}/{startMs}/{durationMs}`
1374
-
1375
- - `startMs` - when the animation begins (milliseconds from start)
1376
- - `durationMs` - how long the animation lasts
1377
-
1378
- When values are omitted (`enter-fade-in`), it uses the full video duration.
1379
-
1380
- ### Fade Animations
1381
-
1382
- Simple opacity transitions with optional direction.
1383
-
1384
- ```tsx
1385
- // Fade in from 0ms to 500ms
1386
- <h1 style={tw('enter-fade-in/0/500')}>Hello</h1>
1387
-
1388
- // Fade in with upward motion
1389
- <h1 style={tw('enter-fade-in-up/0/600')}>Hello</h1>
1390
- ```
1391
-
1392
- | Class | Description |
1393
- |-------|-------------|
1394
- | `enter-fade-in/0/500` | Fade in (opacity 0 → 1) |
1395
- | `enter-fade-in-up/0/500` | Fade in + slide up (30px) |
1396
- | `enter-fade-in-down/0/500` | Fade in + slide down (30px) |
1397
- | `enter-fade-in-left/0/500` | Fade in + slide from left (30px) |
1398
- | `enter-fade-in-right/0/500` | Fade in + slide from right (30px) |
1399
-
1400
- ### Slide Animations
1401
-
1402
- Larger movement (100px) with fade.
1403
-
1404
- ```tsx
1405
- // Slide in from left: starts at 0, lasts 500ms
1406
- <div style={tw('enter-slide-left/0/500')}>Content</div>
1407
-
1408
- // Slide up from bottom: starts at 200ms, lasts 600ms
1409
- <div style={tw('enter-slide-up/200/600')}>Content</div>
1410
- ```
1411
-
1412
- | Class | Description |
1413
- |-------|-------------|
1414
- | `enter-slide-left/0/500` | Slide in from left (100px) |
1415
- | `enter-slide-right/0/500` | Slide in from right (100px) |
1416
- | `enter-slide-up/0/500` | Slide in from bottom (100px) |
1417
- | `enter-slide-down/0/500` | Slide in from top (100px) |
1418
-
1419
- ### Bounce Animations
1420
-
1421
- Playful entrance with overshoot effect.
1422
-
1423
- ```tsx
1424
- // Bounce in with scale overshoot
1425
- <h1 style={tw('enter-bounce-in/0/500')}>Bouncy!</h1>
1426
-
1427
- // Bounce in from below
1428
- <div style={tw('enter-bounce-in-up/0/600')}>Pop!</div>
1429
- ```
1430
-
1431
- | Class | Description |
1432
- |-------|-------------|
1433
- | `enter-bounce-in/0/500` | Bounce in with scale overshoot |
1434
- | `enter-bounce-in-up/0/500` | Bounce in from below |
1435
- | `enter-bounce-in-down/0/500` | Bounce in from above |
1436
- | `enter-bounce-in-left/0/500` | Bounce in from left |
1437
- | `enter-bounce-in-right/0/500` | Bounce in from right |
1438
-
1439
- ### Scale & Zoom Animations
1440
-
1441
- Size-based transitions.
1442
-
1443
- ```tsx
1444
- // Scale in from 50%
1445
- <div style={tw('enter-scale-in/0/500')}>Growing</div>
1446
-
1447
- // Zoom in from 0%
1448
- <div style={tw('enter-zoom-in/0/1000')}>Zooming</div>
1449
- ```
1450
-
1451
- | Class | Description |
1452
- |-------|-------------|
1453
- | `enter-scale-in/0/500` | Scale up from 50% to 100% |
1454
- | `enter-zoom-in/0/500` | Zoom in from 0% to 100% |
1455
-
1456
- ### Rotate & Flip Animations
1457
-
1458
- Rotation-based transitions.
1459
-
1460
- ```tsx
1461
- // Rotate in 180 degrees
1462
- <div style={tw('enter-rotate-in/0/500')}>Spinning</div>
1463
-
1464
- // 3D flip on X axis
1465
- <div style={tw('enter-flip-in-x/0/500')}>Flipping</div>
1466
- ```
1467
-
1468
- | Class | Description |
1469
- |-------|-------------|
1470
- | `enter-rotate-in/0/500` | Rotate in from -180° |
1471
- | `enter-flip-in-x/0/500` | 3D flip on horizontal axis |
1472
- | `enter-flip-in-y/0/500` | 3D flip on vertical axis |
1473
-
1474
- ## Exit Animations
1475
-
1476
- Format: `exit-{type}/{startMs}/{durationMs}`
1477
-
1478
- - `startMs` - when the exit animation begins
1479
- - `durationMs` - how long the exit animation lasts
1480
-
1481
- Exit animations use the same timing system but animate elements out.
1482
-
1483
- ```tsx
1484
- // Fade out starting at 2500ms, lasting 500ms (ends at 3000ms)
1485
- <h1 style={tw('exit-fade-out/2500/500')}>Goodbye</h1>
1486
-
1487
- // Combined enter and exit on same element
1488
- <h1 style={tw('enter-fade-in/0/500 exit-fade-out/2500/500')}>
1489
- Hello and Goodbye
1490
- </h1>
1491
- ```
1492
-
1493
- | Class | Description |
1494
- |-------|-------------|
1495
- | `exit-fade-out/2500/500` | Fade out (opacity 1 → 0) |
1496
- | `exit-fade-out-up/2500/500` | Fade out + slide up |
1497
- | `exit-fade-out-down/2500/500` | Fade out + slide down |
1498
- | `exit-fade-out-left/2500/500` | Fade out + slide left |
1499
- | `exit-fade-out-right/2500/500` | Fade out + slide right |
1500
- | `exit-slide-up/2500/500` | Slide out upward (100px) |
1501
- | `exit-slide-down/2500/500` | Slide out downward (100px) |
1502
- | `exit-slide-left/2500/500` | Slide out to left (100px) |
1503
- | `exit-slide-right/2500/500` | Slide out to right (100px) |
1504
- | `exit-scale-out/2500/500` | Scale out to 150% |
1505
- | `exit-zoom-out/2500/500` | Zoom out to 200% |
1506
- | `exit-rotate-out/2500/500` | Rotate out to 180° |
1507
- | `exit-bounce-out/2500/500` | Bounce out with scale |
1508
- | `exit-bounce-out-up/2500/500` | Bounce out upward |
1509
- | `exit-bounce-out-down/2500/500` | Bounce out downward |
1510
- | `exit-bounce-out-left/2500/500` | Bounce out to left |
1511
- | `exit-bounce-out-right/2500/500` | Bounce out to right |
1512
-
1513
- ## Loop Animations
1514
-
1515
- Format: `loop-{type}/{durationMs}`
1516
-
1517
- Loop animations repeat every `{durationMs}` milliseconds:
1518
- - `/1000` = 1 second loop
1519
- - `/500` = 0.5 second loop
1520
- - `/2000` = 2 second loop
1521
-
1522
- When duration is omitted (`loop-bounce`), it defaults to 1000ms (1 second).
1523
-
1524
- ```tsx
1525
- // Pulse opacity every 500ms
1526
- <div style={tw('loop-fade/500')}>Pulsing</div>
1527
-
1528
- // Bounce every 800ms
1529
- <div style={tw('loop-bounce/800')}>Bouncing</div>
1530
-
1531
- // Full rotation every 2000ms
1532
- <div style={tw('loop-spin/2000')}>Spinning</div>
1533
- ```
1534
-
1535
- | Class | Description |
1536
- |-------|-------------|
1537
- | `loop-fade/{ms}` | Opacity pulse (0.5 → 1 → 0.5) |
1538
- | `loop-bounce/{ms}` | Bounce up and down |
1539
- | `loop-spin/{ms}` | Full 360° rotation |
1540
- | `loop-ping/{ms}` | Scale up + fade out (radar effect) |
1541
- | `loop-wiggle/{ms}` | Side to side wiggle |
1542
- | `loop-float/{ms}` | Gentle up and down floating |
1543
- | `loop-pulse/{ms}` | Scale pulse (1.0 → 1.05 → 1.0) |
1544
- | `loop-shake/{ms}` | Shake side to side |
1545
-
1546
- ## Easing Functions
1547
-
1548
- Add an easing class **before** the animation class to control the timing curve.
1549
-
1550
- ```tsx
1551
- // Ease in (accelerate)
1552
- <h1 style={tw('ease-in enter-fade-in/0/1000')}>Accelerating</h1>
1553
-
1554
- // Ease out (decelerate) - default
1555
- <h1 style={tw('ease-out enter-fade-in/0/1000')}>Decelerating</h1>
1556
-
1557
- // Ease in-out (smooth)
1558
- <h1 style={tw('ease-in-out enter-fade-in/0/1000')}>Smooth</h1>
1559
-
1560
- // Strong cubic easing
1561
- <h1 style={tw('ease-out-cubic enter-bounce-in/0/500')}>Dramatic</h1>
1562
- ```
1563
-
1564
- | Class | Description | Best For |
1565
- |-------|-------------|----------|
1566
- | `linear` | Constant speed | Mechanical motion |
1567
- | `ease-in` | Slow start, fast end | Exit animations |
1568
- | `ease-out` | Fast start, slow end (default) | Enter animations |
1569
- | `ease-in-out` | Slow start and end | Subtle transitions |
1570
- | `ease-in-cubic` | Strong slow start | Dramatic exits |
1571
- | `ease-out-cubic` | Strong fast start | Impactful entrances |
1572
- | `ease-in-out-cubic` | Strong both ends | Emphasis animations |
1573
- | `ease-in-quart` | Very strong slow start | Powerful exits |
1574
- | `ease-out-quart` | Very strong fast start | Punchy entrances |
1575
- | `ease-in-out-quart` | Very strong both ends | Maximum drama |
1576
-
1577
- ### Per-Animation-Type Easing
1578
-
1579
- You can apply **different easing functions** to enter, exit, and loop animations on the same element using `enter-ease-*`, `exit-ease-*`, and `loop-ease-*` classes.
1580
-
1581
- ```tsx
1582
- // Different easing for enter and exit
1583
- <h1 style={tw('enter-ease-out-cubic enter-fade-in/0/500 exit-ease-in exit-fade-out/2500/500')}>
1584
- Smooth entrance, sharp exit
1585
- </h1>
1586
-
1587
- // Loop with linear easing, enter with bounce
1588
- <div style={tw('enter-ease-out enter-bounce-in/0/400 loop-ease-linear loop-fade/1000')}>
1589
- Bouncy entrance, linear loop
1590
- </div>
1591
-
1592
- // Default easing still works (applies to all animations)
1593
- <div style={tw('ease-in-out enter-fade-in/0/500 exit-fade-out/2500/500')}>
1594
- Same easing for both
1595
- </div>
1596
-
1597
- // Mix default with specific overrides
1598
- <div style={tw('ease-out enter-fade-in/0/500 exit-ease-in-cubic exit-fade-out/2500/500')}>
1599
- Default ease-out for enter, cubic-in for exit
1600
- </div>
1601
- ```
1602
-
1603
- **How it works:**
1604
-
1605
- 1. **Default easing** (`ease-*`) applies to ALL animations if no specific override is set
1606
- 2. **Specific easing** (`enter-ease-*`, `exit-ease-*`, `loop-ease-*`) overrides the default for that animation type
1607
- 3. If both are present, specific easing takes priority for its animation type
1608
-
1609
- **Available easing classes:**
1610
-
1611
- | Default (all animations) | Enter only | Exit only | Loop only |
1612
- |--------------------------|------------|-----------|-----------|
1613
- | `ease-in` | `enter-ease-in` | `exit-ease-in` | `loop-ease-in` |
1614
- | `ease-out` | `enter-ease-out` | `exit-ease-out` | `loop-ease-out` |
1615
- | `ease-in-out` | `enter-ease-in-out` | `exit-ease-in-out` | `loop-ease-in-out` |
1616
- | `ease-in-cubic` | `enter-ease-in-cubic` | `exit-ease-in-cubic` | `loop-ease-in-cubic` |
1617
- | `ease-out-cubic` | `enter-ease-out-cubic` | `exit-ease-out-cubic` | `loop-ease-out-cubic` |
1618
- | `ease-in-out-cubic` | `enter-ease-in-out-cubic` | `exit-ease-in-out-cubic` | `loop-ease-in-out-cubic` |
1619
- | `ease-in-quart` | `enter-ease-in-quart` | `exit-ease-in-quart` | `loop-ease-in-quart` |
1620
- | `ease-out-quart` | `enter-ease-out-quart` | `exit-ease-out-quart` | `loop-ease-out-quart` |
1621
- | `ease-in-out-quart` | `enter-ease-in-out-quart` | `exit-ease-in-out-quart` | `loop-ease-in-out-quart` |
1622
- | `linear` | `enter-ease-linear` | `exit-ease-linear` | `loop-ease-linear` |
1623
- | `ease-spring` | `enter-ease-spring` | `exit-ease-spring` | `loop-ease-spring` |
1624
-
1625
- ### Spring Easing
1626
-
1627
- Spring easing creates natural, physics-based bouncy animations. Use the built-in `ease-spring` easing or create custom springs with configurable parameters.
1628
-
1629
- ```tsx
1630
- // Default spring easing
1631
- <h1 style={tw('ease-spring enter-bounce-in/0/500')}>Bouncy spring!</h1>
1632
-
1633
- // Per-animation-type spring
1634
- <div style={tw('enter-ease-spring enter-fade-in/0/500 exit-ease-out exit-fade-out/2500/500')}>
1635
- Spring entrance, smooth exit
1636
- </div>
1637
-
1638
- // Custom spring with parameters: ease-spring/mass/stiffness/damping
1639
- <h1 style={tw('ease-spring/1/100/10 enter-scale-in/0/800')}>
1640
- Custom spring (mass=1, stiffness=100, damping=10)
1641
- </h1>
1642
-
1643
- // More bouncy spring (lower damping)
1644
- <div style={tw('ease-spring/1/170/8 enter-bounce-in-up/0/600')}>
1645
- Extra bouncy!
1646
- </div>
1647
-
1648
- // Stiffer spring (higher stiffness, faster)
1649
- <div style={tw('ease-spring/1/200/12 enter-fade-in-up/0/400')}>
1650
- Snappy spring
1651
- </div>
1652
-
1653
- // Per-animation-type custom springs
1654
- <div style={tw('enter-ease-spring/1/150/10 enter-fade-in/0/500 exit-ease-spring/1/100/15 exit-fade-out/2500/500')}>
1655
- Different springs for enter and exit
1656
- </div>
1657
- ```
1658
-
1659
- **Spring parameters:**
1660
-
1661
- | Parameter | Description | Effect when increased | Default |
1662
- |-----------|-------------|----------------------|---------|
1663
- | **mass** | Mass of the spring | Slower, more inertia | 1 |
1664
- | **stiffness** | Spring stiffness | Faster, snappier | 100 |
1665
- | **damping** | Damping coefficient | Less bounce, smoother | 10 |
1666
-
1667
- **Common spring presets:**
1668
-
1669
- ```tsx
1670
- // Gentle bounce (default)
1671
- ease-spring/1/100/10
1672
-
1673
- // Extra bouncy
1674
- ease-spring/1/170/8
1675
-
1676
- // Snappy (no bounce)
1677
- ease-spring/1/200/15
1678
-
1679
- // Slow and bouncy
1680
- ease-spring/2/100/8
1681
-
1682
- // Fast and tight
1683
- ease-spring/0.5/300/20
1684
- ```
1685
-
1686
- **How spring works:**
1687
-
1688
- 1. **Default `ease-spring`** - Uses a pre-calculated spring curve optimized for most use cases
1689
- 2. **Custom `ease-spring/mass/stiffness/damping`** - Generates a physics-based spring curve using the [damped harmonic oscillator](https://www.kvin.me/css-springs) formula
1690
- 3. The spring automatically calculates its ideal duration to reach the final state
1691
- 4. Works with all animation types: `ease-spring`, `enter-ease-spring`, `exit-ease-spring`, `loop-ease-spring`
1692
-
1693
- ## Combining Enter and Exit
1694
-
1695
- You can use both enter and exit animations on the same element:
1696
-
1697
- ```tsx
1698
- export default function EnterExit({ tw, title }) {
1699
- return (
1700
- <div style={tw('flex items-center justify-center w-full h-full bg-black')}>
1701
- {/* Fade in during first 500ms, fade out during last 500ms (assuming 3s video) */}
1702
- <h1 style={tw('text-8xl font-bold text-white enter-fade-in/0/500 exit-fade-out/2500/500')}>
1703
- {title}
1704
- </h1>
1705
- </div>
1706
- );
1707
- }
1708
- ```
1709
-
1710
- The opacities from multiple animations are **multiplied together**, so you get smooth transitions that combine properly.
1711
-
1712
- ## Staggered Animations
1713
-
1714
- Create sequenced animations by offsetting start times:
1715
-
1716
- ```tsx
1717
- export default function StaggeredList({ tw, items }) {
1718
- return (
1719
- <div style={tw('flex flex-col gap-4')}>
1720
- {/* First item: starts at 0ms, lasts 300ms */}
1721
- <div style={tw('ease-out enter-fade-in-left/0/300')}>
1722
- {items[0]}
1723
- </div>
1724
-
1725
- {/* Second item: starts at 100ms, lasts 300ms */}
1726
- <div style={tw('ease-out enter-fade-in-left/100/300')}>
1727
- {items[1]}
1728
- </div>
1729
-
1730
- {/* Third item: starts at 200ms, lasts 300ms */}
1731
- <div style={tw('ease-out enter-fade-in-left/200/300')}>
1732
- {items[2]}
1733
- </div>
1734
- </div>
1735
- );
1736
- }
1737
- ```
1738
-
1739
- ### Dynamic Staggering
1740
-
1741
- For dynamic lists, calculate the timing programmatically:
1742
-
1743
- ```tsx
1744
- export default function DynamicStagger({ tw, items }) {
1745
- return (
1746
- <div style={tw('flex flex-col gap-4')}>
1747
- {items.map((item, i) => {
1748
- const start = i * 100; // Each item starts 100ms later
1749
- const duration = 300; // Each animation lasts 300ms
1750
-
1751
- return (
1752
- <div
1753
- key={i}
1754
- style={tw(`ease-out enter-fade-in-up/${start}/${duration}`)}
1755
- >
1756
- {item}
1757
- </div>
1758
- );
1759
- })}
1760
- </div>
1761
- );
1762
- }
1763
- ```
1764
-
1765
- ## Common Patterns
1766
-
1767
- ### Intro Sequence
1768
-
1769
- ```tsx
1770
- export default function IntroVideo({ tw, title, subtitle, logo }) {
1771
- return (
1772
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
1773
- {/* Logo appears first */}
1774
- <img
1775
- src={logo}
1776
- style={tw('h-20 mb-8 ease-out enter-scale-in/0/300')}
1777
- />
1778
-
1779
- {/* Title bounces in */}
1780
- <h1 style={tw('text-7xl font-bold text-white ease-out enter-bounce-in-up/200/500')}>
1781
- {title}
1782
- </h1>
1783
-
1784
- {/* Subtitle fades in last */}
1785
- <p style={tw('text-2xl text-white/80 mt-4 ease-out enter-fade-in-up/400/700')}>
1786
- {subtitle}
1787
- </p>
1788
- </div>
1789
- );
1790
- }
1791
- ```
1792
-
1793
- ### Text Reveal
1794
-
1795
- ```tsx
1796
- export default function TextReveal({ tw, words }) {
1797
- return (
1798
- <div style={tw('flex flex-wrap gap-2 justify-center')}>
1799
- {words.split(' ').map((word, i) => (
1800
- <span
1801
- key={i}
1802
- style={tw(`text-4xl font-bold ease-out enter-fade-in-up/${i * 100}/200`)}
1803
- >
1804
- {word}
1805
- </span>
1806
- ))}
1807
- </div>
1808
- );
1809
- }
1810
- ```
1811
-
1812
- ### Looping Background Element
1813
-
1814
- ```tsx
1815
- export default function AnimatedBackground({ tw, children }) {
1816
- return (
1817
- <div style={tw('relative w-full h-full')}>
1818
- {/* Floating background circles */}
1819
- <div style={tw('absolute top-10 left-10 w-20 h-20 rounded-full bg-white/10 loop-float/2000')} />
1820
- <div style={tw('absolute bottom-20 right-20 w-32 h-32 rounded-full bg-white/10 loop-fade/1500')} />
1821
-
1822
- {/* Main content */}
1823
- <div style={tw('relative z-10')}>
1824
- {children}
1825
- </div>
1826
- </div>
1827
- );
1828
- }
1829
- ```
1830
-
1831
- ### Full Enter/Exit Animation
1832
-
1833
- ```tsx
1834
- export default function FullAnimation({ tw, title }) {
1835
- return (
1836
- <div style={tw('flex items-center justify-center w-full h-full bg-black')}>
1837
- {/* Enter: starts at 0, lasts 400ms. Exit: starts at 2600ms, lasts 400ms */}
1838
- <h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/400 exit-fade-out-up/2600/400')}>
1839
- {title}
1840
- </h1>
1841
- </div>
1842
- );
1843
- }
1844
- ```
1845
-
1846
- ## Programmatic Animations
1847
-
1848
- For complete control beyond animation classes, use `progress` and `frame` directly.
1849
-
1850
- ### Available Props
1851
-
1852
- | Prop | Type | Description |
1853
- |------|------|-------------|
1854
- | `progress` | `number` | 0 to 1 through the video (0% to 100%) |
1855
- | `frame` | `number` | Current frame number (0, 1, 2, ... totalFrames-1) |
1856
-
1857
- These are **only available in video templates**. Use them when animation classes aren't flexible enough.
1858
-
1859
- ### Using `frame`
1860
-
1861
- ```tsx
1862
- export default function FrameAnimation({ tw, frame, title }) {
1863
- // Color cycling using frame number
1864
- const hue = (frame * 5) % 360; // Cycle through colors
1865
-
1866
- // Pulsing based on frame
1867
- const fps = 30;
1868
- const pulse = Math.sin(frame / fps * Math.PI * 2) * 0.2 + 0.8; // 0.6 to 1.0
1869
-
1870
- return (
1871
- <div style={tw('flex items-center justify-center w-full h-full bg-black')}>
1872
- <h1 style={{
1873
- ...tw('text-8xl font-bold'),
1874
- color: `hsl(${hue}, 70%, 60%)`,
1875
- transform: `scale(${pulse})`
1876
- }}>
1877
- {title}
1878
- </h1>
1879
- </div>
1880
- );
1881
- }
1882
- ```
1883
-
1884
- ### Using `progress`
1885
-
1886
- ```tsx
1887
- export default function ProgressAnimation({ tw, progress, title }) {
1888
- // Custom fade based on progress
1889
- const opacity = progress < 0.3 ? progress / 0.3 : 1;
1890
-
1891
- // Custom scale based on progress
1892
- const scale = 0.8 + progress * 0.2; // 0.8 to 1.0
1893
-
1894
- return (
1895
- <div style={tw('flex items-center justify-center w-full h-full bg-gray-900')}>
1896
- <h1 style={{
1897
- ...tw('text-8xl font-bold text-white'),
1898
- opacity,
1899
- transform: `scale(${scale})`
1900
- }}>
1901
- {title}
1902
- </h1>
1903
- </div>
1904
- );
1905
- }
1906
- ```
1907
-
1908
- ### Custom Easing
1909
-
1910
- ```tsx
1911
- export default function CustomEasing({ tw, progress, title }) {
1912
- // Smoothstep easing
1913
- const eased = progress * progress * (3 - 2 * progress);
1914
-
1915
- // Elastic easing
1916
- const elastic = Math.pow(2, -10 * progress) * Math.sin((progress - 0.075) * (2 * Math.PI) / 0.3) + 1;
1917
-
1918
- return (
1919
- <div style={tw('flex items-center justify-center w-full h-full')}>
1920
- <h1 style={{
1921
- ...tw('text-8xl font-bold'),
1922
- opacity: eased,
1923
- transform: `translateY(${(1 - elastic) * 100}px)`
1924
- }}>
1925
- {title}
1926
- </h1>
1927
- </div>
1928
- );
1929
- }
1930
- ```
1931
-
1932
- ### When to Use Programmatic Animations
1933
-
1934
- Use `progress`/`frame` instead of animation classes when you need:
1935
- - **Custom easing functions** (elastic, bounce with specific curves beyond built-in ease-spring)
1936
- - **Color cycling or gradients** based on time
1937
- - **Mathematical animations** (sine waves, spirals, etc.)
1938
- - **Complex multi-property animations** that need precise coordination
1939
- - **Conditional logic** based on specific frame numbers
1940
-
1941
- For everything else, prefer animation classes - they're simpler and more maintainable.
1942
-
1943
- ### Animating Along Paths
1944
-
1945
- Animate elements along SVG paths with proper rotation using built-in **path helpers**:
1946
-
1947
- ```tsx
1948
- export default function PathFollowing({ tw, progress, path }) {
1949
- // Follow a quadratic Bezier curve - one line!
1950
- const rocket = path.followQuadratic(
1951
- { x: 200, y: 400 }, // Start point
1952
- { x: 960, y: 150 }, // Control point
1953
- { x: 1720, y: 400 }, // End point
1954
- progress
1955
- );
1956
-
1957
- return (
1958
- <div style={{ display: 'flex', ...tw('relative w-full h-full bg-gray-900') }}>
1959
- {/* Draw the path (optional) */}
1960
- <svg width="1920" height="1080" style={{ position: 'absolute' }}>
1961
- <path
1962
- d="M 200 400 Q 960 150 1720 400"
1963
- stroke="rgba(255,255,255,0.2)"
1964
- strokeWidth={2}
1965
- fill="none"
1966
- />
1967
- </svg>
1968
-
1969
- {/* Element following the path */}
1970
- <div
1971
- style={{
1972
- position: "absolute",
1973
- left: rocket.x,
1974
- top: rocket.y,
1975
- transform: `translate(-50%, -50%) rotate(${rocket.angle}deg)`,
1976
- fontSize: '48px'
1977
- }}
1978
- >
1979
- 🚀
1980
- </div>
1981
- </div>
1982
- );
1983
- }
1984
- ```
1985
-
1986
- ### Text Path Animations
1987
-
1988
- Combine `textPath` helpers with animation classes to create animated text along curves:
1989
-
1990
- **Rotating text around a circle:**
1991
- ```tsx
1992
- export default function RotatingCircleText({ tw, textPath, progress }) {
1993
- return (
1994
- <div style={tw('relative w-full h-full bg-black')}>
1995
- {/* Text rotates around circle using progress */}
1996
- {textPath.onCircle(
1997
- "SPINNING TEXT • AROUND • ",
1998
- 960, // center x
1999
- 540, // center y
2000
- 400, // radius
2001
- progress, // rotation offset (0-1 animates full rotation)
2002
- {
2003
- fontSize: "3xl",
2004
- fontWeight: "bold",
2005
- color: "yellow-300"
2006
- }
2007
- )}
2008
- </div>
2009
- );
2010
- }
2011
- ```
2012
-
2013
- **Animated text reveal along a path:**
2014
- ```tsx
2015
- export default function PathTextReveal({ tw, textPath, progress }) {
2016
- // Create custom path follower that animates position
2017
- const pathFollower = (t) => {
2018
- // Only show characters up to current progress
2019
- const visibleProgress = progress * 1.5; // Extend range for smooth reveal
2020
- const opacity = t < visibleProgress ? 1 : 0;
2021
-
2022
- // Follow quadratic curve
2023
- const pos = {
2024
- x: (1 - t) * (1 - t) * 200 + 2 * (1 - t) * t * 960 + t * t * 1720,
2025
- y: (1 - t) * (1 - t) * 400 + 2 * (1 - t) * t * 150 + t * t * 400,
2026
- angle: 0
2027
- };
2028
-
2029
- return { ...pos, opacity };
2030
- };
2031
-
2032
- return (
2033
- <div style={tw('relative w-full h-full bg-gray-900')}>
2034
- {textPath.onPath(
2035
- "REVEALING TEXT",
2036
- pathFollower,
2037
- {
2038
- fontSize: "4xl",
2039
- fontWeight: "bold",
2040
- color: "blue-300"
2041
- }
2042
- ).map((char, i) => (
2043
- <div key={i} style={{ ...char.props.style, opacity: char.props.style.opacity || 1 }}>
2044
- {char}
2045
- </div>
2046
- ))}
2047
- </div>
2048
- );
2049
- }
2050
- ```
2051
-
2052
- **Staggered character entrance:**
2053
- ```tsx
2054
- export default function StaggeredCircleText({ tw, textPath }) {
2055
- const text = "HELLO WORLD";
2056
-
2057
- return (
2058
- <div style={tw('relative w-full h-full bg-slate-900')}>
2059
- {textPath.onCircle(
2060
- text,
2061
- 960, 540, 400, 0,
2062
- { fontSize: "4xl", fontWeight: "bold", color: "white" }
2063
- ).map((char, i) => {
2064
- // Stagger fade-in: each character starts 50ms later
2065
- const staggerDelay = i * 50;
2066
- return (
2067
- <div
2068
- key={i}
2069
- style={{
2070
- ...char.props.style,
2071
- ...tw(`enter-fade-in/${staggerDelay}/300 enter-scale-100/${staggerDelay}/300`)
2072
- }}
2073
- >
2074
- {char.props.children}
2075
- </div>
2076
- );
2077
- })}
2078
- </div>
2079
- );
2080
- }
2081
- ```
2082
-
2083
- **Text with bounce entrance along arc:**
2084
- ```tsx
2085
- export default function BouncyArcText({ tw, textPath }) {
2086
- return (
2087
- <div style={tw('relative w-full h-full bg-gradient-to-br from-purple-600 to-blue-500')}>
2088
- {/* Draw the arc path */}
2089
- <svg width="1920" height="1080" style={{ position: 'absolute' }}>
2090
- <path
2091
- d="M 300 900 A 600 600 0 0 1 1620 900"
2092
- stroke="rgba(255,255,255,0.2)"
2093
- strokeWidth={2}
2094
- fill="none"
2095
- strokeDasharray="5 5"
2096
- />
2097
- </svg>
2098
-
2099
- {/* Text follows arc with staggered bounce */}
2100
- {textPath.onArc(
2101
- "BOUNCING ON ARC",
2102
- 960, // cx
2103
- 300, // cy
2104
- 600, // radius
2105
- 180, // start angle
2106
- 360, // end angle
2107
- { fontSize: "3xl", fontWeight: "bold", color: "white" }
2108
- ).map((char, i) => (
2109
- <div
2110
- key={i}
2111
- style={{
2112
- ...char.props.style,
2113
- ...tw(`ease-out enter-bounce-in-up/${i * 80}/500`)
2114
- }}
2115
- >
2116
- {char.props.children}
2117
- </div>
2118
- ))}
2119
- </div>
2120
- );
2121
- }
2122
- ```
2123
-
2124
- **Loop animation with text on curve:**
2125
- ```tsx
2126
- export default function LoopingCurveText({ tw, textPath, frame }) {
2127
- // Calculate wave effect using frame
2128
- const waveOffset = Math.sin(frame / 30 * Math.PI * 2) * 0.1;
2129
-
2130
- return (
2131
- <div style={tw('relative w-full h-full bg-black')}>
2132
- {textPath.onQuadratic(
2133
- "WAVY TEXT",
2134
- { x: 200, y: 400 },
2135
- { x: 960, y: 150 },
2136
- { x: 1720, y: 400 },
2137
- { fontSize: "4xl", fontWeight: "bold", color: "pink-300" }
2138
- ).map((char, i) => (
2139
- <div
2140
- key={i}
2141
- style={{
2142
- ...char.props.style,
2143
- transform: `${char.props.style.transform} translateY(${Math.sin((i + frame) / 5) * 10}px)`
2144
- }}
2145
- >
2146
- {char.props.children}
2147
- </div>
2148
- ))}
2149
- </div>
2150
- );
2151
- }
2152
- ```
2153
-
2154
- **Tips for animating text paths:**
2155
- 1. **Use `progress` for smooth rotation** on circles and arcs
2156
- 2. **Map over returned characters** to apply individual animations
2157
- 3. **Combine with animation classes** like `enter-fade-in`, `enter-bounce-in`, etc.
2158
- 4. **Stagger character animations** by calculating delays: `i * delayMs`
2159
- 5. **Use `frame` for continuous effects** like waves or pulsing
2160
- 6. **Preserve the original transform** when adding animations: `transform: '${char.props.style.transform} ...'`
2161
-
2162
- **Common path types:**
2163
-
2164
- **Quadratic Bezier** (Q command):
2165
- ```tsx
2166
- // Position: (1-t)²·P0 + 2(1-t)t·P1 + t²·P2
2167
- function pointOnQuadraticBezier(p0, p1, p2, t) {
2168
- const x = (1 - t) * (1 - t) * p0.x + 2 * (1 - t) * t * p1.x + t * t * p2.x;
2169
- const y = (1 - t) * (1 - t) * p0.y + 2 * (1 - t) * t * p1.y + t * t * p2.y;
2170
- return { x, y };
2171
- }
2172
-
2173
- // Tangent angle
2174
- function angleOnQuadraticBezier(p0, p1, p2, t) {
2175
- const dx = 2 * (1 - t) * (p1.x - p0.x) + 2 * t * (p2.x - p1.x);
2176
- const dy = 2 * (1 - t) * (p1.y - p0.y) + 2 * t * (p2.y - p1.y);
2177
- return Math.atan2(dy, dx) * (180 / Math.PI);
2178
- }
2179
- ```
2180
-
2181
- **Cubic Bezier** (C command):
2182
- ```tsx
2183
- // Position: (1-t)³·P0 + 3(1-t)²t·P1 + 3(1-t)t²·P2 + t³·P3
2184
- function pointOnCubicBezier(p0, p1, p2, p3, t) {
2185
- const mt = 1 - t;
2186
- const mt2 = mt * mt;
2187
- const mt3 = mt2 * mt;
2188
- const t2 = t * t;
2189
- const t3 = t2 * t;
2190
- const x = mt3 * p0.x + 3 * mt2 * t * p1.x + 3 * mt * t2 * p2.x + t3 * p3.x;
2191
- const y = mt3 * p0.y + 3 * mt2 * t * p1.y + 3 * mt * t2 * p2.y + t3 * p3.y;
2192
- return { x, y };
2193
- }
2194
-
2195
- // Tangent angle
2196
- function angleOnCubicBezier(p0, p1, p2, p3, t) {
2197
- const mt = 1 - t;
2198
- const mt2 = mt * mt;
2199
- const t2 = t * t;
2200
- const dx = -3 * mt2 * p0.x + 3 * mt2 * p1.x - 6 * mt * t * p1.x - 3 * t2 * p2.x + 6 * mt * t * p2.x + 3 * t2 * p3.x;
2201
- const dy = -3 * mt2 * p0.y + 3 * mt2 * p1.y - 6 * mt * t * p1.y - 3 * t2 * p2.y + 6 * mt * t * p2.y + 3 * t2 * p3.y;
2202
- return Math.atan2(dy, dx) * (180 / Math.PI);
2203
- }
2204
- ```
2205
-
2206
- **Circle**:
2207
- ```tsx
2208
- function pointOnCircle(cx, cy, radius, angleRadians) {
2209
- return {
2210
- x: cx + radius * Math.cos(angleRadians),
2211
- y: cy + radius * Math.sin(angleRadians)
2212
- };
2213
- }
2214
-
2215
- // Usage
2216
- const angleRadians = progress * Math.PI * 2;
2217
- const pos = pointOnCircle(300, 300, 100, angleRadians);
2218
- const tangentAngle = (angleRadians * 180 / Math.PI) + 90; // Tangent is perpendicular
2219
- ```
2220
-
2221
- **Tips:**
2222
- - Use `progress` (0-1) for smooth animation
2223
- - The `translate(-50%, -50%)` centers the element on the path
2224
- - Combine rotation with the translate: `translate(-50%, -50%) rotate(${angle}deg)`
2225
- - For text following a path, you can animate individual characters at different progress values
2226
-
2227
- ## SVG Stroke Animations
2228
-
2229
- Animate SVG path strokes with the **stroke-dash** classes, perfect for drawing or erasing line art, icons, and illustrations.
2230
-
2231
- ### How It Works
2232
-
2233
- SVG stroke animations use `strokeDasharray` and `strokeDashoffset` CSS properties to create drawing effects:
2234
-
2235
- 1. **Enter animations** - Draw the stroke from start to finish
2236
- 2. **Exit animations** - Erase the stroke from finish to start
2237
- 3. **Loop animations** - Continuously draw and erase
2238
-
2239
- ### Format
2240
-
2241
- All stroke-dash animations require the **path length** in brackets:
2242
-
2243
- ```tsx
2244
- enter-stroke-dash-[length]/start/duration
2245
- exit-stroke-dash-[length]/start/duration
2246
- loop-stroke-dash-[length]/duration
2247
- ```
2248
-
2249
- ### Basic Examples
2250
-
2251
- ```tsx
2252
- export default function SVGAnimation({ tw }) {
2253
- return (
2254
- <svg width="400" height="200" viewBox="0 0 400 200">
2255
- {/* Draw a curve over 1 second */}
2256
- <path
2257
- d="M10 150 Q 95 10 180 150"
2258
- stroke="black"
2259
- strokeWidth={4}
2260
- fill="none"
2261
- style={tw('enter-stroke-dash-[300]/0/1000')}
2262
- />
2263
- </svg>
2264
- );
2265
- }
2266
- ```
2267
-
2268
- ### Enter Animations (Drawing)
2269
-
2270
- Draw strokes from 0% to 100%:
2271
-
2272
- ```tsx
2273
- // Draw a 300px path over 1 second
2274
- <path style={tw('enter-stroke-dash-[300]/0/1000')} />
2275
-
2276
- // Draw with spring easing
2277
- <path style={tw('ease-spring enter-stroke-dash-[500]/0/1500')} />
2278
-
2279
- // Stagger multiple paths
2280
- <path style={tw('enter-stroke-dash-[200]/0/600')} />
2281
- <path style={tw('enter-stroke-dash-[200]/200/600')} />
2282
- <path style={tw('enter-stroke-dash-[200]/400/600')} />
2283
- ```
2284
-
2285
- ### Exit Animations (Erasing)
2286
-
2287
- Erase strokes from 100% to 0%:
2288
-
2289
- ```tsx
2290
- // Erase starting at 2000ms, lasting 500ms
2291
- <path style={tw('exit-stroke-dash-[300]/2000/500')} />
2292
-
2293
- // Draw then erase the same path
2294
- <path style={tw('enter-stroke-dash-[400]/0/800 exit-stroke-dash-[400]/2200/800')} />
2295
- ```
2296
-
2297
- ### Loop Animations
2298
-
2299
- Continuously draw and erase:
2300
-
2301
- ```tsx
2302
- // Loop every 2 seconds (draws in first half, erases in second half)
2303
- <path style={tw('loop-stroke-dash-[300]/2000')} />
2304
-
2305
- // Faster loop
2306
- <path style={tw('loop-stroke-dash-[200]/1000')} />
2307
- ```
2308
-
2309
- ### Getting Path Length
2310
-
2311
- To find the path length for your SVG:
2312
-
2313
- ```tsx
2314
- // In browser console or component:
2315
- const path = document.querySelector('path');
2316
- const length = path.getTotalLength();
2317
- console.log(length); // e.g., 347.89
2318
- ```
2319
-
2320
- Then use that value:
2321
-
2322
- ```tsx
2323
- <path style={tw('enter-stroke-dash-[347.89]/0/1000')} />
2324
- ```
2325
-
2326
- ### Complete Example
2327
-
2328
- ```tsx
2329
- export default function DrawingEffect({ tw }) {
2330
- return (
2331
- <div style={tw('flex items-center justify-center w-full h-full bg-gray-900')}>
2332
- <svg width="600" height="400" viewBox="0 0 600 400">
2333
- {/* Checkmark icon drawn in sequence */}
2334
- <path
2335
- d="M100 200 L 200 300 L 400 100"
2336
- stroke="#10b981"
2337
- strokeWidth={8}
2338
- fill="none"
2339
- strokeLinecap="round"
2340
- strokeLinejoin="round"
2341
- style={tw('ease-out enter-stroke-dash-[600]/0/1200')}
2342
- />
2343
-
2344
- {/* Circle drawn after checkmark */}
2345
- <circle
2346
- cx="250"
2347
- cy="200"
2348
- r="150"
2349
- stroke="#10b981"
2350
- strokeWidth={6}
2351
- fill="none"
2352
- style={tw('ease-out enter-stroke-dash-[942]/1000/1000')}
2353
- />
2354
- </svg>
2355
- </div>
2356
- );
2357
- }
2358
- ```
2359
-
2360
- ### Combining with Other Animations
2361
-
2362
- Stroke animations work alongside other animation classes:
2363
-
2364
- ```tsx
2365
- // Fade in while drawing
2366
- <path style={tw('enter-stroke-dash-[300]/0/1000 enter-fade-in/0/1000')} />
2367
-
2368
- // Draw with pulsing color
2369
- <svg>
2370
- <path
2371
- stroke="url(#gradient)"
2372
- style={tw('enter-stroke-dash-[500]/0/1500')}
2373
- />
2374
- <defs>
2375
- <linearGradient id="gradient">
2376
- <stop offset="0%" stopColor="#8b5cf6" />
2377
- <stop offset="100%" stopColor="#ec4899" />
2378
- </linearGradient>
2379
- </defs>
2380
- </svg>
2381
- ```
2382
-
2383
- ### Animated Dashed Strokes (Marching Ants)
2384
-
2385
- For **marching ants** or **animated dashed patterns**, use `frame` or `progress` directly instead of animation classes:
2386
-
2387
- ```tsx
2388
- export default function MarchingAnts({ tw, frame }) {
2389
- // Calculate animated offset (loops every 30 frames)
2390
- const dashOffset = -(frame % 30) * 2;
2391
-
2392
- return (
2393
- <div style={tw('flex items-center justify-center w-full h-full bg-gray-900')}>
2394
- <svg width="600" height="400" viewBox="0 0 600 400">
2395
- {/* Marching ants border */}
2396
- <rect
2397
- x="50"
2398
- y="50"
2399
- width="500"
2400
- height="300"
2401
- fill="none"
2402
- stroke="#3b82f6"
2403
- strokeWidth={3}
2404
- strokeDasharray="10 5"
2405
- strokeDashoffset={dashOffset}
2406
- />
2407
-
2408
- {/* Animated circle with different speed */}
2409
- <circle
2410
- cx="300"
2411
- cy="200"
2412
- r="80"
2413
- fill="none"
2414
- stroke="#10b981"
2415
- strokeWidth={4}
2416
- strokeDasharray="15 8"
2417
- strokeDashoffset={dashOffset * 1.5}
2418
- />
2419
- </svg>
2420
- </div>
2421
- );
2422
- }
2423
- ```
2424
-
2425
- **Tips:**
2426
- - `strokeDasharray="10 5"` - 10px dash, 5px gap
2427
- - `strokeDashoffset={dashOffset}` - animates the pattern position
2428
- - Negative offset moves forward, positive moves backward
2429
- - Different speeds: multiply by different values (e.g., `dashOffset * 2`)
2430
-
2431
- This technique is different from `stroke-dash` classes:
2432
- - **`stroke-dash` classes** - Draw/erase the stroke (reveal animation)
2433
- - **Marching ants** - Move a dashed pattern along the stroke
2434
-
2435
- ## Performance Tips
2436
-
2437
- 1. **Use Tailwind classes** when possible - they're optimized for the renderer
2438
- 2. **Avoid too many nested animations** - each adds computation per frame
2439
- 3. **Use loop animations sparingly** - they're computed every frame
2440
- 4. **Prefer opacity and transform** - they're the most performant properties
2441
-
2442
- ## Next Steps
2443
-
2444
- - [Video Templates](/video) - Creating video templates
2445
- - [SDK](/sdk) - Programmatic rendering with animations
2446
- - [Helpers](/helpers) - QR codes, images, and more
2447
-
2448
-
2449
-
2450
-
2451
- ================================================================================
2452
- FILE: helpers.mdx
2453
- ================================================================================
2454
-
2455
- # Template Helpers
2456
-
2457
- Additional helpers for creating powerful, composable templates.
2458
-
2459
- ## Overview
2460
-
2461
- Beyond the basics, loopwind provides:
2462
- - `template()` - Compose templates together
2463
- - `qr()` - Generate QR codes on the fly
2464
- - `config` - Access user configuration
2465
-
2466
- For image embedding, see the [Images](/images) page.
2467
-
2468
- ## Template Composition
2469
-
2470
- Compose multiple templates together to create complex designs.
2471
-
2472
- ### Usage
2473
-
2474
- ```tsx
2475
- export default function CompositeCard({ tw, template, title, author, avatar }) {
2476
- return (
2477
- <div style={tw('w-full h-full bg-gradient-to-br from-purple-600 to-blue-500 p-12')}>
2478
- <div style={tw('bg-white rounded-2xl p-8 shadow-xl')}>
2479
- <h1 style={tw('text-4xl font-bold text-gray-900 mb-6')}>{title}</h1>
2480
-
2481
- {/* Embed another template */}
2482
- <div style={tw('mb-6')}>
2483
- {template('user-badge', {
2484
- name: author,
2485
- avatar: avatar
2486
- })}
2487
- </div>
2488
-
2489
- <p style={tw('text-gray-600')}>Published by {author}</p>
2490
- </div>
2491
- </div>
2492
- );
2493
- }
2494
- ```
2495
-
2496
- **How it works:**
2497
- 1. `template(name, props)` renders another installed template
2498
- 2. The embedded template is rendered at its specified size
2499
- 3. You can embed multiple templates in one design
2500
- 4. Templates can be nested (template within a template)
2501
-
2502
- ### Use Cases
2503
-
2504
- **1. Reusable components:**
2505
- ```tsx
2506
- // Create a logo template once, use it everywhere
2507
- <div>{template('company-logo', { variant: 'dark' })}</div>
2508
- ```
2509
-
2510
- **2. Complex layouts:**
2511
- ```tsx
2512
- // Combine multiple templates into one design
2513
- <div style={tw('grid grid-cols-2 gap-4')}>
2514
- {template('product-card', { product: product1 })}
2515
- {template('product-card', { product: product2 })}
2516
- </div>
2517
- ```
2518
-
2519
- **3. Dynamic content:**
2520
- ```tsx
2521
- // Render templates based on data
2522
- {users.map(user =>
2523
- template('user-avatar', { name: user.name, image: user.avatar })
2524
- )}
2525
- ```
2526
-
2527
- ### Best Practices
2528
-
2529
- 1. **Keep templates focused** - Each template should do one thing well
2530
- 2. **Pass minimal props** - Only pass what the embedded template needs
2531
- 3. **Document dependencies** - Note which templates are required in your README
2532
- 4. **Avoid deep nesting** - Too many nested templates can be hard to debug
2533
-
2534
- ## QR Codes
2535
-
2536
- Generate QR codes dynamically in your templates.
2537
-
2538
- ### Usage
2539
-
2540
- ```tsx
2541
- export default function QRCard({ tw, qr, title, url }) {
2542
- return (
2543
- <div style={tw('flex flex-col items-center justify-center w-full h-full bg-white p-10')}>
2544
- <h1 style={tw('text-4xl font-bold text-black mb-8')}>{title}</h1>
2545
-
2546
- {/* Generate QR code for the URL */}
2547
- <img src={qr(url)} style={tw('w-64 h-64')} />
2548
-
2549
- <p style={tw('text-gray-600 mt-4')}>{url}</p>
2550
- </div>
2551
- );
2552
- }
2553
- ```
2554
-
2555
- **Props format:**
2556
- ```json
2557
- {
2558
- "title": "Scan Me",
2559
- "url": "https://example.com"
2560
- }
2561
- ```
2562
-
2563
- ### QR Options
2564
-
2565
- You can customize QR code appearance:
2566
-
2567
- ```tsx
2568
- // Basic QR code
2569
- <img src={qr('https://example.com')} />
2570
-
2571
- // With error correction level
2572
- <img src={qr('https://example.com', { errorCorrectionLevel: 'H' })} />
2573
-
2574
- // With custom size
2575
- <img src={qr('https://example.com', { width: 512 })} />
2576
- ```
2577
-
2578
- **Error correction levels:**
2579
- - `L` - Low (~7% correction)
2580
- - `M` - Medium (~15% correction) - default
2581
- - `Q` - Quartile (~25% correction)
2582
- - `H` - High (~30% correction)
2583
-
2584
- ## User Configuration
2585
-
2586
- Access user settings from `_loopwind/loopwind.json` using the `config` prop:
2587
-
2588
- ```tsx
2589
- export default function BrandedTemplate({ tw, config, title }) {
2590
- // Access custom colors from loopwind.json
2591
- const primaryColor = config?.colors?.brand || '#6366f1';
2592
-
2593
- return (
2594
- <div style={tw('w-full h-full p-12')}>
2595
- <h1 style={{
2596
- ...tw('text-6xl font-bold'),
2597
- color: primaryColor
2598
- }}>
2599
- {title}
2600
- </h1>
2601
- </div>
2602
- );
2603
- }
2604
- ```
2605
-
2606
- **User's `_loopwind/loopwind.json`:**
2607
- ```json title="_loopwind/loopwind.json"
2608
- {
2609
- "colors": {
2610
- "brand": "#ff6b6b"
2611
- },
2612
- "fonts": {
2613
- "sans": ["Inter", "system-ui", "sans-serif"]
2614
- }
2615
- }
2616
- ```
2617
-
2618
- This allows templates to adapt to user preferences and brand guidelines.
2619
-
2620
- ## Text on Path
2621
-
2622
- Render text along curves, circles, and custom paths with automatic character positioning and rotation.
2623
-
2624
- ### Usage
2625
-
2626
- ```tsx
2627
- export default function CircleText({ tw, textPath, message }) {
2628
- return (
2629
- <div style={tw('relative w-full h-full bg-slate-900')}>
2630
- {textPath.onCircle(
2631
- message,
2632
- 960, // center x
2633
- 540, // center y
2634
- 400, // radius
2635
- 0, // rotation offset (0-1)
2636
- {
2637
- fontSize: "4xl",
2638
- fontWeight: "bold",
2639
- color: "white",
2640
- letterSpacing: 0.05
2641
- }
2642
- )}
2643
- </div>
2644
- );
2645
- }
2646
- ```
2647
-
2648
- ### Available Functions
2649
-
2650
- All `textPath` functions return an array of positioned character elements:
2651
-
2652
- **`textPath.onCircle(text, cx, cy, radius, offset, options?)`**
2653
- ```tsx
2654
- // Text around a circle
2655
- textPath.onCircle("HELLO WORLD", 960, 540, 400, 0, {
2656
- fontSize: "4xl",
2657
- color: "white"
2658
- })
2659
- ```
2660
-
2661
- **`textPath.onPath(text, pathFollower, options?)`**
2662
- ```tsx
2663
- // Text along any custom path
2664
- textPath.onPath("CUSTOM PATH", (t) => ({
2665
- x: 100 + t * 800,
2666
- y: 200 + Math.sin(t * Math.PI) * 100,
2667
- angle: Math.cos(t * Math.PI) * 20
2668
- }), {
2669
- fontSize: "2xl",
2670
- fontWeight: "semibold"
2671
- })
2672
- ```
2673
-
2674
- **`textPath.onQuadratic(text, p0, p1, p2, options?)`**
2675
- ```tsx
2676
- // Text along a quadratic Bezier curve
2677
- textPath.onQuadratic(
2678
- "CURVED TEXT",
2679
- { x: 200, y: 400 }, // start
2680
- { x: 960, y: 100 }, // control point
2681
- { x: 1720, y: 400 }, // end
2682
- { fontSize: "3xl", color: "blue-300" }
2683
- )
2684
- ```
2685
-
2686
- **`textPath.onCubic(text, p0, p1, p2, p3, options?)`**
2687
- ```tsx
2688
- // Text along a cubic Bezier curve
2689
- textPath.onCubic(
2690
- "S-CURVE",
2691
- { x: 200, y: 600 }, // start
2692
- { x: 600, y: 400 }, // control 1
2693
- { x: 1320, y: 800 }, // control 2
2694
- { x: 1720, y: 600 }, // end
2695
- { fontSize: "3xl", color: "purple-300" }
2696
- )
2697
- ```
2698
-
2699
- **`textPath.onArc(text, cx, cy, radius, startAngle, endAngle, options?)`**
2700
- ```tsx
2701
- // Text along a circular arc
2702
- textPath.onArc(
2703
- "ARC TEXT",
2704
- 960, // center x
2705
- 540, // center y
2706
- 400, // radius
2707
- 0, // start angle (degrees)
2708
- 180, // end angle (degrees)
2709
- { fontSize: "2xl", color: "pink-300" }
2710
- )
2711
- ```
2712
-
2713
- ### Options
2714
-
2715
- All `textPath` functions accept an optional `options` object:
2716
-
2717
- ```typescript
2718
- {
2719
- fontSize?: string; // Tailwind size: "xl", "2xl", "4xl", etc.
2720
- fontWeight?: string; // Tailwind weight: "bold", "semibold", etc.
2721
- color?: string; // Tailwind color: "white", "blue-500", etc.
2722
- letterSpacing?: number; // Space between characters (0-1, default: 0)
2723
- style?: any; // Additional inline styles
2724
- }
2725
- ```
2726
-
2727
- ### Examples
2728
-
2729
- **Animated rotating text:**
2730
- ```tsx
2731
- export default function RotatingText({ tw, textPath, progress }) {
2732
- return (
2733
- <div style={tw('relative w-full h-full bg-black')}>
2734
- {textPath.onCircle(
2735
- "SPINNING • TEXT • ",
2736
- 960, 540, 400,
2737
- progress, // Rotate based on video progress
2738
- { fontSize: "3xl", color: "yellow-300" }
2739
- )}
2740
- </div>
2741
- );
2742
- }
2743
- ```
2744
-
2745
- **Multiple text paths:**
2746
- ```tsx
2747
- export default function MultiPath({ tw, textPath }) {
2748
- return (
2749
- <div style={tw('relative w-full h-full bg-gradient-to-br from-slate-900 to-slate-700')}>
2750
- {/* Text on outer circle */}
2751
- {textPath.onCircle(
2752
- "OUTER RING",
2753
- 960, 540, 500, 0,
2754
- { fontSize: "5xl", fontWeight: "bold", color: "white" }
2755
- )}
2756
-
2757
- {/* Text on inner circle */}
2758
- {textPath.onCircle(
2759
- "inner ring",
2760
- 960, 540, 300, 0.5, // offset by 50% for rotation
2761
- { fontSize: "2xl", color: "white/60" }
2762
- )}
2763
- </div>
2764
- );
2765
- }
2766
- ```
2767
-
2768
- **Text following a drawn path:**
2769
- ```tsx
2770
- export default function PathText({ tw, textPath }) {
2771
- return (
2772
- <div style={tw('relative w-full h-full bg-gray-900')}>
2773
- {/* Draw the path */}
2774
- <svg width="1920" height="1080" style={{ position: 'absolute' }}>
2775
- <path
2776
- d="M 200 400 Q 960 150 1720 400"
2777
- stroke="rgba(255,255,255,0.2)"
2778
- strokeWidth={2}
2779
- fill="none"
2780
- />
2781
- </svg>
2782
-
2783
- {/* Text following the path */}
2784
- {textPath.onQuadratic(
2785
- "FOLLOWING THE CURVE",
2786
- { x: 200, y: 400 },
2787
- { x: 960, y: 150 },
2788
- { x: 1720, y: 400 },
2789
- { fontSize: "3xl", fontWeight: "bold", color: "blue-300" }
2790
- )}
2791
- </div>
2792
- );
2793
- }
2794
- ```
2795
-
2796
- For animated text paths, see [Text Path Animations](/animation#text-path-animations).
2797
-
2798
- ## Reserved Prop Names
2799
-
2800
- The following prop names are **reserved** and cannot be used in your template's `meta.props`:
2801
-
2802
- - `tw`, `qr`, `image`, `template` - Core helpers
2803
- - `path`, `textPath` - Path and text helpers
2804
- - `config`, `frame`, `progress` - System props
2805
-
2806
- **Why?** These names are used for loopwind's built-in helpers. Using them as prop names would cause conflicts.
2807
-
2808
- **Example:**
2809
- ```tsx
2810
- // ❌ BAD - 'image' is reserved
2811
- export const meta = {
2812
- props: {
2813
- title: "string",
2814
- image: "string" // Error!
2815
- }
2816
- };
2817
-
2818
- // ✅ GOOD - Use descriptive alternatives
2819
- export const meta = {
2820
- props: {
2821
- title: "string",
2822
- imageUrl: "string", // or imageSrc, photoUrl, etc.
2823
- logoUrl: "string"
2824
- }
2825
- };
2826
- ```
2827
-
2828
- If you try to use a reserved name, you'll get a helpful error:
2829
- ```
2830
- Template uses reserved prop names: image
2831
-
2832
- Try renaming: "image" → "imageUrl" or "imageSrc"
2833
-
2834
- Reserved names: tw, qr, image, template, path, textPath, config, frame, progress
2835
- ```
2836
-
2837
- ## All Props Reference
2838
-
2839
- Every template receives these props:
2840
-
2841
- ```tsx
2842
- export default function MyTemplate({
2843
- // Core helpers (RESERVED - cannot be used as prop names)
2844
- tw, // Tailwind class converter
2845
- qr, // QR code generator (this page)
2846
- template, // Template composer (this page)
2847
- config, // User config from loopwind.json (this page)
2848
- textPath, // Text on path helpers (this page)
2849
-
2850
- // Media helpers (RESERVED)
2851
- image, // Image embedder → see /images
2852
- path, // Path following → see /animation
2853
-
2854
- // Video-specific (RESERVED - only in video templates)
2855
- frame, // Current frame number → see /video
2856
- progress, // Animation progress 0-1 → see /video
2857
-
2858
- // Your custom props (use any names EXCEPT the reserved ones above)
2859
- ...props // Any props from your meta.props
2860
- }) {
2861
- // Your template code
2862
- }
2863
- ```
2864
-
2865
- ## Next Steps
2866
-
2867
- - [Image Rendering and Embedding](/images)
2868
- - [Video Animation](/video)
2869
- - [Styling with Tailwind & shadcn/ui](/styling)
2870
- - [Custom Fonts](/fonts)
2871
-
2872
-
2873
-
2874
-
2875
-
2876
- ================================================================================
2877
- FILE: styling.mdx
2878
- ================================================================================
2879
-
2880
- # Styling Templates
2881
-
2882
- Style your templates with Tailwind utility classes and shadcn/ui's beautiful design system.
2883
-
2884
- ## Quick Start
2885
-
2886
- ```tsx
2887
- export default function MyTemplate({ title, tw }) {
2888
- return (
2889
- <div style={tw('flex items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
2890
- <h1 style={tw('text-7xl font-bold text-white')}>
2891
- {title}
2892
- </h1>
2893
- </div>
2894
- );
2895
- }
2896
- ```
2897
-
2898
- ## The `tw()` Function
2899
-
2900
- Every template receives a `tw()` function that converts Tailwind classes to inline styles compatible with Satori:
2901
-
2902
- ```tsx
2903
- // Tailwind classes
2904
- tw('flex items-center justify-center p-8 bg-blue-500')
2905
-
2906
- // Converts to inline styles:
2907
- {
2908
- display: 'flex',
2909
- alignItems: 'center',
2910
- justifyContent: 'center',
2911
- padding: '2rem',
2912
- backgroundColor: '#3b82f6'
2913
- }
2914
- ```
2915
-
2916
- ### Basic Usage
2917
-
2918
- ```tsx
2919
- export default function Banner({ title, subtitle, tw }) {
2920
- return (
2921
- <div style={tw('w-full h-full p-12 bg-gray-50')}>
2922
- <h1 style={tw('text-6xl font-bold text-gray-900 mb-4')}>
2923
- {title}
2924
- </h1>
2925
- <p style={tw('text-2xl text-gray-600')}>
2926
- {subtitle}
2927
- </p>
2928
- </div>
2929
- );
2930
- }
2931
- ```
2932
-
2933
- ### Combining with Custom Styles
2934
-
2935
- Mix Tailwind classes with custom styles using the spread operator:
2936
-
2937
- ```tsx
2938
- export default function CustomGradient({ title, tw }) {
2939
- return (
2940
- <div
2941
- style={{
2942
- ...tw('flex flex-col items-center justify-center w-full h-full p-20'),
2943
- background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
2944
- }}
2945
- >
2946
- <h1 style={tw('text-8xl font-bold text-white')}>{title}</h1>
2947
- </div>
2948
- );
2949
- }
2950
- ```
2951
-
2952
- ## shadcn/ui Design System
2953
-
2954
- loopwind uses **shadcn/ui's design system** by default, providing semantic color tokens for beautiful, consistent designs.
2955
-
2956
- ### Default Color Palette
2957
-
2958
- All templates automatically have access to these semantic colors defined in `_loopwind/loopwind.json`:
2959
-
2960
- ```typescript
2961
- colors: {
2962
- // Primary colors
2963
- primary: '#18181b', // Main brand color
2964
- 'primary-foreground': '#fafafa',
2965
-
2966
- // Secondary colors
2967
- secondary: '#f4f4f5', // Subtle accents
2968
- 'secondary-foreground': '#18181b',
2969
-
2970
- // Background
2971
- background: '#ffffff', // Page background
2972
- foreground: '#09090b', // Main text color
2973
-
2974
- // Muted
2975
- muted: '#f4f4f5', // Subtle backgrounds
2976
- 'muted-foreground': '#71717a', // Muted text
2977
-
2978
- // Accent
2979
- accent: '#f4f4f5', // Highlight color
2980
- 'accent-foreground': '#18181b',
2981
-
2982
- // Destructive
2983
- destructive: '#ef4444', // Error/danger states
2984
- 'destructive-foreground': '#fafafa',
2985
-
2986
- // UI Elements
2987
- border: '#e4e4e7', // Border color
2988
- input: '#e4e4e7', // Input borders
2989
- ring: '#18181b', // Focus rings
2990
- card: '#ffffff', // Card background
2991
- 'card-foreground': '#09090b',
2992
- }
2993
- ```
2994
-
2995
- ### Using Semantic Colors
2996
-
2997
- ```tsx
2998
- export default function SemanticCard({ title, description, price, tw }) {
2999
- return (
3000
- <div style={tw('bg-card border border-border rounded-lg p-6')}>
3001
- <h2 style={tw('text-card-foreground text-2xl font-bold mb-2')}>
3002
- {title}
3003
- </h2>
3004
- <p style={tw('text-muted-foreground mb-4')}>
3005
- {description}
3006
- </p>
3007
- <div style={tw('text-primary text-3xl font-bold')}>
3008
- ${price}
3009
- </div>
3010
- </div>
3011
- );
3012
- }
3013
- ```
3014
-
3015
- ### Opacity Modifiers
3016
-
3017
- Use Tailwind's slash syntax for opacity with any color:
3018
-
3019
- ```tsx
3020
- export default function OpacityExample({ tw }) {
3021
- return (
3022
- <div style={tw('bg-primary/50')}> {/* 50% opacity */}
3023
- <p style={tw('text-muted-foreground/75')}> {/* 75% opacity */}
3024
- Subtle text
3025
- </p>
3026
- <div style={tw('border border-border/30')}> {/* 30% opacity */}
3027
- Faint border
3028
- </div>
3029
- </div>
3030
- );
3031
- }
3032
- ```
3033
-
3034
- **Supported syntax:**
3035
- - `bg-{color}/{opacity}` - Background with opacity
3036
- - `text-{color}/{opacity}` - Text with opacity
3037
- - `border-{color}/{opacity}` - Border with opacity
3038
-
3039
- ### Text Hierarchy
3040
-
3041
- ```tsx
3042
- // Primary text
3043
- tw('text-foreground')
3044
-
3045
- // Secondary/muted text
3046
- tw('text-muted-foreground')
3047
-
3048
- // Accent/brand text
3049
- tw('text-primary')
3050
-
3051
- // Destructive/error text
3052
- tw('text-destructive')
3053
- ```
3054
-
3055
- ### Backgrounds
3056
-
3057
- ```tsx
3058
- // Page background
3059
- tw('bg-background')
3060
-
3061
- // Card/elevated surfaces
3062
- tw('bg-card')
3063
-
3064
- // Subtle backgrounds
3065
- tw('bg-muted')
3066
-
3067
- // Accent backgrounds
3068
- tw('bg-accent')
3069
- ```
3070
-
3071
- ## Supported Tailwind Classes
3072
-
3073
- ### Layout
3074
-
3075
- - **Display**: `flex`, `inline-flex`, `block`, `inline-block`, `hidden`
3076
- - **Flex Direction**: `flex-row`, `flex-col`, `flex-row-reverse`, `flex-col-reverse`
3077
- - **Justify**: `justify-start`, `justify-end`, `justify-center`, `justify-between`, `justify-around`
3078
- - **Align**: `items-start`, `items-end`, `items-center`, `items-baseline`, `items-stretch`
3079
-
3080
- ### Spacing
3081
-
3082
- - **Padding**: `p-{n}`, `px-{n}`, `py-{n}`, `pt-{n}`, `pb-{n}`, `pl-{n}`, `pr-{n}`
3083
- - **Margin**: `m-{n}`, `mx-{n}`, `my-{n}`, `mt-{n}`, `mb-{n}`, `ml-{n}`, `mr-{n}`
3084
- - **Gap**: `gap-{n}`, `gap-x-{n}`, `gap-y-{n}`
3085
- - **Sizes**: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 56, 64
3086
-
3087
- Examples:
3088
- ```tsx
3089
- tw('p-4') // padding: 1rem
3090
- tw('px-8') // paddingLeft: 2rem, paddingRight: 2rem
3091
- tw('m-6') // margin: 1.5rem
3092
- tw('gap-4') // gap: 1rem
3093
- ```
3094
-
3095
- ### Sizing
3096
-
3097
- - **Width**: `w-{n}`, `w-full`, `w-screen`, `w-1/2`, `w-1/3`, `w-2/3`
3098
- - **Height**: `h-{n}`, `h-full`, `h-screen`
3099
-
3100
- Examples:
3101
- ```tsx
3102
- tw('w-full') // width: 100%
3103
- tw('h-64') // height: 16rem
3104
- tw('w-1/2') // width: 50%
3105
- ```
3106
-
3107
- ### Typography
3108
-
3109
- - **Font Size**: `text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`, `text-3xl`, `text-4xl`, `text-5xl`, `text-6xl`, `text-7xl`, `text-8xl`, `text-9xl`
3110
- - **Font Weight**: `font-thin`, `font-light`, `font-normal`, `font-medium`, `font-semibold`, `font-bold`, `font-extrabold`, `font-black`
3111
- - **Text Align**: `text-left`, `text-center`, `text-right`
3112
- - **Line Height**: `leading-none`, `leading-tight`, `leading-normal`, `leading-relaxed`, `leading-loose`
3113
-
3114
- ### Colors
3115
-
3116
- All standard Tailwind colors plus shadcn semantic colors:
3117
-
3118
- **Standard colors:**
3119
- - `text-{color}-{shade}`, `bg-{color}-{shade}`, `border-{color}-{shade}`
3120
- - Colors: `red`, `blue`, `green`, `yellow`, `purple`, `pink`, `gray`, `indigo`, `teal`, `orange`
3121
- - Shades: `50`, `100`, `200`, `300`, `400`, `500`, `600`, `700`, `800`, `900`
3122
-
3123
- **shadcn semantic colors:**
3124
- - `text-foreground`, `text-primary`, `text-muted-foreground`, `text-destructive`
3125
- - `bg-background`, `bg-card`, `bg-muted`, `bg-accent`, `bg-primary`
3126
- - `border-border`, `border-input`
3127
-
3128
- ```tsx
3129
- tw('text-blue-500') // Standard Tailwind color
3130
- tw('bg-purple-600') // Standard Tailwind color
3131
- tw('text-primary') // shadcn semantic color
3132
- tw('bg-card') // shadcn semantic color
3133
- ```
3134
-
3135
- ### Position & Layout
3136
-
3137
- - **Position**: `relative`, `absolute`, `fixed`, `sticky`
3138
- - **Inset**: `inset-0`, `top-0`, `bottom-0`, `left-0`, `right-0`
3139
- - **Z-Index**: `z-0`, `z-10`, `z-20`, `z-30`, `z-40`, `z-50`
3140
-
3141
- ### Borders
3142
-
3143
- - **Border Width**: `border`, `border-{n}`, `border-t`, `border-b`, `border-l`, `border-r`
3144
- - **Border Radius**: `rounded`, `rounded-sm`, `rounded-md`, `rounded-lg`, `rounded-xl`, `rounded-2xl`, `rounded-3xl`, `rounded-full`
3145
- - **Border Color**: `border-{color}-{shade}`, `border-border`, `border-input`
3146
-
3147
- ### Effects
3148
-
3149
- - **Shadow**: `shadow-sm`, `shadow`, `shadow-md`, `shadow-lg`, `shadow-xl`, `shadow-2xl`
3150
- - **Opacity**: `opacity-0`, `opacity-25`, `opacity-50`, `opacity-75`, `opacity-100`
3151
-
3152
- ### Filters
3153
-
3154
- - **Blur**: `blur-none`, `blur-sm`, `blur`, `blur-md`, `blur-lg`, `blur-xl`
3155
- - **Brightness**: `brightness-0`, `brightness-50`, `brightness-100`, `brightness-150`, `brightness-200`
3156
- - **Contrast**: `contrast-0`, `contrast-50`, `contrast-100`, `contrast-150`, `contrast-200`
3157
-
3158
- ## Gradients
3159
-
3160
- ### Linear Gradients
3161
-
3162
- ```tsx
3163
- // Gradient direction
3164
- tw('bg-gradient-to-r') // left to right
3165
- tw('bg-gradient-to-br') // top-left to bottom-right
3166
- tw('bg-gradient-to-t') // bottom to top
3167
-
3168
- // Gradient colors
3169
- tw('from-blue-500') // Start color
3170
- tw('via-purple-500') // Middle color
3171
- tw('to-pink-500') // End color
3172
-
3173
- // Complete gradient
3174
- tw('bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500')
3175
- ```
3176
-
3177
- ### Gradient Examples
3178
-
3179
- ```tsx
3180
- export default function GradientCard({ title, tw }) {
3181
- return (
3182
- <div style={tw('w-full h-full bg-gradient-to-br from-cyan-500 to-blue-600 p-12')}>
3183
- <h1 style={tw('text-white text-6xl font-bold')}>
3184
- {title}
3185
- </h1>
3186
- </div>
3187
- );
3188
- }
3189
- ```
3190
-
3191
- ## Custom Theme Colors
3192
-
3193
- You can override the default shadcn colors or add your own custom colors in `_loopwind/loopwind.json`:
3194
-
3195
- ```json title="_loopwind/loopwind.json"
3196
- {
3197
- "theme": {
3198
- "colors": {
3199
- "primary": "#3b82f6",
3200
- "primary-foreground": "#ffffff",
3201
- "accent": "#10b981",
3202
- "brand": "#ff6b6b"
3203
- }
3204
- }
3205
- }
3206
- ```
3207
-
3208
- Then use these custom colors in your templates:
3209
-
3210
- ```tsx
3211
- tw('text-brand') // Uses your custom brand color
3212
- tw('bg-primary') // Uses your custom primary color
3213
- tw('bg-accent') // Uses your custom accent color
3214
- ```
3215
-
3216
- ## Auto-Detection from tailwind.config.js
3217
-
3218
- loopwind automatically detects and loads your project's Tailwind configuration:
3219
-
3220
- ```
3221
- your-project/
3222
- ├── tailwind.config.js ← Automatically detected
3223
- └── _loopwind/
3224
- ├── loopwind.json
3225
- └── templates/
3226
- ```
3227
-
3228
- This includes:
3229
- - Custom colors
3230
- - Custom spacing values
3231
- - Custom fonts
3232
- - Theme extensions
3233
- - Custom utilities
3234
-
3235
- ## Complete Example
3236
-
3237
- ```tsx
3238
- export default function ModernCard({
3239
- tw,
3240
- image,
3241
- title,
3242
- description,
3243
- category,
3244
- author,
3245
- avatar
3246
- }) {
3247
- return (
3248
- <div style={tw('w-full h-full bg-card')}>
3249
- {/* Hero image */}
3250
- <div style={tw('relative h-2/3')}>
3251
- <img
3252
- src={image(hero)}
3253
- style={tw('w-full h-full object-cover')}
3254
- />
3255
- {/* Category badge */}
3256
- <div style={tw('absolute top-4 left-4 bg-primary/90 backdrop-blur px-4 py-2 rounded-full')}>
3257
- <span style={tw('text-sm font-semibold text-primary-foreground')}>
3258
- {category}
3259
- </span>
3260
- </div>
3261
- </div>
3262
-
3263
- {/* Content */}
3264
- <div style={tw('h-1/3 p-8 flex flex-col justify-between')}>
3265
- <div>
3266
- <h2 style={tw('text-3xl font-bold text-foreground mb-2')}>
3267
- {title}
3268
- </h2>
3269
- <p style={tw('text-muted-foreground line-clamp-2')}>
3270
- {description}
3271
- </p>
3272
- </div>
3273
-
3274
- {/* Author */}
3275
- <div style={tw('flex items-center gap-3')}>
3276
- <img
3277
- src={image(avatar)}
3278
- style={tw('w-10 h-10 rounded-full border-2 border-border')}
3279
- />
3280
- <span style={tw('text-sm text-muted-foreground')}>
3281
- {author}
3282
- </span>
3283
- </div>
3284
- </div>
3285
- </div>
3286
- );
3287
- }
3288
- ```
3289
-
3290
- ## Why This Approach?
3291
-
3292
- - **Semantic naming**: `text-primary` instead of `text-blue-600`
3293
- - **Consistency**: All templates use the same design language
3294
- - **Flexibility**: Easy to customize entire theme
3295
- - **Accessibility**: Pre-tested color contrasts
3296
- - **Modern**: Same system as shadcn/ui components
3297
- - **Familiar**: Standard Tailwind syntax
3298
-
3299
- ## Next Steps
3300
-
3301
- - [Custom Fonts](/fonts)
3302
- - [Built-in Helpers](/helpers)
3303
- - [Image Rendering](/images)
3304
- - [Video Rendering](/video)
3305
-
3306
-
3307
-
3308
-
3309
-
3310
- ================================================================================
3311
- FILE: fonts.mdx
3312
- ================================================================================
3313
-
3314
- # Font Handling in loopwind
3315
-
3316
- The recommended way to use fonts is through `loopwind.json` - configure fonts once, use everywhere.
3317
-
3318
- ## Using Fonts from loopwind.json (Recommended)
3319
-
3320
- Configure fonts in your `_loopwind/loopwind.json` and use Tailwind classes in templates.
3321
-
3322
- ### Simple Setup
3323
-
3324
- Define font families in `_loopwind/loopwind.json` without loading custom fonts (uses system fonts):
3325
-
3326
- ```json title="_loopwind/loopwind.json"
3327
- {
3328
- "fonts": {
3329
- "sans": ["Inter", "system-ui", "-apple-system", "sans-serif"],
3330
- "serif": ["Georgia", "serif"],
3331
- "mono": ["Courier New", "monospace"]
3332
- }
3333
- }
3334
- ```
3335
-
3336
- **Template usage:**
3337
- ```tsx
3338
- export default function({ title, tw }) {
3339
- return (
3340
- <div style={tw('w-full h-full')}>
3341
- {/* Uses fonts.sans from loopwind.json */}
3342
- <h1 style={tw('font-sans text-6xl font-bold')}>
3343
- {title}
3344
- </h1>
3345
-
3346
- {/* Uses fonts.mono from loopwind.json */}
3347
- <code style={tw('font-mono text-sm')}>
3348
- {code}
3349
- </code>
3350
- </div>
3351
- );
3352
- }
3353
- ```
3354
-
3355
- **Result:** Uses system fonts, falls back to Inter for rendering.
3356
-
3357
- ### Complete Setup (With Font Files)
3358
-
3359
- Load custom font files for brand-specific typography in `_loopwind/loopwind.json`:
3360
-
3361
- ```json title="_loopwind/loopwind.json"
3362
- {
3363
- "fonts": {
3364
- "sans": {
3365
- "family": ["Inter", "system-ui", "sans-serif"],
3366
- "files": [
3367
- { "path": "./fonts/Inter-Regular.woff", "weight": 400 },
3368
- { "path": "./fonts/Inter-Bold.woff", "weight": 700 }
3369
- ]
3370
- },
3371
- "mono": {
3372
- "family": ["JetBrains Mono", "monospace"],
3373
- "files": [
3374
- { "path": "./fonts/JetBrainsMono-Regular.woff", "weight": 400 }
3375
- ]
3376
- }
3377
- }
3378
- }
3379
- ```
3380
-
3381
- **Project structure:**
3382
- ```
3383
- your-project/
3384
- ├── _loopwind/
3385
- │ ├── loopwind.json
3386
- │ └── templates/
3387
- └── fonts/
3388
- ├── Inter-Regular.woff
3389
- ├── Inter-Bold.woff
3390
- └── JetBrainsMono-Regular.woff
3391
- ```
3392
-
3393
- **Template usage (same as before):**
3394
- ```tsx
3395
- <h1 style={tw('font-sans font-bold')}>
3396
- {/* Uses Inter Bold from loopwind.json */}
3397
- {title}
3398
- </h1>
3399
- ```
3400
-
3401
- **Available classes:**
3402
- - `font-sans` - Uses `fonts.sans` from loopwind.json
3403
- - `font-serif` - Uses `fonts.serif` from loopwind.json
3404
- - `font-mono` - Uses `fonts.mono` from loopwind.json
3405
-
3406
- **Supported formats:**
3407
- - ✅ **WOFF** (`.woff`) - Recommended for best compatibility
3408
- - ✅ **TTF** (`.ttf`) - Also supported
3409
- - ✅ **OTF** (`.otf`) - Also supported
3410
- - ❌ **WOFF2** (`.woff2`) - Not supported by renderer
3411
-
3412
- ## Font Loading Priority
3413
-
3414
- loopwind loads fonts in this order:
3415
-
3416
- 1. **loopwind.json fonts** (if configured with `files`)
3417
- 2. **Bundled Inter fonts** (included with CLI)
3418
-
3419
- This ensures fonts work out of the box with no configuration.
3420
-
3421
- ## Default Fonts
3422
-
3423
- If no fonts are configured, loopwind uses **Inter** (Regular 400, Bold 700) which is bundled with the CLI. This means fonts work offline with no configuration required.
3424
-
3425
- ## Best Practices
3426
-
3427
- 1. ✅ **Use loopwind.json for project-wide fonts** - Configure once, use everywhere
3428
- 2. ✅ **Use font classes** - `tw('font-sans')` instead of `fontFamily: 'Inter'`
3429
- 3. ✅ **Include fallbacks** - Always add system fonts: `["Inter", "system-ui", "sans-serif"]`
3430
- 4. ✅ **Match names** - First font in `family` array is used as the loaded font name
3431
- 5. ✅ **Relative paths** - Font paths are relative to `loopwind.json` location
3432
-
3433
- ## Examples
3434
-
3435
- ### Minimal Setup (System Fonts)
3436
- ```json title="_loopwind/loopwind.json"
3437
- {
3438
- "fonts": {
3439
- "sans": ["Inter", "-apple-system", "sans-serif"]
3440
- }
3441
- }
3442
- ```
3443
- Uses system Inter if available, falls back to Noto Sans for rendering.
3444
-
3445
- ### Brand Fonts Setup
3446
- ```json title="_loopwind/loopwind.json"
3447
- {
3448
- "fonts": {
3449
- "sans": {
3450
- "family": ["Montserrat", "sans-serif"],
3451
- "files": [
3452
- { "path": "./fonts/Montserrat-Regular.woff", "weight": 400 },
3453
- { "path": "./fonts/Montserrat-Bold.woff", "weight": 700 }
3454
- ]
3455
- }
3456
- }
3457
- }
3458
- ```
3459
- Loads and uses Montserrat for all templates.
3460
-
3461
- ### Multi-Font Setup
3462
- ```json title="_loopwind/loopwind.json"
3463
- {
3464
- "fonts": {
3465
- "sans": {
3466
- "family": ["Inter", "sans-serif"],
3467
- "files": [
3468
- { "path": "./fonts/Inter-Regular.woff", "weight": 400 },
3469
- { "path": "./fonts/Inter-Bold.woff", "weight": 700 }
3470
- ]
3471
- },
3472
- "serif": {
3473
- "family": ["Playfair Display", "serif"],
3474
- "files": [
3475
- { "path": "./fonts/Playfair-Regular.woff", "weight": 400 }
3476
- ]
3477
- },
3478
- "mono": {
3479
- "family": ["Fira Code", "monospace"],
3480
- "files": [
3481
- { "path": "./fonts/FiraCode-Regular.woff", "weight": 400 }
3482
- ]
3483
- }
3484
- }
3485
- }
3486
- ```
3487
- Loads different fonts for each style class.
3488
-
3489
- ### External Font URLs
3490
- Load fonts directly from CDNs without downloading files:
3491
-
3492
- ```json title="_loopwind/loopwind.json"
3493
- {
3494
- "fonts": {
3495
- "sans": {
3496
- "family": ["Inter", "sans-serif"],
3497
- "files": [
3498
- {
3499
- "path": "https://unpkg.com/@fontsource/inter@5.0.18/files/inter-latin-400-normal.woff",
3500
- "weight": 400
3501
- },
3502
- {
3503
- "path": "https://unpkg.com/@fontsource/inter@5.0.18/files/inter-latin-700-normal.woff",
3504
- "weight": 700
3505
- }
3506
- ]
3507
- }
3508
- }
3509
- }
3510
- ```
3511
-
3512
- You can also mix local and external fonts:
3513
-
3514
- ```json title="_loopwind/loopwind.json"
3515
- {
3516
- "fonts": {
3517
- "sans": {
3518
- "family": ["Inter", "sans-serif"],
3519
- "files": [
3520
- { "path": "./fonts/Inter-Regular.woff", "weight": 400 },
3521
- {
3522
- "path": "https://unpkg.com/@fontsource/inter@5.0.18/files/inter-latin-700-normal.woff",
3523
- "weight": 700
3524
- }
3525
- ]
3526
- }
3527
- }
3528
- }
3529
- ```
3530
-
3531
- **Note:** Use WOFF format (`.woff`) for best compatibility. WOFF2 is not supported by the underlying renderer.
3532
-
3533
- ## Performance
3534
-
3535
- - ✅ **Font caching** - Fonts load once and are cached for all renders
3536
- - ✅ **Video optimization** - 90-frame video loads fonts once, not 90 times
3537
- - ✅ **No CDN delays** - Local fonts load instantly
3538
-
3539
- ## Next Steps
3540
-
3541
- - [Styling with Tailwind & shadcn/ui](/styling)
3542
- - [Built-in Helpers](/helpers)
3543
- - [Image Rendering](/images)
3544
- - [Video Rendering](/video)
3545
-
3546
-
3547
-
3548
-
3549
- ================================================================================
3550
- FILE: agents.mdx
3551
- ================================================================================
3552
-
3553
- # AI Agents
3554
-
3555
- loopwind is designed to work with AI coding assistants. Simply add the `_loopwind` folder to context.
3556
-
3557
- ## Using with AI Code Agents
3558
-
3559
- In Cursor or Claude Code (Cline), type `@_loopwind` to add the folder to context, then ask:
3560
-
3561
- ```
3562
- Generate an Open Graph image for my blog post about React hooks
3563
- ```
3564
-
3565
- The AI will read the templates and generate the appropriate command.
3566
-
3567
- ## The AGENTS.md File
3568
-
3569
- When you run `loopwind init`, a `_loopwind/AGENTS.md` file is created with:
3570
- - Essential commands
3571
- - Template helpers reference
3572
- - Common workflows
3573
-
3574
- This gives AI agents everything they need to use loopwind effectively.
3575
-
3576
- ## Example Workflows
3577
-
3578
- ### Simple Image Generation
3579
-
3580
- **Prompt:**
3581
- ```
3582
- Create a social media card using the og-image template:
3583
- - Title: "10 React Tips"
3584
- - Description: "Learn advanced patterns"
3585
- ```
3586
-
3587
- **AI generates:**
3588
- ```bash
3589
- loopwind render og-image '{"title":"10 React Tips","description":"Learn advanced patterns"}'
3590
- ```
3591
-
3592
- **Output:** `_loopwind/outputs/og-image.png`
3593
-
3594
- ### Changelog Image
3595
-
3596
- **Prompt:**
3597
- ```
3598
- Create a changelog image for 3 recent changes:
3599
- - Added video rendering support
3600
- - Improved template validation
3601
- - Fixed font loading issues
3602
- ```
3603
-
3604
- **AI generates:**
3605
- ```bash
3606
- loopwind render changelog-card '{
3607
- "title":"Version 1.2.0",
3608
- "changes":[
3609
- "Added video rendering support",
3610
- "Improved template validation",
3611
- "Fixed font loading issues"
3612
- ]
3613
- }'
3614
- ```
3615
-
3616
- **Output:** `_loopwind/outputs/changelog-card.png`
3617
-
3618
- ## Next Steps
3619
-
3620
- - [Learn about Templates](/templates)
3621
- - [Image Rendering](/images)
3622
- - [Video Rendering](/video)
3623
- - [Built-in Helpers](/helpers)
3624
-
3625
-
3626
-