loopwind 0.22.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -117
- package/dist/cli.js +16 -10
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +6 -10
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/agent.d.ts +8 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +101 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +17 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/render.d.ts.map +1 -1
- package/dist/commands/render.js +17 -19
- package/dist/commands/render.js.map +1 -1
- package/dist/default-templates/AGENTS.md +10 -24
- package/dist/lib/config.d.ts +2 -3
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +4 -9
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/constants.d.ts +1 -3
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +2 -4
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/installer.d.ts +2 -0
- package/dist/lib/installer.d.ts.map +1 -1
- package/dist/lib/installer.js +6 -7
- package/dist/lib/installer.js.map +1 -1
- package/dist/lib/renderer.d.ts.map +1 -1
- package/dist/lib/renderer.js +14 -8
- package/dist/lib/renderer.js.map +1 -1
- package/dist/lib/resvg-init.d.ts +15 -0
- package/dist/lib/resvg-init.d.ts.map +1 -0
- package/dist/lib/resvg-init.js +55 -0
- package/dist/lib/resvg-init.js.map +1 -0
- package/dist/lib/tailwind/colors.d.ts +8 -0
- package/dist/lib/tailwind/colors.d.ts.map +1 -0
- package/dist/lib/tailwind/colors.js +102 -0
- package/dist/lib/tailwind/colors.js.map +1 -0
- package/dist/lib/tailwind/index.d.ts +10 -0
- package/dist/lib/tailwind/index.d.ts.map +1 -0
- package/dist/lib/tailwind/index.js +9 -0
- package/dist/lib/tailwind/index.js.map +1 -0
- package/dist/lib/tailwind/resolvers.d.ts +28 -0
- package/dist/lib/tailwind/resolvers.d.ts.map +1 -0
- package/dist/lib/tailwind/resolvers.js +94 -0
- package/dist/lib/tailwind/resolvers.js.map +1 -0
- package/dist/lib/tailwind/types.d.ts +29 -0
- package/dist/lib/tailwind/types.d.ts.map +1 -0
- package/dist/lib/tailwind/types.js +8 -0
- package/dist/lib/tailwind/types.js.map +1 -0
- package/dist/lib/tailwind-config-loader.d.ts +8 -45
- package/dist/lib/tailwind-config-loader.d.ts.map +1 -1
- package/dist/lib/tailwind-config-loader.js +6 -429
- package/dist/lib/tailwind-config-loader.js.map +1 -1
- package/dist/lib/tailwind.d.ts +1 -1
- package/dist/lib/tailwind.d.ts.map +1 -1
- package/dist/lib/tailwind.js +1 -1
- package/dist/lib/tailwind.js.map +1 -1
- package/dist/lib/utils.d.ts +13 -29
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +45 -155
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/video-renderer.d.ts.map +1 -1
- package/dist/lib/video-renderer.js +6 -5
- package/dist/lib/video-renderer.js.map +1 -1
- package/dist/types/config.d.ts +0 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/package.json +2 -12
- package/REGISTRY_SETUP.md +0 -363
- package/_dsgn/templates/dashed-stroke-test/template.tsx +0 -73
- package/_dsgn/templates/path-follow-test/template.tsx +0 -176
- package/_dsgn/templates/path-simple-test/template.tsx +0 -98
- package/_dsgn/templates/stroke-dash-test/meta.json +0 -12
- package/_dsgn/templates/stroke-dash-test/template.tsx +0 -53
- package/dist/default-templates/image/template.d.ts +0 -20
- package/dist/default-templates/image/template.d.ts.map +0 -1
- package/dist/default-templates/image/template.js +0 -18
- package/dist/default-templates/image/template.js.map +0 -1
- package/dist/default-templates/image/template.tsx +0 -20
- package/dist/default-templates/kitchen-sink/template.tsx +0 -64
- package/dist/default-templates/page/template.tsx +0 -37
- package/dist/default-templates/video/template.d.ts +0 -26
- package/dist/default-templates/video/template.d.ts.map +0 -1
- package/dist/default-templates/video/template.js +0 -33
- package/dist/default-templates/video/template.js.map +0 -1
- package/dist/default-templates/video/template.tsx +0 -37
- package/dist/default-templates/website/pages/home.tsx +0 -17
- package/dist/default-templates/website/parts/footer.tsx +0 -17
- package/dist/default-templates/website/parts/header.tsx +0 -17
- package/dist/default-templates/website/template.tsx +0 -17
- package/dist/default-templates/website-template/pages/home.tsx +0 -13
- package/dist/default-templates/website-template/parts/footer.tsx +0 -15
- package/dist/default-templates/website-template/parts/header.tsx +0 -15
- package/dist/default-templates/website-template/template.tsx +0 -32
- package/dist/lib/encode-worker.d.ts +0 -2
- package/dist/lib/encode-worker.d.ts.map +0 -1
- package/dist/lib/encode-worker.js +0 -29
- package/dist/lib/encode-worker.js.map +0 -1
- package/dist/lib/mjpeg-muxer.d.ts +0 -46
- package/dist/lib/mjpeg-muxer.d.ts.map +0 -1
- package/dist/lib/mjpeg-muxer.js +0 -513
- package/dist/lib/mjpeg-muxer.js.map +0 -1
- package/dist/lib/tailwind-browser.d.ts +0 -27
- package/dist/lib/tailwind-browser.d.ts.map +0 -1
- package/dist/lib/tailwind-browser.js +0 -853
- package/dist/lib/tailwind-browser.js.map +0 -1
- package/dist/lib/video-player.d.ts +0 -25
- package/dist/lib/video-player.d.ts.map +0 -1
- package/dist/lib/video-player.js +0 -392
- package/dist/lib/video-player.js.map +0 -1
- package/dist/sdk/compiler.d.ts +0 -94
- package/dist/sdk/compiler.d.ts.map +0 -1
- package/dist/sdk/compiler.js +0 -122
- package/dist/sdk/compiler.js.map +0 -1
- package/dist/sdk/index.d.ts +0 -62
- package/dist/sdk/index.d.ts.map +0 -1
- package/dist/sdk/index.js +0 -141
- package/dist/sdk/index.js.map +0 -1
- package/dist/sdk/preview.d.ts +0 -65
- package/dist/sdk/preview.d.ts.map +0 -1
- package/dist/sdk/preview.js +0 -262
- package/dist/sdk/preview.js.map +0 -1
- package/dist/sdk/template.d.ts +0 -162
- package/dist/sdk/template.d.ts.map +0 -1
- package/dist/sdk/template.js +0 -231
- package/dist/sdk/template.js.map +0 -1
- package/examples/code-editor-templates.ts +0 -173
- package/examples/nextjs-api/README.md +0 -180
- package/examples/nextjs-api/package.json +0 -21
- package/examples/nextjs-api/pages/api/intro-video.ts +0 -53
- package/examples/nextjs-api/pages/api/og-image.ts +0 -50
- package/examples/nextjs-template-import.ts +0 -58
- package/examples/sdk-video-preview.tsx +0 -120
- package/examples/template-compiler-workflow.ts +0 -251
- package/examples/visual-builder-templates.ts +0 -336
- package/render-examples-600x400.mjs +0 -161
- package/render-spring-variants-fixed.mjs +0 -60
- package/render-staggered-text.mjs +0 -56
- package/test-font-files.mjs +0 -72
- package/test-jsx-support.mjs +0 -146
- package/test-sdk-config.mjs +0 -454
- package/test-sdk-source-config.mjs +0 -427
- package/test-sdk-user-templates.mjs +0 -469
- package/test-static-debug.tsx +0 -19
- package/test-templates/TESTS.md +0 -63
- package/test-templates/config-test.mjs +0 -17
- package/test-templates/demo-intro-props.json +0 -4
- package/test-templates/run-tests.sh +0 -44
- package/test-templates/test-sdk.mjs +0 -139
- package/test-video-props.json +0 -3
- package/website/.astro/content.db +0 -0
- package/website/.astro/integrations/_inox-tools_astro-when/types.d.ts +0 -1
- package/website/.astro/integrations/astro_db/db.d.ts +0 -15
- package/website/.astro/settings.json +0 -5
- package/website/.astro/types.d.ts +0 -2
- package/website/DEPLOYMENT.md +0 -143
- package/website/OG_IMAGES.md +0 -142
- package/website/README.md +0 -158
- package/website/astro.config.mjs +0 -46
- package/website/dist/.gitkeep +0 -5
- package/website/dist/_astro/agents.Yx-L_igG.css +0 -1
- package/website/dist/_routes.json +0 -30
- package/website/dist/_worker.js/_@astrojs-ssr-adapter.mjs +0 -1033
- package/website/dist/_worker.js/_astro-internal_middleware.mjs +0 -40
- package/website/dist/_worker.js/chunks/abap_BTmsHiP5.mjs +0 -1
- package/website/dist/_worker.js/chunks/actionscript-3_DmBelb1E.mjs +0 -1
- package/website/dist/_worker.js/chunks/ada_8-E0ahCN.mjs +0 -1
- package/website/dist/_worker.js/chunks/andromeeda_XI-CXx50.mjs +0 -1
- package/website/dist/_worker.js/chunks/angular-html_DKGh3gGH.mjs +0 -1
- package/website/dist/_worker.js/chunks/angular-ts_-qZGsJoA.mjs +0 -1
- package/website/dist/_worker.js/chunks/apache_ijTUt0Ee.mjs +0 -1
- package/website/dist/_worker.js/chunks/apex_agu1c6Sh.mjs +0 -1
- package/website/dist/_worker.js/chunks/apl_Bj2f7Art.mjs +0 -1
- package/website/dist/_worker.js/chunks/applescript_B_vXrOh3.mjs +0 -1
- package/website/dist/_worker.js/chunks/ara_DCEQ2rnh.mjs +0 -1
- package/website/dist/_worker.js/chunks/asciidoc_CGN_EkYS.mjs +0 -1
- package/website/dist/_worker.js/chunks/asm_BBWZgnDp.mjs +0 -1
- package/website/dist/_worker.js/chunks/astro/assets-service_j52rQLzU.mjs +0 -721
- package/website/dist/_worker.js/chunks/astro/server_Y5_QHO8v.mjs +0 -2401
- package/website/dist/_worker.js/chunks/astro-designed-error-pages_BNTLO-TA.mjs +0 -542
- package/website/dist/_worker.js/chunks/astro_Dr_hht3h.mjs +0 -1
- package/website/dist/_worker.js/chunks/aurora-x_9GHG8nSq.mjs +0 -1
- package/website/dist/_worker.js/chunks/awk_DHRvhXot.mjs +0 -1
- package/website/dist/_worker.js/chunks/ayu-dark_CcvqmEHE.mjs +0 -1
- package/website/dist/_worker.js/chunks/ballerina_C7SdeSZb.mjs +0 -1
- package/website/dist/_worker.js/chunks/bat_Dv4A3u45.mjs +0 -1
- package/website/dist/_worker.js/chunks/beancount_BfPf9Luv.mjs +0 -1
- package/website/dist/_worker.js/chunks/berry_B8rfM3lL.mjs +0 -1
- package/website/dist/_worker.js/chunks/bibtex_TcjYgtJM.mjs +0 -1
- package/website/dist/_worker.js/chunks/bicep_CrlFWCdN.mjs +0 -1
- package/website/dist/_worker.js/chunks/blade_lanKVYID.mjs +0 -1
- package/website/dist/_worker.js/chunks/bsl_BhppzXMB.mjs +0 -1
- package/website/dist/_worker.js/chunks/c_6FBALJTK.mjs +0 -1
- package/website/dist/_worker.js/chunks/cadence_2txU9LVE.mjs +0 -1
- package/website/dist/_worker.js/chunks/cairo_BkrFAIlP.mjs +0 -1
- package/website/dist/_worker.js/chunks/catppuccin-frappe_CkEqIYhU.mjs +0 -1
- package/website/dist/_worker.js/chunks/catppuccin-latte_DG4Gx_-v.mjs +0 -1
- package/website/dist/_worker.js/chunks/catppuccin-macchiato_Cwi3vCXf.mjs +0 -1
- package/website/dist/_worker.js/chunks/catppuccin-mocha_L9_OPlFX.mjs +0 -1
- package/website/dist/_worker.js/chunks/clarity_BEAe4Ulu.mjs +0 -1
- package/website/dist/_worker.js/chunks/clojure_VnUX6p2g.mjs +0 -1
- package/website/dist/_worker.js/chunks/cmake_0-SGkZEj.mjs +0 -1
- package/website/dist/_worker.js/chunks/cobol_92M_KGaE.mjs +0 -1
- package/website/dist/_worker.js/chunks/codeowners_CzMwskBv.mjs +0 -1
- package/website/dist/_worker.js/chunks/codeql_DWJZNHv1.mjs +0 -1
- package/website/dist/_worker.js/chunks/coffee_CQjKU2fh.mjs +0 -1
- package/website/dist/_worker.js/chunks/common-lisp_BBLWDpS5.mjs +0 -1
- package/website/dist/_worker.js/chunks/coq_hedRFV3D.mjs +0 -1
- package/website/dist/_worker.js/chunks/cpp_DlS1i6Zs.mjs +0 -1
- package/website/dist/_worker.js/chunks/crystal_D6n65fKV.mjs +0 -1
- package/website/dist/_worker.js/chunks/csharp_C6FCVFzc.mjs +0 -1
- package/website/dist/_worker.js/chunks/css_C5uJEgmJ.mjs +0 -1
- package/website/dist/_worker.js/chunks/csv_CtMYuuJl.mjs +0 -1
- package/website/dist/_worker.js/chunks/cue_BsPexqx6.mjs +0 -1
- package/website/dist/_worker.js/chunks/cypher_apzf6OBi.mjs +0 -1
- package/website/dist/_worker.js/chunks/d_DcvIRcgm.mjs +0 -1
- package/website/dist/_worker.js/chunks/dark-plus_C01ONtzj.mjs +0 -1
- package/website/dist/_worker.js/chunks/dart_WkzM5WrV.mjs +0 -1
- package/website/dist/_worker.js/chunks/dax_DjXAO5V4.mjs +0 -1
- package/website/dist/_worker.js/chunks/desktop_C92LCxdc.mjs +0 -1
- package/website/dist/_worker.js/chunks/diff_CVwM_9XJ.mjs +0 -1
- package/website/dist/_worker.js/chunks/docker_DPzgJf6Z.mjs +0 -1
- package/website/dist/_worker.js/chunks/dotenv_D_vgANvA.mjs +0 -1
- package/website/dist/_worker.js/chunks/dracula-soft_CLnUBwFm.mjs +0 -1
- package/website/dist/_worker.js/chunks/dracula_lBVpb6Lb.mjs +0 -1
- package/website/dist/_worker.js/chunks/dream-maker_DTLbzd_J.mjs +0 -1
- package/website/dist/_worker.js/chunks/edge_i54JYm3_.mjs +0 -1
- package/website/dist/_worker.js/chunks/elixir_BJCIjTu4.mjs +0 -1
- package/website/dist/_worker.js/chunks/elm_BbXD39-_.mjs +0 -1
- package/website/dist/_worker.js/chunks/emacs-lisp_pxa5cXaN.mjs +0 -1
- package/website/dist/_worker.js/chunks/erb_Ccjijeee.mjs +0 -1
- package/website/dist/_worker.js/chunks/erlang_B2VM_hi7.mjs +0 -1
- package/website/dist/_worker.js/chunks/everforest-dark_BxvIPBim.mjs +0 -1
- package/website/dist/_worker.js/chunks/everforest-light_B7VoyaJM.mjs +0 -1
- package/website/dist/_worker.js/chunks/fennel_D-uo7X6c.mjs +0 -1
- package/website/dist/_worker.js/chunks/fish_BjePoK3m.mjs +0 -1
- package/website/dist/_worker.js/chunks/fluent_C8fgkzLX.mjs +0 -1
- package/website/dist/_worker.js/chunks/fortran-fixed-form_D1pu5zrc.mjs +0 -1
- package/website/dist/_worker.js/chunks/fortran-free-form_CSGOhJD6.mjs +0 -1
- package/website/dist/_worker.js/chunks/fsharp_B0xy-A4Y.mjs +0 -1
- package/website/dist/_worker.js/chunks/gdresource_CWppjlHq.mjs +0 -1
- package/website/dist/_worker.js/chunks/gdscript_eQCHchcS.mjs +0 -1
- package/website/dist/_worker.js/chunks/gdshader_C4kxepX7.mjs +0 -1
- package/website/dist/_worker.js/chunks/genie_ACtQLcDW.mjs +0 -1
- package/website/dist/_worker.js/chunks/gherkin_BFp2uKUd.mjs +0 -1
- package/website/dist/_worker.js/chunks/git-commit_CLg9ZwMV.mjs +0 -1
- package/website/dist/_worker.js/chunks/git-rebase_DG8A80Nt.mjs +0 -1
- package/website/dist/_worker.js/chunks/github-dark-default_BI0EP2Kv.mjs +0 -1
- package/website/dist/_worker.js/chunks/github-dark-dimmed_a_NIC0Xb.mjs +0 -1
- package/website/dist/_worker.js/chunks/github-dark-high-contrast_jZGqT7hk.mjs +0 -1
- package/website/dist/_worker.js/chunks/github-dark_CHCDNd2O.mjs +0 -1
- package/website/dist/_worker.js/chunks/github-light-default_DRbOW5RG.mjs +0 -1
- package/website/dist/_worker.js/chunks/github-light-high-contrast_tn_kWutM.mjs +0 -1
- package/website/dist/_worker.js/chunks/github-light_D9brYzot.mjs +0 -1
- package/website/dist/_worker.js/chunks/gleam_Dmhu1oxW.mjs +0 -1
- package/website/dist/_worker.js/chunks/glimmer-js_BfZbXy8A.mjs +0 -1
- package/website/dist/_worker.js/chunks/glimmer-ts_B9QVICrD.mjs +0 -1
- package/website/dist/_worker.js/chunks/glsl_DD2PPwOs.mjs +0 -1
- package/website/dist/_worker.js/chunks/gnuplot_D2OYChUX.mjs +0 -1
- package/website/dist/_worker.js/chunks/go_DYGFTe3h.mjs +0 -1
- package/website/dist/_worker.js/chunks/graphql_B7XsT3nH.mjs +0 -1
- package/website/dist/_worker.js/chunks/groovy_BO12Uwkl.mjs +0 -1
- package/website/dist/_worker.js/chunks/hack_CB2_ztCP.mjs +0 -1
- package/website/dist/_worker.js/chunks/haml_CyfDcDD3.mjs +0 -1
- package/website/dist/_worker.js/chunks/handlebars_CfpxpWm2.mjs +0 -1
- package/website/dist/_worker.js/chunks/haskell_jUeC5uN5.mjs +0 -1
- package/website/dist/_worker.js/chunks/haxe_B6GxP1WB.mjs +0 -1
- package/website/dist/_worker.js/chunks/hcl_DwoHV2oh.mjs +0 -1
- package/website/dist/_worker.js/chunks/hjson_DV7cJRk4.mjs +0 -1
- package/website/dist/_worker.js/chunks/hlsl_BlFCscPI.mjs +0 -1
- package/website/dist/_worker.js/chunks/houston_COBFG1Mx.mjs +0 -1
- package/website/dist/_worker.js/chunks/html-derivative_C9pJ337h.mjs +0 -1
- package/website/dist/_worker.js/chunks/html_D1OkrZS5.mjs +0 -1
- package/website/dist/_worker.js/chunks/http_DIGXRqvJ.mjs +0 -1
- package/website/dist/_worker.js/chunks/hxml_DEwh9i-c.mjs +0 -1
- package/website/dist/_worker.js/chunks/hy_DDoIgW1K.mjs +0 -1
- package/website/dist/_worker.js/chunks/imba_B00zbHo4.mjs +0 -1
- package/website/dist/_worker.js/chunks/index_C1UTDwYg.mjs +0 -1861
- package/website/dist/_worker.js/chunks/ini_D7XQA_p8.mjs +0 -1
- package/website/dist/_worker.js/chunks/java_B9wdFd8K.mjs +0 -1
- package/website/dist/_worker.js/chunks/javascript_CLsPGOON.mjs +0 -1
- package/website/dist/_worker.js/chunks/jinja_jarBCAN1.mjs +0 -1
- package/website/dist/_worker.js/chunks/jison_oGg3J708.mjs +0 -1
- package/website/dist/_worker.js/chunks/json5_DlZ1Kyaa.mjs +0 -1
- package/website/dist/_worker.js/chunks/json_DaYk_FMp.mjs +0 -1
- package/website/dist/_worker.js/chunks/jsonc_DlwgfSDs.mjs +0 -1
- package/website/dist/_worker.js/chunks/jsonl_BbCCVaZF.mjs +0 -1
- package/website/dist/_worker.js/chunks/jsonnet_Dt-G75xe.mjs +0 -1
- package/website/dist/_worker.js/chunks/jssm_BtKFTj2A.mjs +0 -1
- package/website/dist/_worker.js/chunks/jsx_DDx_xAZ8.mjs +0 -1
- package/website/dist/_worker.js/chunks/julia_CK0lv68l.mjs +0 -1
- package/website/dist/_worker.js/chunks/kanagawa-dragon_BldAK3Oo.mjs +0 -1
- package/website/dist/_worker.js/chunks/kanagawa-lotus_DVM8FX9_.mjs +0 -1
- package/website/dist/_worker.js/chunks/kanagawa-wave_Dpih0AKP.mjs +0 -1
- package/website/dist/_worker.js/chunks/kotlin_kWneB9V_.mjs +0 -1
- package/website/dist/_worker.js/chunks/kusto_BKVATd95.mjs +0 -1
- package/website/dist/_worker.js/chunks/laserwave_BqatxsVl.mjs +0 -1
- package/website/dist/_worker.js/chunks/latex_LVDcGBbc.mjs +0 -1
- package/website/dist/_worker.js/chunks/lean_W7qo-5M2.mjs +0 -1
- package/website/dist/_worker.js/chunks/less_DFNwJnBH.mjs +0 -1
- package/website/dist/_worker.js/chunks/light-plus_Dp0AoWsO.mjs +0 -1
- package/website/dist/_worker.js/chunks/liquid_D24qs0pc.mjs +0 -1
- package/website/dist/_worker.js/chunks/log_IPWMXriF.mjs +0 -1
- package/website/dist/_worker.js/chunks/logo_C6KaatrQ.mjs +0 -1
- package/website/dist/_worker.js/chunks/lua_CwnEf-T7.mjs +0 -1
- package/website/dist/_worker.js/chunks/luau_Br3-CXjS.mjs +0 -1
- package/website/dist/_worker.js/chunks/make_UBNG-kOo.mjs +0 -1
- package/website/dist/_worker.js/chunks/markdown_C7mhJFCm.mjs +0 -1
- package/website/dist/_worker.js/chunks/marko_4tchUvI7.mjs +0 -1
- package/website/dist/_worker.js/chunks/material-theme-darker_SKtaNEPn.mjs +0 -1
- package/website/dist/_worker.js/chunks/material-theme-lighter_zOX_DZCH.mjs +0 -1
- package/website/dist/_worker.js/chunks/material-theme-ocean_BN9WbhdC.mjs +0 -1
- package/website/dist/_worker.js/chunks/material-theme-palenight_DT_covjH.mjs +0 -1
- package/website/dist/_worker.js/chunks/material-theme_6RpeM3kc.mjs +0 -1
- package/website/dist/_worker.js/chunks/matlab_DCOXsPKR.mjs +0 -1
- package/website/dist/_worker.js/chunks/mdc_B9gb2UFP.mjs +0 -1
- package/website/dist/_worker.js/chunks/mdx_DGU7Nu9u.mjs +0 -1
- package/website/dist/_worker.js/chunks/mermaid_B69URzsZ.mjs +0 -1
- package/website/dist/_worker.js/chunks/min-dark_BgxifOMI.mjs +0 -1
- package/website/dist/_worker.js/chunks/min-light_BrPjXxUp.mjs +0 -1
- package/website/dist/_worker.js/chunks/mipsasm_9U-4_t7k.mjs +0 -1
- package/website/dist/_worker.js/chunks/mojo_B0wt7ug3.mjs +0 -1
- package/website/dist/_worker.js/chunks/monokai_B6Pxpoyi.mjs +0 -1
- package/website/dist/_worker.js/chunks/move_1eid4CyR.mjs +0 -1
- package/website/dist/_worker.js/chunks/narrat_Ds6-p5JZ.mjs +0 -1
- package/website/dist/_worker.js/chunks/nextflow_v2N1Qlqa.mjs +0 -1
- package/website/dist/_worker.js/chunks/nginx_Bp9Ab2NH.mjs +0 -1
- package/website/dist/_worker.js/chunks/night-owl_CdwOw_sc.mjs +0 -1
- package/website/dist/_worker.js/chunks/nim_BXGDUe53.mjs +0 -1
- package/website/dist/_worker.js/chunks/nix_CUig1nJH.mjs +0 -1
- package/website/dist/_worker.js/chunks/noop-middleware_DlWGj5t5.mjs +0 -10
- package/website/dist/_worker.js/chunks/nord_SPoG1iae.mjs +0 -1
- package/website/dist/_worker.js/chunks/nushell_DJw1Lca8.mjs +0 -1
- package/website/dist/_worker.js/chunks/objective-c_Bktzl_CO.mjs +0 -1
- package/website/dist/_worker.js/chunks/objective-cpp_CP4DWdDp.mjs +0 -1
- package/website/dist/_worker.js/chunks/ocaml_CeEAs7bZ.mjs +0 -1
- package/website/dist/_worker.js/chunks/one-dark-pro_-hIwCNMi.mjs +0 -1
- package/website/dist/_worker.js/chunks/one-light_DSmYvJ05.mjs +0 -1
- package/website/dist/_worker.js/chunks/pascal_C-S_Ms_o.mjs +0 -1
- package/website/dist/_worker.js/chunks/perl_CKamvo15.mjs +0 -1
- package/website/dist/_worker.js/chunks/php_BlmcX_F3.mjs +0 -1
- package/website/dist/_worker.js/chunks/plastic_Ryt8tVoA.mjs +0 -1
- package/website/dist/_worker.js/chunks/plsql_Cb3v7cBj.mjs +0 -1
- package/website/dist/_worker.js/chunks/po_DZbdNRlo.mjs +0 -1
- package/website/dist/_worker.js/chunks/poimandres_bYmE3_5d.mjs +0 -1
- package/website/dist/_worker.js/chunks/polar_pJkMGwoW.mjs +0 -1
- package/website/dist/_worker.js/chunks/postcss_BAXSOKgk.mjs +0 -1
- package/website/dist/_worker.js/chunks/powerquery_oITMGN4x.mjs +0 -1
- package/website/dist/_worker.js/chunks/powershell_6306-xIF.mjs +0 -1
- package/website/dist/_worker.js/chunks/prisma_DSDxnZGz.mjs +0 -1
- package/website/dist/_worker.js/chunks/prolog_CxG7tjZR.mjs +0 -1
- package/website/dist/_worker.js/chunks/proto_CS9ByXm1.mjs +0 -1
- package/website/dist/_worker.js/chunks/pug_BMtLJo6U.mjs +0 -1
- package/website/dist/_worker.js/chunks/puppet_BfeeSzee.mjs +0 -1
- package/website/dist/_worker.js/chunks/purescript_BFfueNaH.mjs +0 -1
- package/website/dist/_worker.js/chunks/python_Cc4Faapv.mjs +0 -1
- package/website/dist/_worker.js/chunks/qml_C1CTJTK8.mjs +0 -1
- package/website/dist/_worker.js/chunks/qmldir_nG1KaqKR.mjs +0 -1
- package/website/dist/_worker.js/chunks/qss_Cncxk263.mjs +0 -1
- package/website/dist/_worker.js/chunks/r_ChR54Ihi.mjs +0 -1
- package/website/dist/_worker.js/chunks/racket_BDrhptDs.mjs +0 -1
- package/website/dist/_worker.js/chunks/raku_07OUHa0P.mjs +0 -1
- package/website/dist/_worker.js/chunks/razor_DIP3INLa.mjs +0 -1
- package/website/dist/_worker.js/chunks/red_DOPXfj-6.mjs +0 -1
- package/website/dist/_worker.js/chunks/reg_B64SwEDj.mjs +0 -1
- package/website/dist/_worker.js/chunks/regexp_ButFGoB5.mjs +0 -1
- package/website/dist/_worker.js/chunks/rel_BWJAWqZD.mjs +0 -1
- package/website/dist/_worker.js/chunks/riscv_79gXlbsF.mjs +0 -1
- package/website/dist/_worker.js/chunks/rose-pine-dawn_DHIjVGd3.mjs +0 -1
- package/website/dist/_worker.js/chunks/rose-pine-moon_t86aEbs0.mjs +0 -1
- package/website/dist/_worker.js/chunks/rose-pine_BHgrcDCs.mjs +0 -1
- package/website/dist/_worker.js/chunks/rst_D3F4Fcpj.mjs +0 -1
- package/website/dist/_worker.js/chunks/ruby_Cs7vM9iv.mjs +0 -1
- package/website/dist/_worker.js/chunks/rust_DpyRVatH.mjs +0 -1
- package/website/dist/_worker.js/chunks/sas_DW45xZXN.mjs +0 -1
- package/website/dist/_worker.js/chunks/sass_C6SiMwN_.mjs +0 -1
- package/website/dist/_worker.js/chunks/scala_DlZOjNZk.mjs +0 -1
- package/website/dist/_worker.js/chunks/scheme_D2ezSJXu.mjs +0 -1
- package/website/dist/_worker.js/chunks/scss_DG5Spjqu.mjs +0 -1
- package/website/dist/_worker.js/chunks/sdbl_ZCYaj4VN.mjs +0 -1
- package/website/dist/_worker.js/chunks/shaderlab_CAcRkg1_.mjs +0 -1
- package/website/dist/_worker.js/chunks/shellscript_BWwhkDVh.mjs +0 -1
- package/website/dist/_worker.js/chunks/shellsession_BfEA3juK.mjs +0 -1
- package/website/dist/_worker.js/chunks/slack-dark_CL3lSpCc.mjs +0 -1
- package/website/dist/_worker.js/chunks/slack-ochin_DdZKOQVh.mjs +0 -1
- package/website/dist/_worker.js/chunks/smalltalk_DgilzSui.mjs +0 -1
- package/website/dist/_worker.js/chunks/snazzy-light_eJU08Pz_.mjs +0 -1
- package/website/dist/_worker.js/chunks/solarized-dark_Dg_YQywx.mjs +0 -1
- package/website/dist/_worker.js/chunks/solarized-light_BnIsrA6p.mjs +0 -1
- package/website/dist/_worker.js/chunks/solidity_DkseH8pQ.mjs +0 -1
- package/website/dist/_worker.js/chunks/soy_DU7bOYoG.mjs +0 -1
- package/website/dist/_worker.js/chunks/sparql_BuI1DBDH.mjs +0 -1
- package/website/dist/_worker.js/chunks/splunk_B8Ha9Pkg.mjs +0 -1
- package/website/dist/_worker.js/chunks/sql_BniHwea5.mjs +0 -1
- package/website/dist/_worker.js/chunks/ssh-config_CkE1GuVe.mjs +0 -1
- package/website/dist/_worker.js/chunks/stata_Dtqpbd_l.mjs +0 -1
- package/website/dist/_worker.js/chunks/stylus_CXTtglzO.mjs +0 -1
- package/website/dist/_worker.js/chunks/svelte_BjWYcUCN.mjs +0 -1
- package/website/dist/_worker.js/chunks/swift_BzHql_rM.mjs +0 -1
- package/website/dist/_worker.js/chunks/synthwave-84_DLRNhxNA.mjs +0 -1
- package/website/dist/_worker.js/chunks/system-verilog_ChyInPph.mjs +0 -1
- package/website/dist/_worker.js/chunks/systemd_Bi9Qa2qD.mjs +0 -1
- package/website/dist/_worker.js/chunks/talonscript_B3sH_Y-V.mjs +0 -1
- package/website/dist/_worker.js/chunks/tasl_BJ5yipRs.mjs +0 -1
- package/website/dist/_worker.js/chunks/tcl_CoJQjNoP.mjs +0 -1
- package/website/dist/_worker.js/chunks/templ_CrU7Ffil.mjs +0 -1
- package/website/dist/_worker.js/chunks/terraform_DT9JSFpC.mjs +0 -1
- package/website/dist/_worker.js/chunks/tex_5PKu2yA0.mjs +0 -1
- package/website/dist/_worker.js/chunks/tokyo-night_Buo8OK7-.mjs +0 -1
- package/website/dist/_worker.js/chunks/toml_CPuXX3oc.mjs +0 -1
- package/website/dist/_worker.js/chunks/ts-tags_D0M_1VSH.mjs +0 -1
- package/website/dist/_worker.js/chunks/tsv_CuivVNot.mjs +0 -1
- package/website/dist/_worker.js/chunks/tsx_MkuGr8MY.mjs +0 -1
- package/website/dist/_worker.js/chunks/turtle_BqgEPK7f.mjs +0 -1
- package/website/dist/_worker.js/chunks/twig_r1G9rpYJ.mjs +0 -1
- package/website/dist/_worker.js/chunks/typescript_Au5buqzM.mjs +0 -1
- package/website/dist/_worker.js/chunks/typespec_47rhBK_z.mjs +0 -1
- package/website/dist/_worker.js/chunks/typst_BAtuQLh-.mjs +0 -1
- package/website/dist/_worker.js/chunks/v_BIvWImHg.mjs +0 -1
- package/website/dist/_worker.js/chunks/vala_DYEacj30.mjs +0 -1
- package/website/dist/_worker.js/chunks/vb_CikQuqGJ.mjs +0 -1
- package/website/dist/_worker.js/chunks/verilog_BQRENwI-.mjs +0 -1
- package/website/dist/_worker.js/chunks/vesper_DA0kvTmj.mjs +0 -1
- package/website/dist/_worker.js/chunks/vhdl_DHscJIyg.mjs +0 -1
- package/website/dist/_worker.js/chunks/viml_F2pvMwvG.mjs +0 -1
- package/website/dist/_worker.js/chunks/vitesse-black_D9tjNzd0.mjs +0 -1
- package/website/dist/_worker.js/chunks/vitesse-dark_Bnm5d0hd.mjs +0 -1
- package/website/dist/_worker.js/chunks/vitesse-light_CHwbyjNR.mjs +0 -1
- package/website/dist/_worker.js/chunks/vue-html_DyYtbbMK.mjs +0 -1
- package/website/dist/_worker.js/chunks/vue_DofN6juy.mjs +0 -1
- package/website/dist/_worker.js/chunks/vyper_CiR0m-OV.mjs +0 -1
- package/website/dist/_worker.js/chunks/wasm_CwIGgRGf.mjs +0 -1
- package/website/dist/_worker.js/chunks/wasm_jKWhg0J0.mjs +0 -1
- package/website/dist/_worker.js/chunks/wenyan_DKvVZKXW.mjs +0 -1
- package/website/dist/_worker.js/chunks/wgsl_BOWZY7yw.mjs +0 -1
- package/website/dist/_worker.js/chunks/wikitext_CXDhhHPy.mjs +0 -1
- package/website/dist/_worker.js/chunks/wolfram_ChkmGnW0.mjs +0 -1
- package/website/dist/_worker.js/chunks/xml_DXH3hHIu.mjs +0 -1
- package/website/dist/_worker.js/chunks/xsl_DuP2mFjg.mjs +0 -1
- package/website/dist/_worker.js/chunks/yaml_IGiEkTge.mjs +0 -1
- package/website/dist/_worker.js/chunks/zenscript_59iXGyNw.mjs +0 -1
- package/website/dist/_worker.js/chunks/zig_DKzb0zdT.mjs +0 -1
- package/website/dist/_worker.js/index.js +0 -53
- package/website/dist/_worker.js/manifest_CT_D-YDe.mjs +0 -98
- package/website/dist/_worker.js/pages/_image.astro.mjs +0 -24
- package/website/dist/_worker.js/pages/agents.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/animation.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/api/raw-markdown/_---path_.astro.mjs +0 -44
- package/website/dist/_worker.js/pages/config.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/fonts.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/getting-started.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/helpers.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/images.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/index.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/llm.txt.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/preview.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/sdk.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/sitemap.xml.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/styling.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/templates.astro.mjs +0 -1
- package/website/dist/_worker.js/pages/video.astro.mjs +0 -1
- package/website/dist/_worker.js/renderers.mjs +0 -57
- package/website/dist/agents/index.html +0 -52
- package/website/dist/animation/index.html +0 -879
- package/website/dist/config/index.html +0 -184
- package/website/dist/fonts/index.html +0 -198
- package/website/dist/getting-started/index.html +0 -107
- package/website/dist/helpers/index.html +0 -164
- package/website/dist/images/index.html +0 -335
- package/website/dist/index.html +0 -140
- package/website/dist/llm.txt +0 -2776
- package/website/dist/preview/index.html +0 -111
- package/website/dist/robots.txt +0 -40
- package/website/dist/sdk/index.html +0 -1076
- package/website/dist/sitemap.xml +0 -76
- package/website/dist/styling/index.html +0 -366
- package/website/dist/templates/index.html +0 -74
- package/website/dist/video/index.html +0 -319
- package/website/package-lock.json +0 -8089
- package/website/package.json +0 -41
- package/website/public/.gitkeep +0 -5
- package/website/public/robots.txt +0 -40
- package/website/templates/og-image.tsx +0 -60
package/website/dist/llm.txt
DELETED
|
@@ -1,2776 +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-11-20T11:24:11.500Z
|
|
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
|
-
# With custom encoding
|
|
754
|
-
loopwind render video-intro '{"title":"Welcome!"}' --preset ultrafast
|
|
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
|
-
### Default (Balanced)
|
|
922
|
-
|
|
923
|
-
```bash
|
|
924
|
-
loopwind render video-intro '{"title":"Welcome"}'
|
|
925
|
-
```
|
|
926
|
-
|
|
927
|
-
Uses H.264 codec with good quality/size balance.
|
|
928
|
-
|
|
929
|
-
### Fast Encoding
|
|
930
|
-
|
|
931
|
-
```bash
|
|
932
|
-
loopwind render video-intro '{"title":"Welcome"}' --preset ultrafast
|
|
933
|
-
```
|
|
934
|
-
|
|
935
|
-
Faster encoding, slightly larger files. Good for previews.
|
|
936
|
-
|
|
937
|
-
### High Quality
|
|
938
|
-
|
|
939
|
-
```bash
|
|
940
|
-
loopwind render video-intro '{"title":"Welcome"}' --preset slow
|
|
941
|
-
```
|
|
942
|
-
|
|
943
|
-
Better compression, slower encoding. Good for final output.
|
|
944
|
-
|
|
945
|
-
## Performance
|
|
946
|
-
|
|
947
|
-
### Rendering Speed
|
|
948
|
-
|
|
949
|
-
On an M1 Mac:
|
|
950
|
-
- **30fps, 3s video** (90 frames): ~15-20 seconds
|
|
951
|
-
- **60fps, 3s video** (180 frames): ~30-40 seconds
|
|
952
|
-
|
|
953
|
-
Speed depends on:
|
|
954
|
-
- Template complexity
|
|
955
|
-
- Image/video embedding
|
|
956
|
-
- CPU performance
|
|
957
|
-
- Frame count
|
|
958
|
-
|
|
959
|
-
### Optimization Tips
|
|
960
|
-
|
|
961
|
-
1. **Lower FPS for previews**: Test at 15fps, render final at 30fps
|
|
962
|
-
2. **Shorter duration**: Preview with 1s, expand for final
|
|
963
|
-
3. **Simplify templates**: Complex layouts take longer
|
|
964
|
-
4. **Pre-optimize media**: Resize images/videos before embedding
|
|
965
|
-
|
|
966
|
-
### Memory Usage
|
|
967
|
-
|
|
968
|
-
Video frames are cached in memory during rendering:
|
|
969
|
-
- **1920x1080 @ 30fps for 3s**: ~500MB RAM
|
|
970
|
-
- **1920x1080 @ 60fps for 5s**: ~1.7GB RAM
|
|
971
|
-
|
|
972
|
-
For long videos, consider rendering in segments.
|
|
973
|
-
|
|
974
|
-
## Common Video Templates
|
|
975
|
-
|
|
976
|
-
### Loading Spinner
|
|
977
|
-
|
|
978
|
-
```tsx
|
|
979
|
-
export default function LoadingVideo({ tw, title }) {
|
|
980
|
-
return (
|
|
981
|
-
<div style={tw('flex flex-col items-center justify-center w-full h-full bg-gray-900')}>
|
|
982
|
-
{/* Spinner fades out at 60% (1800ms for 3s video) */}
|
|
983
|
-
<div style={tw('exit-fade-out/1800/400')}>
|
|
984
|
-
<div style={tw('w-32 h-32 border-8 border-blue-500 border-t-transparent rounded-full loop-spin/1000')} />
|
|
985
|
-
</div>
|
|
986
|
-
|
|
987
|
-
{/* Title fades in after spinner */}
|
|
988
|
-
<h1 style={tw('text-6xl font-bold text-white mt-12 ease-out enter-fade-in-up/1800/400')}>
|
|
989
|
-
{title}
|
|
990
|
-
</h1>
|
|
991
|
-
</div>
|
|
992
|
-
);
|
|
993
|
-
}
|
|
994
|
-
```
|
|
995
|
-
|
|
996
|
-
### Progress Bar
|
|
997
|
-
|
|
998
|
-
```tsx
|
|
999
|
-
export default function ProgressVideo({ tw, title, subtitle }) {
|
|
1000
|
-
return (
|
|
1001
|
-
<div style={tw('flex flex-col items-center justify-center w-full h-full bg-gray-900 p-12')}>
|
|
1002
|
-
{/* Title bounces in */}
|
|
1003
|
-
<h2 style={tw('text-5xl font-bold text-white mb-12 ease-out enter-bounce-in/0/500')}>
|
|
1004
|
-
{title}
|
|
1005
|
-
</h2>
|
|
1006
|
-
|
|
1007
|
-
{/* Progress bar container fades in */}
|
|
1008
|
-
<div style={tw('w-full max-w-2xl h-4 bg-gray-700 rounded-full overflow-hidden ease-out enter-fade-in/300/400')}>
|
|
1009
|
-
<div style={tw('h-full bg-gradient-to-r from-blue-500 to-green-500 w-full')} />
|
|
1010
|
-
</div>
|
|
1011
|
-
|
|
1012
|
-
{/* Subtitle fades in last */}
|
|
1013
|
-
<p style={tw('text-2xl text-gray-400 mt-8 ease-out enter-fade-in/600/400')}>
|
|
1014
|
-
{subtitle}
|
|
1015
|
-
</p>
|
|
1016
|
-
</div>
|
|
1017
|
-
);
|
|
1018
|
-
}
|
|
1019
|
-
```
|
|
1020
|
-
|
|
1021
|
-
### Countdown Timer
|
|
1022
|
-
|
|
1023
|
-
```tsx
|
|
1024
|
-
export default function CountdownVideo({ tw, frame, message }) {
|
|
1025
|
-
const fps = 30;
|
|
1026
|
-
const secondsRemaining = Math.ceil((90 - frame) / fps); // 3s countdown
|
|
1027
|
-
|
|
1028
|
-
return (
|
|
1029
|
-
<div style={tw('flex flex-col items-center justify-center w-full h-full bg-black')}>
|
|
1030
|
-
{/* Number scales in and pulses continuously */}
|
|
1031
|
-
<div style={tw('text-[200px] font-black text-white ease-out enter-scale-in/0/400 loop-pulse/1000')}>
|
|
1032
|
-
{secondsRemaining}
|
|
1033
|
-
</div>
|
|
1034
|
-
|
|
1035
|
-
{/* Message fades in */}
|
|
1036
|
-
<p style={tw('text-3xl text-gray-400 mt-8 ease-out enter-fade-in/400/500')}>
|
|
1037
|
-
{message}
|
|
1038
|
-
</p>
|
|
1039
|
-
</div>
|
|
1040
|
-
);
|
|
1041
|
-
}
|
|
1042
|
-
```
|
|
1043
|
-
|
|
1044
|
-
## Output Formats
|
|
1045
|
-
|
|
1046
|
-
Videos can be rendered in two formats:
|
|
1047
|
-
|
|
1048
|
-
### MP4 (Default)
|
|
1049
|
-
|
|
1050
|
-
```bash
|
|
1051
|
-
# MP4 - H.264 codec, smaller file size, best quality
|
|
1052
|
-
loopwind render video-intro '{"title":"Welcome"}' --out intro.mp4
|
|
1053
|
-
|
|
1054
|
-
# With custom quality (lower CRF = better quality)
|
|
1055
|
-
loopwind render video-intro '{"title":"Welcome"}' --crf 18
|
|
1056
|
-
```
|
|
1057
|
-
|
|
1058
|
-
**Benefits:**
|
|
1059
|
-
- Smaller file sizes
|
|
1060
|
-
- Better color reproduction
|
|
1061
|
-
- Universal playback support
|
|
1062
|
-
- Best for social media, websites
|
|
1063
|
-
|
|
1064
|
-
### GIF
|
|
1065
|
-
|
|
1066
|
-
```bash
|
|
1067
|
-
# GIF - animated, works everywhere
|
|
1068
|
-
loopwind render video-intro '{"title":"Welcome"}' --format gif --out intro.gif
|
|
1069
|
-
|
|
1070
|
-
# Or just use .gif extension
|
|
1071
|
-
loopwind render video-intro '{"title":"Welcome"}' --out intro.gif
|
|
1072
|
-
```
|
|
1073
|
-
|
|
1074
|
-
**Benefits:**
|
|
1075
|
-
- Works in emails, GitHub READMEs, Slack
|
|
1076
|
-
- No video player needed
|
|
1077
|
-
- Auto-loops by default
|
|
1078
|
-
- Great for short animations
|
|
1079
|
-
|
|
1080
|
-
**Limitations:**
|
|
1081
|
-
- Limited to 256 colors per frame
|
|
1082
|
-
- Larger file sizes than MP4
|
|
1083
|
-
- Some color banding with gradients
|
|
1084
|
-
|
|
1085
|
-
**When to use GIF:**
|
|
1086
|
-
- Short loops (< 5 seconds)
|
|
1087
|
-
- Simple animations with solid colors
|
|
1088
|
-
- Platforms that don't support video (email, GitHub)
|
|
1089
|
-
|
|
1090
|
-
**When to use MP4:**
|
|
1091
|
-
- Longer videos
|
|
1092
|
-
- Complex gradients or photos
|
|
1093
|
-
- Best quality and smallest size
|
|
1094
|
-
|
|
1095
|
-
## Video-Specific Props
|
|
1096
|
-
|
|
1097
|
-
Every video template receives these additional props:
|
|
1098
|
-
|
|
1099
|
-
### `frame`
|
|
1100
|
-
Current frame number (0 to totalFrames - 1)
|
|
1101
|
-
|
|
1102
|
-
```tsx
|
|
1103
|
-
export default function MyVideo({ frame }) {
|
|
1104
|
-
// Frame 0, 1, 2, 3, ... 89 (for 3s @ 30fps)
|
|
1105
|
-
return <div>Frame: {frame}</div>;
|
|
1106
|
-
}
|
|
1107
|
-
```
|
|
1108
|
-
|
|
1109
|
-
### `progress`
|
|
1110
|
-
Animation progress from 0 to 1
|
|
1111
|
-
|
|
1112
|
-
```tsx
|
|
1113
|
-
export default function MyVideo({ progress }) {
|
|
1114
|
-
// 0.0 at start, 0.5 at middle, 1.0 at end
|
|
1115
|
-
const x = progress * 100; // Move from 0 to 100
|
|
1116
|
-
|
|
1117
|
-
return (
|
|
1118
|
-
<div style={{ transform: `translateX(${x}px)` }}>
|
|
1119
|
-
Moving element
|
|
1120
|
-
</div>
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
```
|
|
1124
|
-
|
|
1125
|
-
## Next Steps
|
|
1126
|
-
|
|
1127
|
-
- [Animation - Complete animation guide](/animation)
|
|
1128
|
-
- [Image Rendering](/images)
|
|
1129
|
-
- [Helpers - QR Codes and Template Composition](/helpers)
|
|
1130
|
-
- [Styling with Tailwind & shadcn/ui](/styling)
|
|
1131
|
-
- [Custom Fonts in Videos](/fonts)
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
================================================================================
|
|
1138
|
-
FILE: animation.mdx
|
|
1139
|
-
================================================================================
|
|
1140
|
-
|
|
1141
|
-
# Animation
|
|
1142
|
-
|
|
1143
|
-
loopwind provides **Tailwind-style animation classes** that work with time to create smooth video animations without writing custom code.
|
|
1144
|
-
|
|
1145
|
-
> **Note:** Animation classes only work with **video templates** and **GIFs**. For static images, animations will have no effect since there's no time context.
|
|
1146
|
-
|
|
1147
|
-
## Quick Start
|
|
1148
|
-
|
|
1149
|
-
```tsx
|
|
1150
|
-
export default function MyVideo({ tw, title, subtitle }) {
|
|
1151
|
-
return (
|
|
1152
|
-
<div style={tw('flex flex-col items-center justify-center w-full h-full bg-black')}>
|
|
1153
|
-
{/* Bounce in from below: starts at 0, lasts 400ms */}
|
|
1154
|
-
<h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/400')}>
|
|
1155
|
-
{title}
|
|
1156
|
-
</h1>
|
|
1157
|
-
|
|
1158
|
-
{/* Fade in with upward motion: starts at 300ms, lasts 400ms */}
|
|
1159
|
-
<p style={tw('text-2xl text-white/80 mt-4 ease-out enter-fade-in-up/300/400')}>
|
|
1160
|
-
{subtitle}
|
|
1161
|
-
</p>
|
|
1162
|
-
|
|
1163
|
-
{/* Continuous floating animation: repeats every 1s (1000ms) */}
|
|
1164
|
-
<div style={tw('mt-8 text-4xl loop-float/1000')}>
|
|
1165
|
-
⬇️
|
|
1166
|
-
</div>
|
|
1167
|
-
</div>
|
|
1168
|
-
);
|
|
1169
|
-
}
|
|
1170
|
-
```
|
|
1171
|
-
|
|
1172
|
-
## Animation Format
|
|
1173
|
-
|
|
1174
|
-
loopwind uses three types of animations with **millisecond timing**:
|
|
1175
|
-
|
|
1176
|
-
| Type | Format | Description |
|
|
1177
|
-
|------|--------|-------------|
|
|
1178
|
-
| Enter | `enter-{type}/{start}/{duration}` | Animations that play when entering |
|
|
1179
|
-
| Exit | `exit-{type}/{start}/{duration}` | Animations that play when exiting |
|
|
1180
|
-
| Loop | `loop-{type}/{duration}` | Continuous looping animations |
|
|
1181
|
-
|
|
1182
|
-
All timing values are in **milliseconds** (1000ms = 1 second).
|
|
1183
|
-
|
|
1184
|
-
## Utility-Based Animations
|
|
1185
|
-
|
|
1186
|
-
In addition to predefined animations, loopwind supports **Tailwind utility-based animations** that let you animate any transform or opacity property directly:
|
|
1187
|
-
|
|
1188
|
-
```tsx
|
|
1189
|
-
// Slide in 20px from the left
|
|
1190
|
-
<div style={tw('enter-translate-x-5/0/1000')}>Content</div>
|
|
1191
|
-
|
|
1192
|
-
// Rotate 90 degrees on entrance
|
|
1193
|
-
<div style={tw('enter-rotate-90/0/500')}>Spinning</div>
|
|
1194
|
-
|
|
1195
|
-
// Fade to 50% opacity in a loop
|
|
1196
|
-
<div style={tw('loop-opacity-50/1000')}>Pulsing</div>
|
|
1197
|
-
|
|
1198
|
-
// Scale down with negative value
|
|
1199
|
-
<div style={tw('enter--scale-50/0/800')}>Shrinking</div>
|
|
1200
|
-
```
|
|
1201
|
-
|
|
1202
|
-
### Supported Utilities
|
|
1203
|
-
|
|
1204
|
-
| Utility | Format | Description | Example |
|
|
1205
|
-
|---------|--------|-------------|---------|
|
|
1206
|
-
| **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 |
|
|
1207
|
-
| **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 |
|
|
1208
|
-
| **opacity** | `enter-opacity-{n}` | Set opacity (0-100) | `enter-opacity-50` = 50% |
|
|
1209
|
-
| **scale** | `enter-scale-{n}` | Scale element (0-200) | `enter-scale-100` = 1.0x |
|
|
1210
|
-
| **rotate** | `enter-rotate-{n}` | Rotate in degrees | `enter-rotate-45` = 45° |
|
|
1211
|
-
| **skew-x** | `enter-skew-x-{n}` | Skew on X axis in degrees | `enter-skew-x-12` = 12° |
|
|
1212
|
-
| **skew-y** | `enter-skew-y-{n}` | Skew on Y axis in degrees | `exit-skew-y-6` = 6° |
|
|
1213
|
-
|
|
1214
|
-
**Translate value formats:**
|
|
1215
|
-
- **Numeric**: `5` = 20px (Tailwind spacing scale: 1 unit = 4px)
|
|
1216
|
-
- **Keywords**: `full` = 100%
|
|
1217
|
-
- **Fractions**: `1/2` = 50%, `1/3` = 33.333%, `2/3` = 66.666%, etc.
|
|
1218
|
-
- **Arbitrary values**: `[20px]`, `[5rem]`, `[10%]` (rem converts to px: 1rem = 16px)
|
|
1219
|
-
|
|
1220
|
-
All utilities work with:
|
|
1221
|
-
- **All prefixes**: `enter-`, `exit-`, `loop-`, `animate-`
|
|
1222
|
-
- **Negative values**: Prefix with `-` (e.g., `-translate-x-5`, `-rotate-45`)
|
|
1223
|
-
- **Timing syntax**: Add `/start/duration` (e.g., `enter-translate-x-5/0/800`)
|
|
1224
|
-
|
|
1225
|
-
### Translate Animations
|
|
1226
|
-
|
|
1227
|
-
```tsx
|
|
1228
|
-
// Numeric (Tailwind spacing): 20px (5 * 4px)
|
|
1229
|
-
<div style={tw('enter-translate-x-5/0/500')}>Content</div>
|
|
1230
|
-
|
|
1231
|
-
// Keyword: Full width (100%)
|
|
1232
|
-
<div style={tw('enter-translate-y-full/0/800')}>Dropping full height</div>
|
|
1233
|
-
|
|
1234
|
-
// Fraction: Half width (50%)
|
|
1235
|
-
<div style={tw('enter-translate-x-1/2/0/600')}>Slide in halfway</div>
|
|
1236
|
-
|
|
1237
|
-
// Arbitrary values: Exact px or rem
|
|
1238
|
-
<div style={tw('enter-translate-y-[20px]/0/500')}>Slide 20px</div>
|
|
1239
|
-
<div style={tw('enter-translate-x-[5rem]/0/800')}>Slide 5rem (80px)</div>
|
|
1240
|
-
|
|
1241
|
-
// Loop with fractions
|
|
1242
|
-
<div style={tw('loop-translate-y-1/4/1000')}>Oscillate 25%</div>
|
|
1243
|
-
|
|
1244
|
-
// Negative values
|
|
1245
|
-
<div style={tw('exit--translate-y-8/2000/500')}>Rising</div>
|
|
1246
|
-
```
|
|
1247
|
-
|
|
1248
|
-
### Opacity Animations
|
|
1249
|
-
|
|
1250
|
-
```tsx
|
|
1251
|
-
// Fade to 100% opacity
|
|
1252
|
-
<div style={tw('enter-opacity-100/0/500')}>Fading In</div>
|
|
1253
|
-
|
|
1254
|
-
// Fade to 50% opacity
|
|
1255
|
-
<div style={tw('enter-opacity-50/0/800')}>Half Opacity</div>
|
|
1256
|
-
|
|
1257
|
-
// Pulse between 50% and 100%
|
|
1258
|
-
<div style={tw('loop-opacity-50/1000')}>Pulsing</div>
|
|
1259
|
-
|
|
1260
|
-
// Fade out to 0%
|
|
1261
|
-
<div style={tw('exit-opacity-0/2500/500')}>Vanishing</div>
|
|
1262
|
-
```
|
|
1263
|
-
|
|
1264
|
-
### Scale Animations
|
|
1265
|
-
|
|
1266
|
-
```tsx
|
|
1267
|
-
// Scale from 0 to 100% (1.0x)
|
|
1268
|
-
<div style={tw('enter-scale-100/0/500')}>Growing</div>
|
|
1269
|
-
|
|
1270
|
-
// Scale to 150% (1.5x)
|
|
1271
|
-
<div style={tw('enter-scale-150/0/800')}>Enlarging</div>
|
|
1272
|
-
|
|
1273
|
-
// Pulse scale in a loop
|
|
1274
|
-
<div style={tw('loop-scale-110/1000')}>Breathing</div>
|
|
1275
|
-
|
|
1276
|
-
// Scale down to 50%
|
|
1277
|
-
<div style={tw('exit-scale-50/2000/500')}>Shrinking</div>
|
|
1278
|
-
```
|
|
1279
|
-
|
|
1280
|
-
### Rotate Animations
|
|
1281
|
-
|
|
1282
|
-
```tsx
|
|
1283
|
-
// Rotate 90 degrees
|
|
1284
|
-
<div style={tw('enter-rotate-90/0/500')}>Quarter Turn</div>
|
|
1285
|
-
|
|
1286
|
-
// Rotate 180 degrees
|
|
1287
|
-
<div style={tw('enter-rotate-180/0/1000')}>Half Turn</div>
|
|
1288
|
-
|
|
1289
|
-
// Continuous rotation in loop (360 degrees per cycle)
|
|
1290
|
-
<div style={tw('loop-rotate-360/2000')}>Spinning</div>
|
|
1291
|
-
|
|
1292
|
-
// Rotate backwards with negative value
|
|
1293
|
-
<div style={tw('enter--rotate-45/0/500')}>Counter Rotation</div>
|
|
1294
|
-
```
|
|
1295
|
-
|
|
1296
|
-
### Skew Animations
|
|
1297
|
-
|
|
1298
|
-
```tsx
|
|
1299
|
-
// Skew on X axis
|
|
1300
|
-
<div style={tw('enter-skew-x-12/0/500')}>Slanted</div>
|
|
1301
|
-
|
|
1302
|
-
// Skew on Y axis
|
|
1303
|
-
<div style={tw('enter-skew-y-6/0/800')}>Tilted</div>
|
|
1304
|
-
|
|
1305
|
-
// Oscillating skew in loop
|
|
1306
|
-
<div style={tw('loop-skew-x-6/1000')}>Wobbling</div>
|
|
1307
|
-
|
|
1308
|
-
// Negative skew
|
|
1309
|
-
<div style={tw('exit--skew-x-12/2000/500')}>Reverse Slant</div>
|
|
1310
|
-
```
|
|
1311
|
-
|
|
1312
|
-
### Combining Utilities
|
|
1313
|
-
|
|
1314
|
-
You can combine multiple utility animations on the same element:
|
|
1315
|
-
|
|
1316
|
-
```tsx
|
|
1317
|
-
// Translate and rotate together
|
|
1318
|
-
<div style={tw('enter-translate-y-10/0/500 enter-rotate-45/0/500')}>
|
|
1319
|
-
Flying In
|
|
1320
|
-
</div>
|
|
1321
|
-
|
|
1322
|
-
// Fade and scale
|
|
1323
|
-
<div style={tw('enter-opacity-100/0/800 enter-scale-100/0/800')}>
|
|
1324
|
-
Appearing
|
|
1325
|
-
</div>
|
|
1326
|
-
|
|
1327
|
-
// Enter with translate, exit with rotation
|
|
1328
|
-
<div style={tw('enter-translate-x-5/0/500 exit-rotate-180/2500/500')}>
|
|
1329
|
-
Slide and Spin
|
|
1330
|
-
</div>
|
|
1331
|
-
```
|
|
1332
|
-
|
|
1333
|
-
### Bracket Notation
|
|
1334
|
-
|
|
1335
|
-
For more CSS-like syntax, you can use brackets with units:
|
|
1336
|
-
|
|
1337
|
-
```tsx
|
|
1338
|
-
// Using bracket notation with seconds
|
|
1339
|
-
<h1 style={tw('enter-slide-up/[0.6s]/[1.5s]')}>Hello</h1>
|
|
1340
|
-
|
|
1341
|
-
// Using bracket notation with milliseconds
|
|
1342
|
-
<h1 style={tw('enter-fade-in/[300ms]/[800ms]')}>World</h1>
|
|
1343
|
-
|
|
1344
|
-
// Mix and match - plain numbers are milliseconds
|
|
1345
|
-
<h1 style={tw('enter-bounce-in/0/[1.2s]')}>Mixed</h1>
|
|
1346
|
-
```
|
|
1347
|
-
|
|
1348
|
-
## Enter Animations
|
|
1349
|
-
|
|
1350
|
-
Format: `enter-{type}/{startMs}/{durationMs}`
|
|
1351
|
-
|
|
1352
|
-
- `startMs` - when the animation begins (milliseconds from start)
|
|
1353
|
-
- `durationMs` - how long the animation lasts
|
|
1354
|
-
|
|
1355
|
-
When values are omitted (`enter-fade-in`), it uses the full video duration.
|
|
1356
|
-
|
|
1357
|
-
### Fade Animations
|
|
1358
|
-
|
|
1359
|
-
Simple opacity transitions with optional direction.
|
|
1360
|
-
|
|
1361
|
-
```tsx
|
|
1362
|
-
// Fade in from 0ms to 500ms
|
|
1363
|
-
<h1 style={tw('enter-fade-in/0/500')}>Hello</h1>
|
|
1364
|
-
|
|
1365
|
-
// Fade in with upward motion
|
|
1366
|
-
<h1 style={tw('enter-fade-in-up/0/600')}>Hello</h1>
|
|
1367
|
-
```
|
|
1368
|
-
|
|
1369
|
-
| Class | Description |
|
|
1370
|
-
|-------|-------------|
|
|
1371
|
-
| `enter-fade-in/0/500` | Fade in (opacity 0 → 1) |
|
|
1372
|
-
| `enter-fade-in-up/0/500` | Fade in + slide up (30px) |
|
|
1373
|
-
| `enter-fade-in-down/0/500` | Fade in + slide down (30px) |
|
|
1374
|
-
| `enter-fade-in-left/0/500` | Fade in + slide from left (30px) |
|
|
1375
|
-
| `enter-fade-in-right/0/500` | Fade in + slide from right (30px) |
|
|
1376
|
-
|
|
1377
|
-
### Slide Animations
|
|
1378
|
-
|
|
1379
|
-
Larger movement (100px) with fade.
|
|
1380
|
-
|
|
1381
|
-
```tsx
|
|
1382
|
-
// Slide in from left: starts at 0, lasts 500ms
|
|
1383
|
-
<div style={tw('enter-slide-left/0/500')}>Content</div>
|
|
1384
|
-
|
|
1385
|
-
// Slide up from bottom: starts at 200ms, lasts 600ms
|
|
1386
|
-
<div style={tw('enter-slide-up/200/600')}>Content</div>
|
|
1387
|
-
```
|
|
1388
|
-
|
|
1389
|
-
| Class | Description |
|
|
1390
|
-
|-------|-------------|
|
|
1391
|
-
| `enter-slide-left/0/500` | Slide in from left (100px) |
|
|
1392
|
-
| `enter-slide-right/0/500` | Slide in from right (100px) |
|
|
1393
|
-
| `enter-slide-up/0/500` | Slide in from bottom (100px) |
|
|
1394
|
-
| `enter-slide-down/0/500` | Slide in from top (100px) |
|
|
1395
|
-
|
|
1396
|
-
### Bounce Animations
|
|
1397
|
-
|
|
1398
|
-
Playful entrance with overshoot effect.
|
|
1399
|
-
|
|
1400
|
-
```tsx
|
|
1401
|
-
// Bounce in with scale overshoot
|
|
1402
|
-
<h1 style={tw('enter-bounce-in/0/500')}>Bouncy!</h1>
|
|
1403
|
-
|
|
1404
|
-
// Bounce in from below
|
|
1405
|
-
<div style={tw('enter-bounce-in-up/0/600')}>Pop!</div>
|
|
1406
|
-
```
|
|
1407
|
-
|
|
1408
|
-
| Class | Description |
|
|
1409
|
-
|-------|-------------|
|
|
1410
|
-
| `enter-bounce-in/0/500` | Bounce in with scale overshoot |
|
|
1411
|
-
| `enter-bounce-in-up/0/500` | Bounce in from below |
|
|
1412
|
-
| `enter-bounce-in-down/0/500` | Bounce in from above |
|
|
1413
|
-
| `enter-bounce-in-left/0/500` | Bounce in from left |
|
|
1414
|
-
| `enter-bounce-in-right/0/500` | Bounce in from right |
|
|
1415
|
-
|
|
1416
|
-
### Scale & Zoom Animations
|
|
1417
|
-
|
|
1418
|
-
Size-based transitions.
|
|
1419
|
-
|
|
1420
|
-
```tsx
|
|
1421
|
-
// Scale in from 50%
|
|
1422
|
-
<div style={tw('enter-scale-in/0/500')}>Growing</div>
|
|
1423
|
-
|
|
1424
|
-
// Zoom in from 0%
|
|
1425
|
-
<div style={tw('enter-zoom-in/0/1000')}>Zooming</div>
|
|
1426
|
-
```
|
|
1427
|
-
|
|
1428
|
-
| Class | Description |
|
|
1429
|
-
|-------|-------------|
|
|
1430
|
-
| `enter-scale-in/0/500` | Scale up from 50% to 100% |
|
|
1431
|
-
| `enter-zoom-in/0/500` | Zoom in from 0% to 100% |
|
|
1432
|
-
|
|
1433
|
-
### Rotate & Flip Animations
|
|
1434
|
-
|
|
1435
|
-
Rotation-based transitions.
|
|
1436
|
-
|
|
1437
|
-
```tsx
|
|
1438
|
-
// Rotate in 180 degrees
|
|
1439
|
-
<div style={tw('enter-rotate-in/0/500')}>Spinning</div>
|
|
1440
|
-
|
|
1441
|
-
// 3D flip on X axis
|
|
1442
|
-
<div style={tw('enter-flip-in-x/0/500')}>Flipping</div>
|
|
1443
|
-
```
|
|
1444
|
-
|
|
1445
|
-
| Class | Description |
|
|
1446
|
-
|-------|-------------|
|
|
1447
|
-
| `enter-rotate-in/0/500` | Rotate in from -180° |
|
|
1448
|
-
| `enter-flip-in-x/0/500` | 3D flip on horizontal axis |
|
|
1449
|
-
| `enter-flip-in-y/0/500` | 3D flip on vertical axis |
|
|
1450
|
-
|
|
1451
|
-
## Exit Animations
|
|
1452
|
-
|
|
1453
|
-
Format: `exit-{type}/{startMs}/{durationMs}`
|
|
1454
|
-
|
|
1455
|
-
- `startMs` - when the exit animation begins
|
|
1456
|
-
- `durationMs` - how long the exit animation lasts
|
|
1457
|
-
|
|
1458
|
-
Exit animations use the same timing system but animate elements out.
|
|
1459
|
-
|
|
1460
|
-
```tsx
|
|
1461
|
-
// Fade out starting at 2500ms, lasting 500ms (ends at 3000ms)
|
|
1462
|
-
<h1 style={tw('exit-fade-out/2500/500')}>Goodbye</h1>
|
|
1463
|
-
|
|
1464
|
-
// Combined enter and exit on same element
|
|
1465
|
-
<h1 style={tw('enter-fade-in/0/500 exit-fade-out/2500/500')}>
|
|
1466
|
-
Hello and Goodbye
|
|
1467
|
-
</h1>
|
|
1468
|
-
```
|
|
1469
|
-
|
|
1470
|
-
| Class | Description |
|
|
1471
|
-
|-------|-------------|
|
|
1472
|
-
| `exit-fade-out/2500/500` | Fade out (opacity 1 → 0) |
|
|
1473
|
-
| `exit-fade-out-up/2500/500` | Fade out + slide up |
|
|
1474
|
-
| `exit-fade-out-down/2500/500` | Fade out + slide down |
|
|
1475
|
-
| `exit-fade-out-left/2500/500` | Fade out + slide left |
|
|
1476
|
-
| `exit-fade-out-right/2500/500` | Fade out + slide right |
|
|
1477
|
-
| `exit-slide-up/2500/500` | Slide out upward (100px) |
|
|
1478
|
-
| `exit-slide-down/2500/500` | Slide out downward (100px) |
|
|
1479
|
-
| `exit-slide-left/2500/500` | Slide out to left (100px) |
|
|
1480
|
-
| `exit-slide-right/2500/500` | Slide out to right (100px) |
|
|
1481
|
-
| `exit-scale-out/2500/500` | Scale out to 150% |
|
|
1482
|
-
| `exit-zoom-out/2500/500` | Zoom out to 200% |
|
|
1483
|
-
| `exit-rotate-out/2500/500` | Rotate out to 180° |
|
|
1484
|
-
| `exit-bounce-out/2500/500` | Bounce out with scale |
|
|
1485
|
-
| `exit-bounce-out-up/2500/500` | Bounce out upward |
|
|
1486
|
-
| `exit-bounce-out-down/2500/500` | Bounce out downward |
|
|
1487
|
-
| `exit-bounce-out-left/2500/500` | Bounce out to left |
|
|
1488
|
-
| `exit-bounce-out-right/2500/500` | Bounce out to right |
|
|
1489
|
-
|
|
1490
|
-
## Loop Animations
|
|
1491
|
-
|
|
1492
|
-
Format: `loop-{type}/{durationMs}`
|
|
1493
|
-
|
|
1494
|
-
Loop animations repeat every `{durationMs}` milliseconds:
|
|
1495
|
-
- `/1000` = 1 second loop
|
|
1496
|
-
- `/500` = 0.5 second loop
|
|
1497
|
-
- `/2000` = 2 second loop
|
|
1498
|
-
|
|
1499
|
-
When duration is omitted (`loop-bounce`), it defaults to 1000ms (1 second).
|
|
1500
|
-
|
|
1501
|
-
```tsx
|
|
1502
|
-
// Pulse opacity every 500ms
|
|
1503
|
-
<div style={tw('loop-fade/500')}>Pulsing</div>
|
|
1504
|
-
|
|
1505
|
-
// Bounce every 800ms
|
|
1506
|
-
<div style={tw('loop-bounce/800')}>Bouncing</div>
|
|
1507
|
-
|
|
1508
|
-
// Full rotation every 2000ms
|
|
1509
|
-
<div style={tw('loop-spin/2000')}>Spinning</div>
|
|
1510
|
-
```
|
|
1511
|
-
|
|
1512
|
-
| Class | Description |
|
|
1513
|
-
|-------|-------------|
|
|
1514
|
-
| `loop-fade/{ms}` | Opacity pulse (0.5 → 1 → 0.5) |
|
|
1515
|
-
| `loop-bounce/{ms}` | Bounce up and down |
|
|
1516
|
-
| `loop-spin/{ms}` | Full 360° rotation |
|
|
1517
|
-
| `loop-ping/{ms}` | Scale up + fade out (radar effect) |
|
|
1518
|
-
| `loop-wiggle/{ms}` | Side to side wiggle |
|
|
1519
|
-
| `loop-float/{ms}` | Gentle up and down floating |
|
|
1520
|
-
| `loop-pulse/{ms}` | Scale pulse (1.0 → 1.05 → 1.0) |
|
|
1521
|
-
| `loop-shake/{ms}` | Shake side to side |
|
|
1522
|
-
|
|
1523
|
-
## Easing Functions
|
|
1524
|
-
|
|
1525
|
-
Add an easing class **before** the animation class to control the timing curve.
|
|
1526
|
-
|
|
1527
|
-
```tsx
|
|
1528
|
-
// Ease in (accelerate)
|
|
1529
|
-
<h1 style={tw('ease-in enter-fade-in/0/1000')}>Accelerating</h1>
|
|
1530
|
-
|
|
1531
|
-
// Ease out (decelerate) - default
|
|
1532
|
-
<h1 style={tw('ease-out enter-fade-in/0/1000')}>Decelerating</h1>
|
|
1533
|
-
|
|
1534
|
-
// Ease in-out (smooth)
|
|
1535
|
-
<h1 style={tw('ease-in-out enter-fade-in/0/1000')}>Smooth</h1>
|
|
1536
|
-
|
|
1537
|
-
// Strong cubic easing
|
|
1538
|
-
<h1 style={tw('ease-out-cubic enter-bounce-in/0/500')}>Dramatic</h1>
|
|
1539
|
-
```
|
|
1540
|
-
|
|
1541
|
-
| Class | Description | Best For |
|
|
1542
|
-
|-------|-------------|----------|
|
|
1543
|
-
| `linear` | Constant speed | Mechanical motion |
|
|
1544
|
-
| `ease-in` | Slow start, fast end | Exit animations |
|
|
1545
|
-
| `ease-out` | Fast start, slow end (default) | Enter animations |
|
|
1546
|
-
| `ease-in-out` | Slow start and end | Subtle transitions |
|
|
1547
|
-
| `ease-in-cubic` | Strong slow start | Dramatic exits |
|
|
1548
|
-
| `ease-out-cubic` | Strong fast start | Impactful entrances |
|
|
1549
|
-
| `ease-in-out-cubic` | Strong both ends | Emphasis animations |
|
|
1550
|
-
| `ease-in-quart` | Very strong slow start | Powerful exits |
|
|
1551
|
-
| `ease-out-quart` | Very strong fast start | Punchy entrances |
|
|
1552
|
-
| `ease-in-out-quart` | Very strong both ends | Maximum drama |
|
|
1553
|
-
|
|
1554
|
-
## Combining Enter and Exit
|
|
1555
|
-
|
|
1556
|
-
You can use both enter and exit animations on the same element:
|
|
1557
|
-
|
|
1558
|
-
```tsx
|
|
1559
|
-
export default function EnterExit({ tw, title }) {
|
|
1560
|
-
return (
|
|
1561
|
-
<div style={tw('flex items-center justify-center w-full h-full bg-black')}>
|
|
1562
|
-
{/* Fade in during first 500ms, fade out during last 500ms (assuming 3s video) */}
|
|
1563
|
-
<h1 style={tw('text-8xl font-bold text-white enter-fade-in/0/500 exit-fade-out/2500/500')}>
|
|
1564
|
-
{title}
|
|
1565
|
-
</h1>
|
|
1566
|
-
</div>
|
|
1567
|
-
);
|
|
1568
|
-
}
|
|
1569
|
-
```
|
|
1570
|
-
|
|
1571
|
-
The opacities from multiple animations are **multiplied together**, so you get smooth transitions that combine properly.
|
|
1572
|
-
|
|
1573
|
-
## Staggered Animations
|
|
1574
|
-
|
|
1575
|
-
Create sequenced animations by offsetting start times:
|
|
1576
|
-
|
|
1577
|
-
```tsx
|
|
1578
|
-
export default function StaggeredList({ tw, items }) {
|
|
1579
|
-
return (
|
|
1580
|
-
<div style={tw('flex flex-col gap-4')}>
|
|
1581
|
-
{/* First item: starts at 0ms, lasts 300ms */}
|
|
1582
|
-
<div style={tw('ease-out enter-fade-in-left/0/300')}>
|
|
1583
|
-
{items[0]}
|
|
1584
|
-
</div>
|
|
1585
|
-
|
|
1586
|
-
{/* Second item: starts at 100ms, lasts 300ms */}
|
|
1587
|
-
<div style={tw('ease-out enter-fade-in-left/100/300')}>
|
|
1588
|
-
{items[1]}
|
|
1589
|
-
</div>
|
|
1590
|
-
|
|
1591
|
-
{/* Third item: starts at 200ms, lasts 300ms */}
|
|
1592
|
-
<div style={tw('ease-out enter-fade-in-left/200/300')}>
|
|
1593
|
-
{items[2]}
|
|
1594
|
-
</div>
|
|
1595
|
-
</div>
|
|
1596
|
-
);
|
|
1597
|
-
}
|
|
1598
|
-
```
|
|
1599
|
-
|
|
1600
|
-
### Dynamic Staggering
|
|
1601
|
-
|
|
1602
|
-
For dynamic lists, calculate the timing programmatically:
|
|
1603
|
-
|
|
1604
|
-
```tsx
|
|
1605
|
-
export default function DynamicStagger({ tw, items }) {
|
|
1606
|
-
return (
|
|
1607
|
-
<div style={tw('flex flex-col gap-4')}>
|
|
1608
|
-
{items.map((item, i) => {
|
|
1609
|
-
const start = i * 100; // Each item starts 100ms later
|
|
1610
|
-
const duration = 300; // Each animation lasts 300ms
|
|
1611
|
-
|
|
1612
|
-
return (
|
|
1613
|
-
<div
|
|
1614
|
-
key={i}
|
|
1615
|
-
style={tw(`ease-out enter-fade-in-up/${start}/${duration}`)}
|
|
1616
|
-
>
|
|
1617
|
-
{item}
|
|
1618
|
-
</div>
|
|
1619
|
-
);
|
|
1620
|
-
})}
|
|
1621
|
-
</div>
|
|
1622
|
-
);
|
|
1623
|
-
}
|
|
1624
|
-
```
|
|
1625
|
-
|
|
1626
|
-
## Common Patterns
|
|
1627
|
-
|
|
1628
|
-
### Intro Sequence
|
|
1629
|
-
|
|
1630
|
-
```tsx
|
|
1631
|
-
export default function IntroVideo({ tw, title, subtitle, logo }) {
|
|
1632
|
-
return (
|
|
1633
|
-
<div style={tw('flex flex-col items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
|
|
1634
|
-
{/* Logo appears first */}
|
|
1635
|
-
<img
|
|
1636
|
-
src={logo}
|
|
1637
|
-
style={tw('h-20 mb-8 ease-out enter-scale-in/0/300')}
|
|
1638
|
-
/>
|
|
1639
|
-
|
|
1640
|
-
{/* Title bounces in */}
|
|
1641
|
-
<h1 style={tw('text-7xl font-bold text-white ease-out enter-bounce-in-up/200/500')}>
|
|
1642
|
-
{title}
|
|
1643
|
-
</h1>
|
|
1644
|
-
|
|
1645
|
-
{/* Subtitle fades in last */}
|
|
1646
|
-
<p style={tw('text-2xl text-white/80 mt-4 ease-out enter-fade-in-up/400/700')}>
|
|
1647
|
-
{subtitle}
|
|
1648
|
-
</p>
|
|
1649
|
-
</div>
|
|
1650
|
-
);
|
|
1651
|
-
}
|
|
1652
|
-
```
|
|
1653
|
-
|
|
1654
|
-
### Text Reveal
|
|
1655
|
-
|
|
1656
|
-
```tsx
|
|
1657
|
-
export default function TextReveal({ tw, words }) {
|
|
1658
|
-
return (
|
|
1659
|
-
<div style={tw('flex flex-wrap gap-2 justify-center')}>
|
|
1660
|
-
{words.split(' ').map((word, i) => (
|
|
1661
|
-
<span
|
|
1662
|
-
key={i}
|
|
1663
|
-
style={tw(`text-4xl font-bold ease-out enter-fade-in-up/${i * 100}/200`)}
|
|
1664
|
-
>
|
|
1665
|
-
{word}
|
|
1666
|
-
</span>
|
|
1667
|
-
))}
|
|
1668
|
-
</div>
|
|
1669
|
-
);
|
|
1670
|
-
}
|
|
1671
|
-
```
|
|
1672
|
-
|
|
1673
|
-
### Looping Background Element
|
|
1674
|
-
|
|
1675
|
-
```tsx
|
|
1676
|
-
export default function AnimatedBackground({ tw, children }) {
|
|
1677
|
-
return (
|
|
1678
|
-
<div style={tw('relative w-full h-full')}>
|
|
1679
|
-
{/* Floating background circles */}
|
|
1680
|
-
<div style={tw('absolute top-10 left-10 w-20 h-20 rounded-full bg-white/10 loop-float/2000')} />
|
|
1681
|
-
<div style={tw('absolute bottom-20 right-20 w-32 h-32 rounded-full bg-white/10 loop-fade/1500')} />
|
|
1682
|
-
|
|
1683
|
-
{/* Main content */}
|
|
1684
|
-
<div style={tw('relative z-10')}>
|
|
1685
|
-
{children}
|
|
1686
|
-
</div>
|
|
1687
|
-
</div>
|
|
1688
|
-
);
|
|
1689
|
-
}
|
|
1690
|
-
```
|
|
1691
|
-
|
|
1692
|
-
### Full Enter/Exit Animation
|
|
1693
|
-
|
|
1694
|
-
```tsx
|
|
1695
|
-
export default function FullAnimation({ tw, title }) {
|
|
1696
|
-
return (
|
|
1697
|
-
<div style={tw('flex items-center justify-center w-full h-full bg-black')}>
|
|
1698
|
-
{/* Enter: starts at 0, lasts 400ms. Exit: starts at 2600ms, lasts 400ms */}
|
|
1699
|
-
<h1 style={tw('text-8xl font-bold text-white ease-out enter-bounce-in-up/0/400 exit-fade-out-up/2600/400')}>
|
|
1700
|
-
{title}
|
|
1701
|
-
</h1>
|
|
1702
|
-
</div>
|
|
1703
|
-
);
|
|
1704
|
-
}
|
|
1705
|
-
```
|
|
1706
|
-
|
|
1707
|
-
## Programmatic Animations
|
|
1708
|
-
|
|
1709
|
-
For complete control beyond animation classes, use `progress` and `frame` directly.
|
|
1710
|
-
|
|
1711
|
-
### Available Props
|
|
1712
|
-
|
|
1713
|
-
| Prop | Type | Description |
|
|
1714
|
-
|------|------|-------------|
|
|
1715
|
-
| `progress` | `number` | 0 to 1 through the video (0% to 100%) |
|
|
1716
|
-
| `frame` | `number` | Current frame number (0, 1, 2, ... totalFrames-1) |
|
|
1717
|
-
|
|
1718
|
-
These are **only available in video templates**. Use them when animation classes aren't flexible enough.
|
|
1719
|
-
|
|
1720
|
-
### Using `frame`
|
|
1721
|
-
|
|
1722
|
-
```tsx
|
|
1723
|
-
export default function FrameAnimation({ tw, frame, title }) {
|
|
1724
|
-
// Color cycling using frame number
|
|
1725
|
-
const hue = (frame * 5) % 360; // Cycle through colors
|
|
1726
|
-
|
|
1727
|
-
// Pulsing based on frame
|
|
1728
|
-
const fps = 30;
|
|
1729
|
-
const pulse = Math.sin(frame / fps * Math.PI * 2) * 0.2 + 0.8; // 0.6 to 1.0
|
|
1730
|
-
|
|
1731
|
-
return (
|
|
1732
|
-
<div style={tw('flex items-center justify-center w-full h-full bg-black')}>
|
|
1733
|
-
<h1 style={{
|
|
1734
|
-
...tw('text-8xl font-bold'),
|
|
1735
|
-
color: `hsl(${hue}, 70%, 60%)`,
|
|
1736
|
-
transform: `scale(${pulse})`
|
|
1737
|
-
}}>
|
|
1738
|
-
{title}
|
|
1739
|
-
</h1>
|
|
1740
|
-
</div>
|
|
1741
|
-
);
|
|
1742
|
-
}
|
|
1743
|
-
```
|
|
1744
|
-
|
|
1745
|
-
### Using `progress`
|
|
1746
|
-
|
|
1747
|
-
```tsx
|
|
1748
|
-
export default function ProgressAnimation({ tw, progress, title }) {
|
|
1749
|
-
// Custom fade based on progress
|
|
1750
|
-
const opacity = progress < 0.3 ? progress / 0.3 : 1;
|
|
1751
|
-
|
|
1752
|
-
// Custom scale based on progress
|
|
1753
|
-
const scale = 0.8 + progress * 0.2; // 0.8 to 1.0
|
|
1754
|
-
|
|
1755
|
-
return (
|
|
1756
|
-
<div style={tw('flex items-center justify-center w-full h-full bg-gray-900')}>
|
|
1757
|
-
<h1 style={{
|
|
1758
|
-
...tw('text-8xl font-bold text-white'),
|
|
1759
|
-
opacity,
|
|
1760
|
-
transform: `scale(${scale})`
|
|
1761
|
-
}}>
|
|
1762
|
-
{title}
|
|
1763
|
-
</h1>
|
|
1764
|
-
</div>
|
|
1765
|
-
);
|
|
1766
|
-
}
|
|
1767
|
-
```
|
|
1768
|
-
|
|
1769
|
-
### Custom Easing
|
|
1770
|
-
|
|
1771
|
-
```tsx
|
|
1772
|
-
export default function CustomEasing({ tw, progress, title }) {
|
|
1773
|
-
// Smoothstep easing
|
|
1774
|
-
const eased = progress * progress * (3 - 2 * progress);
|
|
1775
|
-
|
|
1776
|
-
// Elastic easing
|
|
1777
|
-
const elastic = Math.pow(2, -10 * progress) * Math.sin((progress - 0.075) * (2 * Math.PI) / 0.3) + 1;
|
|
1778
|
-
|
|
1779
|
-
return (
|
|
1780
|
-
<div style={tw('flex items-center justify-center w-full h-full')}>
|
|
1781
|
-
<h1 style={{
|
|
1782
|
-
...tw('text-8xl font-bold'),
|
|
1783
|
-
opacity: eased,
|
|
1784
|
-
transform: `translateY(${(1 - elastic) * 100}px)`
|
|
1785
|
-
}}>
|
|
1786
|
-
{title}
|
|
1787
|
-
</h1>
|
|
1788
|
-
</div>
|
|
1789
|
-
);
|
|
1790
|
-
}
|
|
1791
|
-
```
|
|
1792
|
-
|
|
1793
|
-
### When to Use Programmatic Animations
|
|
1794
|
-
|
|
1795
|
-
Use `progress`/`frame` instead of animation classes when you need:
|
|
1796
|
-
- **Custom easing functions** (elastic, spring, bounce with specific curves)
|
|
1797
|
-
- **Color cycling or gradients** based on time
|
|
1798
|
-
- **Mathematical animations** (sine waves, spirals, etc.)
|
|
1799
|
-
- **Complex multi-property animations** that need precise coordination
|
|
1800
|
-
- **Conditional logic** based on specific frame numbers
|
|
1801
|
-
|
|
1802
|
-
For everything else, prefer animation classes - they're simpler and more maintainable.
|
|
1803
|
-
|
|
1804
|
-
## Performance Tips
|
|
1805
|
-
|
|
1806
|
-
1. **Use Tailwind classes** when possible - they're optimized for the renderer
|
|
1807
|
-
2. **Avoid too many nested animations** - each adds computation per frame
|
|
1808
|
-
3. **Use loop animations sparingly** - they're computed every frame
|
|
1809
|
-
4. **Prefer opacity and transform** - they're the most performant properties
|
|
1810
|
-
|
|
1811
|
-
## Next Steps
|
|
1812
|
-
|
|
1813
|
-
- [Video Templates](/video) - Creating video templates
|
|
1814
|
-
- [SDK](/sdk) - Programmatic rendering with animations
|
|
1815
|
-
- [Helpers](/helpers) - QR codes, images, and more
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
================================================================================
|
|
1821
|
-
FILE: helpers.mdx
|
|
1822
|
-
================================================================================
|
|
1823
|
-
|
|
1824
|
-
# Template Helpers
|
|
1825
|
-
|
|
1826
|
-
Additional helpers for creating powerful, composable templates.
|
|
1827
|
-
|
|
1828
|
-
## Overview
|
|
1829
|
-
|
|
1830
|
-
Beyond the basics, loopwind provides:
|
|
1831
|
-
- `template()` - Compose templates together
|
|
1832
|
-
- `qr()` - Generate QR codes on the fly
|
|
1833
|
-
- `config` - Access user configuration
|
|
1834
|
-
|
|
1835
|
-
For image embedding, see the [Images](/images) page.
|
|
1836
|
-
|
|
1837
|
-
## Template Composition
|
|
1838
|
-
|
|
1839
|
-
Compose multiple templates together to create complex designs.
|
|
1840
|
-
|
|
1841
|
-
### Usage
|
|
1842
|
-
|
|
1843
|
-
```tsx
|
|
1844
|
-
export default function CompositeCard({ tw, template, title, author, avatar }) {
|
|
1845
|
-
return (
|
|
1846
|
-
<div style={tw('w-full h-full bg-gradient-to-br from-purple-600 to-blue-500 p-12')}>
|
|
1847
|
-
<div style={tw('bg-white rounded-2xl p-8 shadow-xl')}>
|
|
1848
|
-
<h1 style={tw('text-4xl font-bold text-gray-900 mb-6')}>{title}</h1>
|
|
1849
|
-
|
|
1850
|
-
{/* Embed another template */}
|
|
1851
|
-
<div style={tw('mb-6')}>
|
|
1852
|
-
{template('user-badge', {
|
|
1853
|
-
name: author,
|
|
1854
|
-
avatar: avatar
|
|
1855
|
-
})}
|
|
1856
|
-
</div>
|
|
1857
|
-
|
|
1858
|
-
<p style={tw('text-gray-600')}>Published by {author}</p>
|
|
1859
|
-
</div>
|
|
1860
|
-
</div>
|
|
1861
|
-
);
|
|
1862
|
-
}
|
|
1863
|
-
```
|
|
1864
|
-
|
|
1865
|
-
**How it works:**
|
|
1866
|
-
1. `template(name, props)` renders another installed template
|
|
1867
|
-
2. The embedded template is rendered at its specified size
|
|
1868
|
-
3. You can embed multiple templates in one design
|
|
1869
|
-
4. Templates can be nested (template within a template)
|
|
1870
|
-
|
|
1871
|
-
### Use Cases
|
|
1872
|
-
|
|
1873
|
-
**1. Reusable components:**
|
|
1874
|
-
```tsx
|
|
1875
|
-
// Create a logo template once, use it everywhere
|
|
1876
|
-
<div>{template('company-logo', { variant: 'dark' })}</div>
|
|
1877
|
-
```
|
|
1878
|
-
|
|
1879
|
-
**2. Complex layouts:**
|
|
1880
|
-
```tsx
|
|
1881
|
-
// Combine multiple templates into one design
|
|
1882
|
-
<div style={tw('grid grid-cols-2 gap-4')}>
|
|
1883
|
-
{template('product-card', { product: product1 })}
|
|
1884
|
-
{template('product-card', { product: product2 })}
|
|
1885
|
-
</div>
|
|
1886
|
-
```
|
|
1887
|
-
|
|
1888
|
-
**3. Dynamic content:**
|
|
1889
|
-
```tsx
|
|
1890
|
-
// Render templates based on data
|
|
1891
|
-
{users.map(user =>
|
|
1892
|
-
template('user-avatar', { name: user.name, image: user.avatar })
|
|
1893
|
-
)}
|
|
1894
|
-
```
|
|
1895
|
-
|
|
1896
|
-
### Best Practices
|
|
1897
|
-
|
|
1898
|
-
1. **Keep templates focused** - Each template should do one thing well
|
|
1899
|
-
2. **Pass minimal props** - Only pass what the embedded template needs
|
|
1900
|
-
3. **Document dependencies** - Note which templates are required in your README
|
|
1901
|
-
4. **Avoid deep nesting** - Too many nested templates can be hard to debug
|
|
1902
|
-
|
|
1903
|
-
## QR Codes
|
|
1904
|
-
|
|
1905
|
-
Generate QR codes dynamically in your templates.
|
|
1906
|
-
|
|
1907
|
-
### Usage
|
|
1908
|
-
|
|
1909
|
-
```tsx
|
|
1910
|
-
export default function QRCard({ tw, qr, title, url }) {
|
|
1911
|
-
return (
|
|
1912
|
-
<div style={tw('flex flex-col items-center justify-center w-full h-full bg-white p-10')}>
|
|
1913
|
-
<h1 style={tw('text-4xl font-bold text-black mb-8')}>{title}</h1>
|
|
1914
|
-
|
|
1915
|
-
{/* Generate QR code for the URL */}
|
|
1916
|
-
<img src={qr(url)} style={tw('w-64 h-64')} />
|
|
1917
|
-
|
|
1918
|
-
<p style={tw('text-gray-600 mt-4')}>{url}</p>
|
|
1919
|
-
</div>
|
|
1920
|
-
);
|
|
1921
|
-
}
|
|
1922
|
-
```
|
|
1923
|
-
|
|
1924
|
-
**Props format:**
|
|
1925
|
-
```json
|
|
1926
|
-
{
|
|
1927
|
-
"title": "Scan Me",
|
|
1928
|
-
"url": "https://example.com"
|
|
1929
|
-
}
|
|
1930
|
-
```
|
|
1931
|
-
|
|
1932
|
-
### QR Options
|
|
1933
|
-
|
|
1934
|
-
You can customize QR code appearance:
|
|
1935
|
-
|
|
1936
|
-
```tsx
|
|
1937
|
-
// Basic QR code
|
|
1938
|
-
<img src={qr('https://example.com')} />
|
|
1939
|
-
|
|
1940
|
-
// With error correction level
|
|
1941
|
-
<img src={qr('https://example.com', { errorCorrectionLevel: 'H' })} />
|
|
1942
|
-
|
|
1943
|
-
// With custom size
|
|
1944
|
-
<img src={qr('https://example.com', { width: 512 })} />
|
|
1945
|
-
```
|
|
1946
|
-
|
|
1947
|
-
**Error correction levels:**
|
|
1948
|
-
- `L` - Low (~7% correction)
|
|
1949
|
-
- `M` - Medium (~15% correction) - default
|
|
1950
|
-
- `Q` - Quartile (~25% correction)
|
|
1951
|
-
- `H` - High (~30% correction)
|
|
1952
|
-
|
|
1953
|
-
## User Configuration
|
|
1954
|
-
|
|
1955
|
-
Access user settings from `_loopwind/loopwind.json` using the `config` prop:
|
|
1956
|
-
|
|
1957
|
-
```tsx
|
|
1958
|
-
export default function BrandedTemplate({ tw, config, title }) {
|
|
1959
|
-
// Access custom colors from loopwind.json
|
|
1960
|
-
const primaryColor = config?.colors?.brand || '#6366f1';
|
|
1961
|
-
|
|
1962
|
-
return (
|
|
1963
|
-
<div style={tw('w-full h-full p-12')}>
|
|
1964
|
-
<h1 style={{
|
|
1965
|
-
...tw('text-6xl font-bold'),
|
|
1966
|
-
color: primaryColor
|
|
1967
|
-
}}>
|
|
1968
|
-
{title}
|
|
1969
|
-
</h1>
|
|
1970
|
-
</div>
|
|
1971
|
-
);
|
|
1972
|
-
}
|
|
1973
|
-
```
|
|
1974
|
-
|
|
1975
|
-
**User's `_loopwind/loopwind.json`:**
|
|
1976
|
-
```json title="_loopwind/loopwind.json"
|
|
1977
|
-
{
|
|
1978
|
-
"colors": {
|
|
1979
|
-
"brand": "#ff6b6b"
|
|
1980
|
-
},
|
|
1981
|
-
"fonts": {
|
|
1982
|
-
"sans": ["Inter", "system-ui", "sans-serif"]
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
```
|
|
1986
|
-
|
|
1987
|
-
This allows templates to adapt to user preferences and brand guidelines.
|
|
1988
|
-
|
|
1989
|
-
## All Props Reference
|
|
1990
|
-
|
|
1991
|
-
Every template receives these props:
|
|
1992
|
-
|
|
1993
|
-
```tsx
|
|
1994
|
-
export default function MyTemplate({
|
|
1995
|
-
// Core helpers
|
|
1996
|
-
tw, // Tailwind class converter
|
|
1997
|
-
qr, // QR code generator (this page)
|
|
1998
|
-
template, // Template composer (this page)
|
|
1999
|
-
config, // User config from loopwind.json (this page)
|
|
2000
|
-
|
|
2001
|
-
// Media helpers (see dedicated pages)
|
|
2002
|
-
image, // Image embedder → see /images
|
|
2003
|
-
|
|
2004
|
-
// Video-specific (only in video templates)
|
|
2005
|
-
frame, // Current frame number → see /video
|
|
2006
|
-
progress, // Animation progress 0-1 → see /video
|
|
2007
|
-
|
|
2008
|
-
// Your custom props
|
|
2009
|
-
...props // Any props from your props.json
|
|
2010
|
-
}) {
|
|
2011
|
-
// Your template code
|
|
2012
|
-
}
|
|
2013
|
-
```
|
|
2014
|
-
|
|
2015
|
-
## Next Steps
|
|
2016
|
-
|
|
2017
|
-
- [Image Rendering and Embedding](/images)
|
|
2018
|
-
- [Video Animation](/video)
|
|
2019
|
-
- [Styling with Tailwind & shadcn/ui](/styling)
|
|
2020
|
-
- [Custom Fonts](/fonts)
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
================================================================================
|
|
2027
|
-
FILE: styling.mdx
|
|
2028
|
-
================================================================================
|
|
2029
|
-
|
|
2030
|
-
# Styling Templates
|
|
2031
|
-
|
|
2032
|
-
Style your templates with Tailwind utility classes and shadcn/ui's beautiful design system.
|
|
2033
|
-
|
|
2034
|
-
## Quick Start
|
|
2035
|
-
|
|
2036
|
-
```tsx
|
|
2037
|
-
export default function MyTemplate({ title, tw }) {
|
|
2038
|
-
return (
|
|
2039
|
-
<div style={tw('flex items-center justify-center w-full h-full bg-gradient-to-br from-blue-600 to-purple-700')}>
|
|
2040
|
-
<h1 style={tw('text-7xl font-bold text-white')}>
|
|
2041
|
-
{title}
|
|
2042
|
-
</h1>
|
|
2043
|
-
</div>
|
|
2044
|
-
);
|
|
2045
|
-
}
|
|
2046
|
-
```
|
|
2047
|
-
|
|
2048
|
-
## The `tw()` Function
|
|
2049
|
-
|
|
2050
|
-
Every template receives a `tw()` function that converts Tailwind classes to inline styles compatible with Satori:
|
|
2051
|
-
|
|
2052
|
-
```tsx
|
|
2053
|
-
// Tailwind classes
|
|
2054
|
-
tw('flex items-center justify-center p-8 bg-blue-500')
|
|
2055
|
-
|
|
2056
|
-
// Converts to inline styles:
|
|
2057
|
-
{
|
|
2058
|
-
display: 'flex',
|
|
2059
|
-
alignItems: 'center',
|
|
2060
|
-
justifyContent: 'center',
|
|
2061
|
-
padding: '2rem',
|
|
2062
|
-
backgroundColor: '#3b82f6'
|
|
2063
|
-
}
|
|
2064
|
-
```
|
|
2065
|
-
|
|
2066
|
-
### Basic Usage
|
|
2067
|
-
|
|
2068
|
-
```tsx
|
|
2069
|
-
export default function Banner({ title, subtitle, tw }) {
|
|
2070
|
-
return (
|
|
2071
|
-
<div style={tw('w-full h-full p-12 bg-gray-50')}>
|
|
2072
|
-
<h1 style={tw('text-6xl font-bold text-gray-900 mb-4')}>
|
|
2073
|
-
{title}
|
|
2074
|
-
</h1>
|
|
2075
|
-
<p style={tw('text-2xl text-gray-600')}>
|
|
2076
|
-
{subtitle}
|
|
2077
|
-
</p>
|
|
2078
|
-
</div>
|
|
2079
|
-
);
|
|
2080
|
-
}
|
|
2081
|
-
```
|
|
2082
|
-
|
|
2083
|
-
### Combining with Custom Styles
|
|
2084
|
-
|
|
2085
|
-
Mix Tailwind classes with custom styles using the spread operator:
|
|
2086
|
-
|
|
2087
|
-
```tsx
|
|
2088
|
-
export default function CustomGradient({ title, tw }) {
|
|
2089
|
-
return (
|
|
2090
|
-
<div
|
|
2091
|
-
style={{
|
|
2092
|
-
...tw('flex flex-col items-center justify-center w-full h-full p-20'),
|
|
2093
|
-
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
2094
|
-
}}
|
|
2095
|
-
>
|
|
2096
|
-
<h1 style={tw('text-8xl font-bold text-white')}>{title}</h1>
|
|
2097
|
-
</div>
|
|
2098
|
-
);
|
|
2099
|
-
}
|
|
2100
|
-
```
|
|
2101
|
-
|
|
2102
|
-
## shadcn/ui Design System
|
|
2103
|
-
|
|
2104
|
-
loopwind uses **shadcn/ui's design system** by default, providing semantic color tokens for beautiful, consistent designs.
|
|
2105
|
-
|
|
2106
|
-
### Default Color Palette
|
|
2107
|
-
|
|
2108
|
-
All templates automatically have access to these semantic colors defined in `_loopwind/loopwind.json`:
|
|
2109
|
-
|
|
2110
|
-
```typescript
|
|
2111
|
-
colors: {
|
|
2112
|
-
// Primary colors
|
|
2113
|
-
primary: '#18181b', // Main brand color
|
|
2114
|
-
'primary-foreground': '#fafafa',
|
|
2115
|
-
|
|
2116
|
-
// Secondary colors
|
|
2117
|
-
secondary: '#f4f4f5', // Subtle accents
|
|
2118
|
-
'secondary-foreground': '#18181b',
|
|
2119
|
-
|
|
2120
|
-
// Background
|
|
2121
|
-
background: '#ffffff', // Page background
|
|
2122
|
-
foreground: '#09090b', // Main text color
|
|
2123
|
-
|
|
2124
|
-
// Muted
|
|
2125
|
-
muted: '#f4f4f5', // Subtle backgrounds
|
|
2126
|
-
'muted-foreground': '#71717a', // Muted text
|
|
2127
|
-
|
|
2128
|
-
// Accent
|
|
2129
|
-
accent: '#f4f4f5', // Highlight color
|
|
2130
|
-
'accent-foreground': '#18181b',
|
|
2131
|
-
|
|
2132
|
-
// Destructive
|
|
2133
|
-
destructive: '#ef4444', // Error/danger states
|
|
2134
|
-
'destructive-foreground': '#fafafa',
|
|
2135
|
-
|
|
2136
|
-
// UI Elements
|
|
2137
|
-
border: '#e4e4e7', // Border color
|
|
2138
|
-
input: '#e4e4e7', // Input borders
|
|
2139
|
-
ring: '#18181b', // Focus rings
|
|
2140
|
-
card: '#ffffff', // Card background
|
|
2141
|
-
'card-foreground': '#09090b',
|
|
2142
|
-
}
|
|
2143
|
-
```
|
|
2144
|
-
|
|
2145
|
-
### Using Semantic Colors
|
|
2146
|
-
|
|
2147
|
-
```tsx
|
|
2148
|
-
export default function SemanticCard({ title, description, price, tw }) {
|
|
2149
|
-
return (
|
|
2150
|
-
<div style={tw('bg-card border border-border rounded-lg p-6')}>
|
|
2151
|
-
<h2 style={tw('text-card-foreground text-2xl font-bold mb-2')}>
|
|
2152
|
-
{title}
|
|
2153
|
-
</h2>
|
|
2154
|
-
<p style={tw('text-muted-foreground mb-4')}>
|
|
2155
|
-
{description}
|
|
2156
|
-
</p>
|
|
2157
|
-
<div style={tw('text-primary text-3xl font-bold')}>
|
|
2158
|
-
${price}
|
|
2159
|
-
</div>
|
|
2160
|
-
</div>
|
|
2161
|
-
);
|
|
2162
|
-
}
|
|
2163
|
-
```
|
|
2164
|
-
|
|
2165
|
-
### Opacity Modifiers
|
|
2166
|
-
|
|
2167
|
-
Use Tailwind's slash syntax for opacity with any color:
|
|
2168
|
-
|
|
2169
|
-
```tsx
|
|
2170
|
-
export default function OpacityExample({ tw }) {
|
|
2171
|
-
return (
|
|
2172
|
-
<div style={tw('bg-primary/50')}> {/* 50% opacity */}
|
|
2173
|
-
<p style={tw('text-muted-foreground/75')}> {/* 75% opacity */}
|
|
2174
|
-
Subtle text
|
|
2175
|
-
</p>
|
|
2176
|
-
<div style={tw('border border-border/30')}> {/* 30% opacity */}
|
|
2177
|
-
Faint border
|
|
2178
|
-
</div>
|
|
2179
|
-
</div>
|
|
2180
|
-
);
|
|
2181
|
-
}
|
|
2182
|
-
```
|
|
2183
|
-
|
|
2184
|
-
**Supported syntax:**
|
|
2185
|
-
- `bg-{color}/{opacity}` - Background with opacity
|
|
2186
|
-
- `text-{color}/{opacity}` - Text with opacity
|
|
2187
|
-
- `border-{color}/{opacity}` - Border with opacity
|
|
2188
|
-
|
|
2189
|
-
### Text Hierarchy
|
|
2190
|
-
|
|
2191
|
-
```tsx
|
|
2192
|
-
// Primary text
|
|
2193
|
-
tw('text-foreground')
|
|
2194
|
-
|
|
2195
|
-
// Secondary/muted text
|
|
2196
|
-
tw('text-muted-foreground')
|
|
2197
|
-
|
|
2198
|
-
// Accent/brand text
|
|
2199
|
-
tw('text-primary')
|
|
2200
|
-
|
|
2201
|
-
// Destructive/error text
|
|
2202
|
-
tw('text-destructive')
|
|
2203
|
-
```
|
|
2204
|
-
|
|
2205
|
-
### Backgrounds
|
|
2206
|
-
|
|
2207
|
-
```tsx
|
|
2208
|
-
// Page background
|
|
2209
|
-
tw('bg-background')
|
|
2210
|
-
|
|
2211
|
-
// Card/elevated surfaces
|
|
2212
|
-
tw('bg-card')
|
|
2213
|
-
|
|
2214
|
-
// Subtle backgrounds
|
|
2215
|
-
tw('bg-muted')
|
|
2216
|
-
|
|
2217
|
-
// Accent backgrounds
|
|
2218
|
-
tw('bg-accent')
|
|
2219
|
-
```
|
|
2220
|
-
|
|
2221
|
-
## Supported Tailwind Classes
|
|
2222
|
-
|
|
2223
|
-
### Layout
|
|
2224
|
-
|
|
2225
|
-
- **Display**: `flex`, `inline-flex`, `block`, `inline-block`, `hidden`
|
|
2226
|
-
- **Flex Direction**: `flex-row`, `flex-col`, `flex-row-reverse`, `flex-col-reverse`
|
|
2227
|
-
- **Justify**: `justify-start`, `justify-end`, `justify-center`, `justify-between`, `justify-around`
|
|
2228
|
-
- **Align**: `items-start`, `items-end`, `items-center`, `items-baseline`, `items-stretch`
|
|
2229
|
-
|
|
2230
|
-
### Spacing
|
|
2231
|
-
|
|
2232
|
-
- **Padding**: `p-{n}`, `px-{n}`, `py-{n}`, `pt-{n}`, `pb-{n}`, `pl-{n}`, `pr-{n}`
|
|
2233
|
-
- **Margin**: `m-{n}`, `mx-{n}`, `my-{n}`, `mt-{n}`, `mb-{n}`, `ml-{n}`, `mr-{n}`
|
|
2234
|
-
- **Gap**: `gap-{n}`, `gap-x-{n}`, `gap-y-{n}`
|
|
2235
|
-
- **Sizes**: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 56, 64
|
|
2236
|
-
|
|
2237
|
-
Examples:
|
|
2238
|
-
```tsx
|
|
2239
|
-
tw('p-4') // padding: 1rem
|
|
2240
|
-
tw('px-8') // paddingLeft: 2rem, paddingRight: 2rem
|
|
2241
|
-
tw('m-6') // margin: 1.5rem
|
|
2242
|
-
tw('gap-4') // gap: 1rem
|
|
2243
|
-
```
|
|
2244
|
-
|
|
2245
|
-
### Sizing
|
|
2246
|
-
|
|
2247
|
-
- **Width**: `w-{n}`, `w-full`, `w-screen`, `w-1/2`, `w-1/3`, `w-2/3`
|
|
2248
|
-
- **Height**: `h-{n}`, `h-full`, `h-screen`
|
|
2249
|
-
|
|
2250
|
-
Examples:
|
|
2251
|
-
```tsx
|
|
2252
|
-
tw('w-full') // width: 100%
|
|
2253
|
-
tw('h-64') // height: 16rem
|
|
2254
|
-
tw('w-1/2') // width: 50%
|
|
2255
|
-
```
|
|
2256
|
-
|
|
2257
|
-
### Typography
|
|
2258
|
-
|
|
2259
|
-
- **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`
|
|
2260
|
-
- **Font Weight**: `font-thin`, `font-light`, `font-normal`, `font-medium`, `font-semibold`, `font-bold`, `font-extrabold`, `font-black`
|
|
2261
|
-
- **Text Align**: `text-left`, `text-center`, `text-right`
|
|
2262
|
-
- **Line Height**: `leading-none`, `leading-tight`, `leading-normal`, `leading-relaxed`, `leading-loose`
|
|
2263
|
-
|
|
2264
|
-
### Colors
|
|
2265
|
-
|
|
2266
|
-
All standard Tailwind colors plus shadcn semantic colors:
|
|
2267
|
-
|
|
2268
|
-
**Standard colors:**
|
|
2269
|
-
- `text-{color}-{shade}`, `bg-{color}-{shade}`, `border-{color}-{shade}`
|
|
2270
|
-
- Colors: `red`, `blue`, `green`, `yellow`, `purple`, `pink`, `gray`, `indigo`, `teal`, `orange`
|
|
2271
|
-
- Shades: `50`, `100`, `200`, `300`, `400`, `500`, `600`, `700`, `800`, `900`
|
|
2272
|
-
|
|
2273
|
-
**shadcn semantic colors:**
|
|
2274
|
-
- `text-foreground`, `text-primary`, `text-muted-foreground`, `text-destructive`
|
|
2275
|
-
- `bg-background`, `bg-card`, `bg-muted`, `bg-accent`, `bg-primary`
|
|
2276
|
-
- `border-border`, `border-input`
|
|
2277
|
-
|
|
2278
|
-
```tsx
|
|
2279
|
-
tw('text-blue-500') // Standard Tailwind color
|
|
2280
|
-
tw('bg-purple-600') // Standard Tailwind color
|
|
2281
|
-
tw('text-primary') // shadcn semantic color
|
|
2282
|
-
tw('bg-card') // shadcn semantic color
|
|
2283
|
-
```
|
|
2284
|
-
|
|
2285
|
-
### Position & Layout
|
|
2286
|
-
|
|
2287
|
-
- **Position**: `relative`, `absolute`, `fixed`, `sticky`
|
|
2288
|
-
- **Inset**: `inset-0`, `top-0`, `bottom-0`, `left-0`, `right-0`
|
|
2289
|
-
- **Z-Index**: `z-0`, `z-10`, `z-20`, `z-30`, `z-40`, `z-50`
|
|
2290
|
-
|
|
2291
|
-
### Borders
|
|
2292
|
-
|
|
2293
|
-
- **Border Width**: `border`, `border-{n}`, `border-t`, `border-b`, `border-l`, `border-r`
|
|
2294
|
-
- **Border Radius**: `rounded`, `rounded-sm`, `rounded-md`, `rounded-lg`, `rounded-xl`, `rounded-2xl`, `rounded-3xl`, `rounded-full`
|
|
2295
|
-
- **Border Color**: `border-{color}-{shade}`, `border-border`, `border-input`
|
|
2296
|
-
|
|
2297
|
-
### Effects
|
|
2298
|
-
|
|
2299
|
-
- **Shadow**: `shadow-sm`, `shadow`, `shadow-md`, `shadow-lg`, `shadow-xl`, `shadow-2xl`
|
|
2300
|
-
- **Opacity**: `opacity-0`, `opacity-25`, `opacity-50`, `opacity-75`, `opacity-100`
|
|
2301
|
-
|
|
2302
|
-
### Filters
|
|
2303
|
-
|
|
2304
|
-
- **Blur**: `blur-none`, `blur-sm`, `blur`, `blur-md`, `blur-lg`, `blur-xl`
|
|
2305
|
-
- **Brightness**: `brightness-0`, `brightness-50`, `brightness-100`, `brightness-150`, `brightness-200`
|
|
2306
|
-
- **Contrast**: `contrast-0`, `contrast-50`, `contrast-100`, `contrast-150`, `contrast-200`
|
|
2307
|
-
|
|
2308
|
-
## Gradients
|
|
2309
|
-
|
|
2310
|
-
### Linear Gradients
|
|
2311
|
-
|
|
2312
|
-
```tsx
|
|
2313
|
-
// Gradient direction
|
|
2314
|
-
tw('bg-gradient-to-r') // left to right
|
|
2315
|
-
tw('bg-gradient-to-br') // top-left to bottom-right
|
|
2316
|
-
tw('bg-gradient-to-t') // bottom to top
|
|
2317
|
-
|
|
2318
|
-
// Gradient colors
|
|
2319
|
-
tw('from-blue-500') // Start color
|
|
2320
|
-
tw('via-purple-500') // Middle color
|
|
2321
|
-
tw('to-pink-500') // End color
|
|
2322
|
-
|
|
2323
|
-
// Complete gradient
|
|
2324
|
-
tw('bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500')
|
|
2325
|
-
```
|
|
2326
|
-
|
|
2327
|
-
### Gradient Examples
|
|
2328
|
-
|
|
2329
|
-
```tsx
|
|
2330
|
-
export default function GradientCard({ title, tw }) {
|
|
2331
|
-
return (
|
|
2332
|
-
<div style={tw('w-full h-full bg-gradient-to-br from-cyan-500 to-blue-600 p-12')}>
|
|
2333
|
-
<h1 style={tw('text-white text-6xl font-bold')}>
|
|
2334
|
-
{title}
|
|
2335
|
-
</h1>
|
|
2336
|
-
</div>
|
|
2337
|
-
);
|
|
2338
|
-
}
|
|
2339
|
-
```
|
|
2340
|
-
|
|
2341
|
-
## Custom Theme Colors
|
|
2342
|
-
|
|
2343
|
-
You can override the default shadcn colors or add your own custom colors in `_loopwind/loopwind.json`:
|
|
2344
|
-
|
|
2345
|
-
```json title="_loopwind/loopwind.json"
|
|
2346
|
-
{
|
|
2347
|
-
"theme": {
|
|
2348
|
-
"colors": {
|
|
2349
|
-
"primary": "#3b82f6",
|
|
2350
|
-
"primary-foreground": "#ffffff",
|
|
2351
|
-
"accent": "#10b981",
|
|
2352
|
-
"brand": "#ff6b6b"
|
|
2353
|
-
}
|
|
2354
|
-
}
|
|
2355
|
-
}
|
|
2356
|
-
```
|
|
2357
|
-
|
|
2358
|
-
Then use these custom colors in your templates:
|
|
2359
|
-
|
|
2360
|
-
```tsx
|
|
2361
|
-
tw('text-brand') // Uses your custom brand color
|
|
2362
|
-
tw('bg-primary') // Uses your custom primary color
|
|
2363
|
-
tw('bg-accent') // Uses your custom accent color
|
|
2364
|
-
```
|
|
2365
|
-
|
|
2366
|
-
## Auto-Detection from tailwind.config.js
|
|
2367
|
-
|
|
2368
|
-
loopwind automatically detects and loads your project's Tailwind configuration:
|
|
2369
|
-
|
|
2370
|
-
```
|
|
2371
|
-
your-project/
|
|
2372
|
-
├── tailwind.config.js ← Automatically detected
|
|
2373
|
-
└── _loopwind/
|
|
2374
|
-
├── loopwind.json
|
|
2375
|
-
└── templates/
|
|
2376
|
-
```
|
|
2377
|
-
|
|
2378
|
-
This includes:
|
|
2379
|
-
- Custom colors
|
|
2380
|
-
- Custom spacing values
|
|
2381
|
-
- Custom fonts
|
|
2382
|
-
- Theme extensions
|
|
2383
|
-
- Custom utilities
|
|
2384
|
-
|
|
2385
|
-
## Complete Example
|
|
2386
|
-
|
|
2387
|
-
```tsx
|
|
2388
|
-
export default function ModernCard({
|
|
2389
|
-
tw,
|
|
2390
|
-
image,
|
|
2391
|
-
title,
|
|
2392
|
-
description,
|
|
2393
|
-
category,
|
|
2394
|
-
author,
|
|
2395
|
-
avatar
|
|
2396
|
-
}) {
|
|
2397
|
-
return (
|
|
2398
|
-
<div style={tw('w-full h-full bg-card')}>
|
|
2399
|
-
{/* Hero image */}
|
|
2400
|
-
<div style={tw('relative h-2/3')}>
|
|
2401
|
-
<img
|
|
2402
|
-
src={image(hero)}
|
|
2403
|
-
style={tw('w-full h-full object-cover')}
|
|
2404
|
-
/>
|
|
2405
|
-
{/* Category badge */}
|
|
2406
|
-
<div style={tw('absolute top-4 left-4 bg-primary/90 backdrop-blur px-4 py-2 rounded-full')}>
|
|
2407
|
-
<span style={tw('text-sm font-semibold text-primary-foreground')}>
|
|
2408
|
-
{category}
|
|
2409
|
-
</span>
|
|
2410
|
-
</div>
|
|
2411
|
-
</div>
|
|
2412
|
-
|
|
2413
|
-
{/* Content */}
|
|
2414
|
-
<div style={tw('h-1/3 p-8 flex flex-col justify-between')}>
|
|
2415
|
-
<div>
|
|
2416
|
-
<h2 style={tw('text-3xl font-bold text-foreground mb-2')}>
|
|
2417
|
-
{title}
|
|
2418
|
-
</h2>
|
|
2419
|
-
<p style={tw('text-muted-foreground line-clamp-2')}>
|
|
2420
|
-
{description}
|
|
2421
|
-
</p>
|
|
2422
|
-
</div>
|
|
2423
|
-
|
|
2424
|
-
{/* Author */}
|
|
2425
|
-
<div style={tw('flex items-center gap-3')}>
|
|
2426
|
-
<img
|
|
2427
|
-
src={image(avatar)}
|
|
2428
|
-
style={tw('w-10 h-10 rounded-full border-2 border-border')}
|
|
2429
|
-
/>
|
|
2430
|
-
<span style={tw('text-sm text-muted-foreground')}>
|
|
2431
|
-
{author}
|
|
2432
|
-
</span>
|
|
2433
|
-
</div>
|
|
2434
|
-
</div>
|
|
2435
|
-
</div>
|
|
2436
|
-
);
|
|
2437
|
-
}
|
|
2438
|
-
```
|
|
2439
|
-
|
|
2440
|
-
## Why This Approach?
|
|
2441
|
-
|
|
2442
|
-
- **Semantic naming**: `text-primary` instead of `text-blue-600`
|
|
2443
|
-
- **Consistency**: All templates use the same design language
|
|
2444
|
-
- **Flexibility**: Easy to customize entire theme
|
|
2445
|
-
- **Accessibility**: Pre-tested color contrasts
|
|
2446
|
-
- **Modern**: Same system as shadcn/ui components
|
|
2447
|
-
- **Familiar**: Standard Tailwind syntax
|
|
2448
|
-
|
|
2449
|
-
## Next Steps
|
|
2450
|
-
|
|
2451
|
-
- [Custom Fonts](/fonts)
|
|
2452
|
-
- [Built-in Helpers](/helpers)
|
|
2453
|
-
- [Image Rendering](/images)
|
|
2454
|
-
- [Video Rendering](/video)
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
================================================================================
|
|
2461
|
-
FILE: fonts.mdx
|
|
2462
|
-
================================================================================
|
|
2463
|
-
|
|
2464
|
-
# Font Handling in loopwind
|
|
2465
|
-
|
|
2466
|
-
The recommended way to use fonts is through `loopwind.json` - configure fonts once, use everywhere.
|
|
2467
|
-
|
|
2468
|
-
## Using Fonts from loopwind.json (Recommended)
|
|
2469
|
-
|
|
2470
|
-
Configure fonts in your `_loopwind/loopwind.json` and use Tailwind classes in templates.
|
|
2471
|
-
|
|
2472
|
-
### Simple Setup
|
|
2473
|
-
|
|
2474
|
-
Define font families in `_loopwind/loopwind.json` without loading custom fonts (uses system fonts):
|
|
2475
|
-
|
|
2476
|
-
```json title="_loopwind/loopwind.json"
|
|
2477
|
-
{
|
|
2478
|
-
"fonts": {
|
|
2479
|
-
"sans": ["Inter", "system-ui", "-apple-system", "sans-serif"],
|
|
2480
|
-
"serif": ["Georgia", "serif"],
|
|
2481
|
-
"mono": ["Courier New", "monospace"]
|
|
2482
|
-
}
|
|
2483
|
-
}
|
|
2484
|
-
```
|
|
2485
|
-
|
|
2486
|
-
**Template usage:**
|
|
2487
|
-
```tsx
|
|
2488
|
-
export default function({ title, tw }) {
|
|
2489
|
-
return (
|
|
2490
|
-
<div style={tw('w-full h-full')}>
|
|
2491
|
-
{/* Uses fonts.sans from loopwind.json */}
|
|
2492
|
-
<h1 style={tw('font-sans text-6xl font-bold')}>
|
|
2493
|
-
{title}
|
|
2494
|
-
</h1>
|
|
2495
|
-
|
|
2496
|
-
{/* Uses fonts.mono from loopwind.json */}
|
|
2497
|
-
<code style={tw('font-mono text-sm')}>
|
|
2498
|
-
{code}
|
|
2499
|
-
</code>
|
|
2500
|
-
</div>
|
|
2501
|
-
);
|
|
2502
|
-
}
|
|
2503
|
-
```
|
|
2504
|
-
|
|
2505
|
-
**Result:** Uses system fonts, falls back to Inter for rendering.
|
|
2506
|
-
|
|
2507
|
-
### Complete Setup (With Font Files)
|
|
2508
|
-
|
|
2509
|
-
Load custom font files for brand-specific typography in `_loopwind/loopwind.json`:
|
|
2510
|
-
|
|
2511
|
-
```json title="_loopwind/loopwind.json"
|
|
2512
|
-
{
|
|
2513
|
-
"fonts": {
|
|
2514
|
-
"sans": {
|
|
2515
|
-
"family": ["Inter", "system-ui", "sans-serif"],
|
|
2516
|
-
"files": [
|
|
2517
|
-
{ "path": "./fonts/Inter-Regular.woff", "weight": 400 },
|
|
2518
|
-
{ "path": "./fonts/Inter-Bold.woff", "weight": 700 }
|
|
2519
|
-
]
|
|
2520
|
-
},
|
|
2521
|
-
"mono": {
|
|
2522
|
-
"family": ["JetBrains Mono", "monospace"],
|
|
2523
|
-
"files": [
|
|
2524
|
-
{ "path": "./fonts/JetBrainsMono-Regular.woff", "weight": 400 }
|
|
2525
|
-
]
|
|
2526
|
-
}
|
|
2527
|
-
}
|
|
2528
|
-
}
|
|
2529
|
-
```
|
|
2530
|
-
|
|
2531
|
-
**Project structure:**
|
|
2532
|
-
```
|
|
2533
|
-
your-project/
|
|
2534
|
-
├── _loopwind/
|
|
2535
|
-
│ ├── loopwind.json
|
|
2536
|
-
│ └── templates/
|
|
2537
|
-
└── fonts/
|
|
2538
|
-
├── Inter-Regular.woff
|
|
2539
|
-
├── Inter-Bold.woff
|
|
2540
|
-
└── JetBrainsMono-Regular.woff
|
|
2541
|
-
```
|
|
2542
|
-
|
|
2543
|
-
**Template usage (same as before):**
|
|
2544
|
-
```tsx
|
|
2545
|
-
<h1 style={tw('font-sans font-bold')}>
|
|
2546
|
-
{/* Uses Inter Bold from loopwind.json */}
|
|
2547
|
-
{title}
|
|
2548
|
-
</h1>
|
|
2549
|
-
```
|
|
2550
|
-
|
|
2551
|
-
**Available classes:**
|
|
2552
|
-
- `font-sans` - Uses `fonts.sans` from loopwind.json
|
|
2553
|
-
- `font-serif` - Uses `fonts.serif` from loopwind.json
|
|
2554
|
-
- `font-mono` - Uses `fonts.mono` from loopwind.json
|
|
2555
|
-
|
|
2556
|
-
**Supported formats:**
|
|
2557
|
-
- ✅ **WOFF** (`.woff`) - Recommended for best compatibility
|
|
2558
|
-
- ✅ **TTF** (`.ttf`) - Also supported
|
|
2559
|
-
- ✅ **OTF** (`.otf`) - Also supported
|
|
2560
|
-
- ❌ **WOFF2** (`.woff2`) - Not supported by renderer
|
|
2561
|
-
|
|
2562
|
-
## Font Loading Priority
|
|
2563
|
-
|
|
2564
|
-
loopwind loads fonts in this order:
|
|
2565
|
-
|
|
2566
|
-
1. **loopwind.json fonts** (if configured with `files`)
|
|
2567
|
-
2. **Bundled Inter fonts** (included with CLI)
|
|
2568
|
-
|
|
2569
|
-
This ensures fonts work out of the box with no configuration.
|
|
2570
|
-
|
|
2571
|
-
## Default Fonts
|
|
2572
|
-
|
|
2573
|
-
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.
|
|
2574
|
-
|
|
2575
|
-
## Best Practices
|
|
2576
|
-
|
|
2577
|
-
1. ✅ **Use loopwind.json for project-wide fonts** - Configure once, use everywhere
|
|
2578
|
-
2. ✅ **Use font classes** - `tw('font-sans')` instead of `fontFamily: 'Inter'`
|
|
2579
|
-
3. ✅ **Include fallbacks** - Always add system fonts: `["Inter", "system-ui", "sans-serif"]`
|
|
2580
|
-
4. ✅ **Match names** - First font in `family` array is used as the loaded font name
|
|
2581
|
-
5. ✅ **Relative paths** - Font paths are relative to `loopwind.json` location
|
|
2582
|
-
|
|
2583
|
-
## Examples
|
|
2584
|
-
|
|
2585
|
-
### Minimal Setup (System Fonts)
|
|
2586
|
-
```json title="_loopwind/loopwind.json"
|
|
2587
|
-
{
|
|
2588
|
-
"fonts": {
|
|
2589
|
-
"sans": ["Inter", "-apple-system", "sans-serif"]
|
|
2590
|
-
}
|
|
2591
|
-
}
|
|
2592
|
-
```
|
|
2593
|
-
Uses system Inter if available, falls back to Noto Sans for rendering.
|
|
2594
|
-
|
|
2595
|
-
### Brand Fonts Setup
|
|
2596
|
-
```json title="_loopwind/loopwind.json"
|
|
2597
|
-
{
|
|
2598
|
-
"fonts": {
|
|
2599
|
-
"sans": {
|
|
2600
|
-
"family": ["Montserrat", "sans-serif"],
|
|
2601
|
-
"files": [
|
|
2602
|
-
{ "path": "./fonts/Montserrat-Regular.woff", "weight": 400 },
|
|
2603
|
-
{ "path": "./fonts/Montserrat-Bold.woff", "weight": 700 }
|
|
2604
|
-
]
|
|
2605
|
-
}
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
```
|
|
2609
|
-
Loads and uses Montserrat for all templates.
|
|
2610
|
-
|
|
2611
|
-
### Multi-Font Setup
|
|
2612
|
-
```json title="_loopwind/loopwind.json"
|
|
2613
|
-
{
|
|
2614
|
-
"fonts": {
|
|
2615
|
-
"sans": {
|
|
2616
|
-
"family": ["Inter", "sans-serif"],
|
|
2617
|
-
"files": [
|
|
2618
|
-
{ "path": "./fonts/Inter-Regular.woff", "weight": 400 },
|
|
2619
|
-
{ "path": "./fonts/Inter-Bold.woff", "weight": 700 }
|
|
2620
|
-
]
|
|
2621
|
-
},
|
|
2622
|
-
"serif": {
|
|
2623
|
-
"family": ["Playfair Display", "serif"],
|
|
2624
|
-
"files": [
|
|
2625
|
-
{ "path": "./fonts/Playfair-Regular.woff", "weight": 400 }
|
|
2626
|
-
]
|
|
2627
|
-
},
|
|
2628
|
-
"mono": {
|
|
2629
|
-
"family": ["Fira Code", "monospace"],
|
|
2630
|
-
"files": [
|
|
2631
|
-
{ "path": "./fonts/FiraCode-Regular.woff", "weight": 400 }
|
|
2632
|
-
]
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
}
|
|
2636
|
-
```
|
|
2637
|
-
Loads different fonts for each style class.
|
|
2638
|
-
|
|
2639
|
-
### External Font URLs
|
|
2640
|
-
Load fonts directly from CDNs without downloading files:
|
|
2641
|
-
|
|
2642
|
-
```json title="_loopwind/loopwind.json"
|
|
2643
|
-
{
|
|
2644
|
-
"fonts": {
|
|
2645
|
-
"sans": {
|
|
2646
|
-
"family": ["Inter", "sans-serif"],
|
|
2647
|
-
"files": [
|
|
2648
|
-
{
|
|
2649
|
-
"path": "https://unpkg.com/@fontsource/inter@5.0.18/files/inter-latin-400-normal.woff",
|
|
2650
|
-
"weight": 400
|
|
2651
|
-
},
|
|
2652
|
-
{
|
|
2653
|
-
"path": "https://unpkg.com/@fontsource/inter@5.0.18/files/inter-latin-700-normal.woff",
|
|
2654
|
-
"weight": 700
|
|
2655
|
-
}
|
|
2656
|
-
]
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
}
|
|
2660
|
-
```
|
|
2661
|
-
|
|
2662
|
-
You can also mix local and external fonts:
|
|
2663
|
-
|
|
2664
|
-
```json title="_loopwind/loopwind.json"
|
|
2665
|
-
{
|
|
2666
|
-
"fonts": {
|
|
2667
|
-
"sans": {
|
|
2668
|
-
"family": ["Inter", "sans-serif"],
|
|
2669
|
-
"files": [
|
|
2670
|
-
{ "path": "./fonts/Inter-Regular.woff", "weight": 400 },
|
|
2671
|
-
{
|
|
2672
|
-
"path": "https://unpkg.com/@fontsource/inter@5.0.18/files/inter-latin-700-normal.woff",
|
|
2673
|
-
"weight": 700
|
|
2674
|
-
}
|
|
2675
|
-
]
|
|
2676
|
-
}
|
|
2677
|
-
}
|
|
2678
|
-
}
|
|
2679
|
-
```
|
|
2680
|
-
|
|
2681
|
-
**Note:** Use WOFF format (`.woff`) for best compatibility. WOFF2 is not supported by the underlying renderer.
|
|
2682
|
-
|
|
2683
|
-
## Performance
|
|
2684
|
-
|
|
2685
|
-
- ✅ **Font caching** - Fonts load once and are cached for all renders
|
|
2686
|
-
- ✅ **Video optimization** - 90-frame video loads fonts once, not 90 times
|
|
2687
|
-
- ✅ **No CDN delays** - Local fonts load instantly
|
|
2688
|
-
|
|
2689
|
-
## Next Steps
|
|
2690
|
-
|
|
2691
|
-
- [Styling with Tailwind & shadcn/ui](/styling)
|
|
2692
|
-
- [Built-in Helpers](/helpers)
|
|
2693
|
-
- [Image Rendering](/images)
|
|
2694
|
-
- [Video Rendering](/video)
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
================================================================================
|
|
2700
|
-
FILE: agents.mdx
|
|
2701
|
-
================================================================================
|
|
2702
|
-
|
|
2703
|
-
# AI Agents
|
|
2704
|
-
|
|
2705
|
-
loopwind is designed to work with AI coding assistants. Simply add the `_loopwind` folder to context.
|
|
2706
|
-
|
|
2707
|
-
## Using with AI Code Agents
|
|
2708
|
-
|
|
2709
|
-
In Cursor or Claude Code (Cline), type `@_loopwind` to add the folder to context, then ask:
|
|
2710
|
-
|
|
2711
|
-
```
|
|
2712
|
-
Generate an Open Graph image for my blog post about React hooks
|
|
2713
|
-
```
|
|
2714
|
-
|
|
2715
|
-
The AI will read the templates and generate the appropriate command.
|
|
2716
|
-
|
|
2717
|
-
## The AGENTS.md File
|
|
2718
|
-
|
|
2719
|
-
When you run `loopwind init`, a `_loopwind/AGENTS.md` file is created with:
|
|
2720
|
-
- Essential commands
|
|
2721
|
-
- Template helpers reference
|
|
2722
|
-
- Common workflows
|
|
2723
|
-
|
|
2724
|
-
This gives AI agents everything they need to use loopwind effectively.
|
|
2725
|
-
|
|
2726
|
-
## Example Workflows
|
|
2727
|
-
|
|
2728
|
-
### Simple Image Generation
|
|
2729
|
-
|
|
2730
|
-
**Prompt:**
|
|
2731
|
-
```
|
|
2732
|
-
Create a social media card using the og-image template:
|
|
2733
|
-
- Title: "10 React Tips"
|
|
2734
|
-
- Description: "Learn advanced patterns"
|
|
2735
|
-
```
|
|
2736
|
-
|
|
2737
|
-
**AI generates:**
|
|
2738
|
-
```bash
|
|
2739
|
-
loopwind render og-image '{"title":"10 React Tips","description":"Learn advanced patterns"}'
|
|
2740
|
-
```
|
|
2741
|
-
|
|
2742
|
-
**Output:** `_loopwind/outputs/og-image.png`
|
|
2743
|
-
|
|
2744
|
-
### Changelog Image
|
|
2745
|
-
|
|
2746
|
-
**Prompt:**
|
|
2747
|
-
```
|
|
2748
|
-
Create a changelog image for 3 recent changes:
|
|
2749
|
-
- Added video rendering support
|
|
2750
|
-
- Improved template validation
|
|
2751
|
-
- Fixed font loading issues
|
|
2752
|
-
```
|
|
2753
|
-
|
|
2754
|
-
**AI generates:**
|
|
2755
|
-
```bash
|
|
2756
|
-
loopwind render changelog-card '{
|
|
2757
|
-
"title":"Version 1.2.0",
|
|
2758
|
-
"changes":[
|
|
2759
|
-
"Added video rendering support",
|
|
2760
|
-
"Improved template validation",
|
|
2761
|
-
"Fixed font loading issues"
|
|
2762
|
-
]
|
|
2763
|
-
}'
|
|
2764
|
-
```
|
|
2765
|
-
|
|
2766
|
-
**Output:** `_loopwind/outputs/changelog-card.png`
|
|
2767
|
-
|
|
2768
|
-
## Next Steps
|
|
2769
|
-
|
|
2770
|
-
- [Learn about Templates](/templates)
|
|
2771
|
-
- [Image Rendering](/images)
|
|
2772
|
-
- [Video Rendering](/video)
|
|
2773
|
-
- [Built-in Helpers](/helpers)
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|