@nerdev/skillset 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (567) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +351 -0
  3. package/bin/skillset.mjs +205 -0
  4. package/curations/README.md +53 -0
  5. package/curations/TEMPLATE/WHY.md +23 -0
  6. package/curations/TEMPLATE/overlay/SKILL.md +6 -0
  7. package/curations/impeccable/WHY.md +34 -0
  8. package/curations/impeccable/overlay/SKILL.md +116 -0
  9. package/package.json +43 -0
  10. package/scripts/ste-gate.mjs +39 -0
  11. package/scripts/ste-lint.py +201 -0
  12. package/scripts/sync.mjs +174 -0
  13. package/skills/ai-agent/agent-docs-writer/SKILL.md +105 -0
  14. package/skills/ai-agent/agent-docs-writer/references/principles.md +73 -0
  15. package/skills/ai-agent/agent-docs-writer/templates/AGENTS.template.md +44 -0
  16. package/skills/ai-agent/agent-docs-writer/templates/README.template.md +19 -0
  17. package/skills/ai-agent/ultra-context-engine/SKILL.md +941 -0
  18. package/skills/architecture/blueprint/SKILL.md +191 -0
  19. package/skills/architecture/code-tour/SKILL.md +204 -0
  20. package/skills/architecture/progress-guard/SKILL.md +148 -0
  21. package/skills/architecture/search-first/SKILL.md +97 -0
  22. package/skills/backend/api-design/SKILL.md +456 -0
  23. package/skills/backend/auth/SKILL.md +596 -0
  24. package/skills/backend/database/SKILL.md +431 -0
  25. package/skills/backend/realtime/SKILL.md +670 -0
  26. package/skills/backend/security/SKILL.md +590 -0
  27. package/skills/business/brag/SKILL.md +177 -0
  28. package/skills/business/brag/assets/music/README.md +20 -0
  29. package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.json +3846 -0
  30. package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.md +35 -0
  31. package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.json +1826 -0
  32. package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.md +35 -0
  33. package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.json +2266 -0
  34. package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.md +35 -0
  35. package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.json +2806 -0
  36. package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.md +35 -0
  37. package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.json +2796 -0
  38. package/skills/business/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.md +35 -0
  39. package/skills/business/brag/assets/music/happy-beats-business-moves-vol-1-by-ende-dot-app.mp3 +0 -0
  40. package/skills/business/brag/assets/music/happy-beats-business-moves-vol-10-by-ende-dot-app.mp3 +0 -0
  41. package/skills/business/brag/assets/music/happy-beats-business-moves-vol-11-by-ende-dot-app.mp3 +0 -0
  42. package/skills/business/brag/assets/music/happy-beats-business-moves-vol-12-by-ende-dot-app.mp3 +0 -0
  43. package/skills/business/brag/assets/music/happy-beats-business-moves-vol-9-by-ende-dot-app.mp3 +0 -0
  44. package/skills/business/brag/assets/sfx/casino/card-fan-1.ogg +0 -0
  45. package/skills/business/brag/assets/sfx/casino/card-fan-2.ogg +0 -0
  46. package/skills/business/brag/assets/sfx/casino/card-place-1.ogg +0 -0
  47. package/skills/business/brag/assets/sfx/casino/card-place-2.ogg +0 -0
  48. package/skills/business/brag/assets/sfx/casino/card-place-3.ogg +0 -0
  49. package/skills/business/brag/assets/sfx/casino/card-place-4.ogg +0 -0
  50. package/skills/business/brag/assets/sfx/casino/card-shove-1.ogg +0 -0
  51. package/skills/business/brag/assets/sfx/casino/card-shove-2.ogg +0 -0
  52. package/skills/business/brag/assets/sfx/casino/card-shove-3.ogg +0 -0
  53. package/skills/business/brag/assets/sfx/casino/card-shove-4.ogg +0 -0
  54. package/skills/business/brag/assets/sfx/casino/card-shuffle.ogg +0 -0
  55. package/skills/business/brag/assets/sfx/casino/card-slide-1.ogg +0 -0
  56. package/skills/business/brag/assets/sfx/casino/card-slide-2.ogg +0 -0
  57. package/skills/business/brag/assets/sfx/casino/card-slide-3.ogg +0 -0
  58. package/skills/business/brag/assets/sfx/casino/card-slide-4.ogg +0 -0
  59. package/skills/business/brag/assets/sfx/casino/card-slide-5.ogg +0 -0
  60. package/skills/business/brag/assets/sfx/casino/card-slide-6.ogg +0 -0
  61. package/skills/business/brag/assets/sfx/casino/card-slide-7.ogg +0 -0
  62. package/skills/business/brag/assets/sfx/casino/card-slide-8.ogg +0 -0
  63. package/skills/business/brag/assets/sfx/casino/cards-pack-open-1.ogg +0 -0
  64. package/skills/business/brag/assets/sfx/casino/cards-pack-open-2.ogg +0 -0
  65. package/skills/business/brag/assets/sfx/casino/chip-lay-1.ogg +0 -0
  66. package/skills/business/brag/assets/sfx/casino/chip-lay-2.ogg +0 -0
  67. package/skills/business/brag/assets/sfx/casino/chip-lay-3.ogg +0 -0
  68. package/skills/business/brag/assets/sfx/casino/chips-collide-1.ogg +0 -0
  69. package/skills/business/brag/assets/sfx/casino/chips-collide-2.ogg +0 -0
  70. package/skills/business/brag/assets/sfx/casino/chips-collide-3.ogg +0 -0
  71. package/skills/business/brag/assets/sfx/casino/chips-collide-4.ogg +0 -0
  72. package/skills/business/brag/assets/sfx/casino/chips-handle-1.ogg +0 -0
  73. package/skills/business/brag/assets/sfx/casino/chips-handle-2.ogg +0 -0
  74. package/skills/business/brag/assets/sfx/casino/chips-handle-3.ogg +0 -0
  75. package/skills/business/brag/assets/sfx/casino/chips-handle-4.ogg +0 -0
  76. package/skills/business/brag/assets/sfx/casino/chips-handle-6.ogg +0 -0
  77. package/skills/business/brag/assets/sfx/casino/chips-stack-1.ogg +0 -0
  78. package/skills/business/brag/assets/sfx/casino/chips-stack-2.ogg +0 -0
  79. package/skills/business/brag/assets/sfx/casino/chips-stack-3.ogg +0 -0
  80. package/skills/business/brag/assets/sfx/casino/chips-stack-4.ogg +0 -0
  81. package/skills/business/brag/assets/sfx/casino/chips-stack-5.ogg +0 -0
  82. package/skills/business/brag/assets/sfx/casino/chips-stack-6.ogg +0 -0
  83. package/skills/business/brag/assets/sfx/casino/dice-grab-1.ogg +0 -0
  84. package/skills/business/brag/assets/sfx/casino/dice-grab-2.ogg +0 -0
  85. package/skills/business/brag/assets/sfx/casino/dice-shake-1.ogg +0 -0
  86. package/skills/business/brag/assets/sfx/casino/dice-shake-2.ogg +0 -0
  87. package/skills/business/brag/assets/sfx/casino/dice-shake-3.ogg +0 -0
  88. package/skills/business/brag/assets/sfx/casino/dice-throw-1.ogg +0 -0
  89. package/skills/business/brag/assets/sfx/casino/dice-throw-2.ogg +0 -0
  90. package/skills/business/brag/assets/sfx/casino/dice-throw-3.ogg +0 -0
  91. package/skills/business/brag/assets/sfx/casino/die-throw-1.ogg +0 -0
  92. package/skills/business/brag/assets/sfx/casino/die-throw-2.ogg +0 -0
  93. package/skills/business/brag/assets/sfx/casino/die-throw-3.ogg +0 -0
  94. package/skills/business/brag/assets/sfx/casino/die-throw-4.ogg +0 -0
  95. package/skills/business/brag/assets/sfx/impact/footstep_carpet_000.ogg +0 -0
  96. package/skills/business/brag/assets/sfx/impact/footstep_carpet_003.ogg +0 -0
  97. package/skills/business/brag/assets/sfx/impact/footstep_carpet_004.ogg +0 -0
  98. package/skills/business/brag/assets/sfx/impact/footstep_concrete_000.ogg +0 -0
  99. package/skills/business/brag/assets/sfx/impact/footstep_concrete_001.ogg +0 -0
  100. package/skills/business/brag/assets/sfx/impact/footstep_concrete_002.ogg +0 -0
  101. package/skills/business/brag/assets/sfx/impact/footstep_concrete_003.ogg +0 -0
  102. package/skills/business/brag/assets/sfx/impact/footstep_concrete_004.ogg +0 -0
  103. package/skills/business/brag/assets/sfx/impact/footstep_grass_000.ogg +0 -0
  104. package/skills/business/brag/assets/sfx/impact/footstep_grass_001.ogg +0 -0
  105. package/skills/business/brag/assets/sfx/impact/footstep_grass_002.ogg +0 -0
  106. package/skills/business/brag/assets/sfx/impact/footstep_grass_003.ogg +0 -0
  107. package/skills/business/brag/assets/sfx/impact/footstep_grass_004.ogg +0 -0
  108. package/skills/business/brag/assets/sfx/impact/footstep_snow_000.ogg +0 -0
  109. package/skills/business/brag/assets/sfx/impact/footstep_snow_001.ogg +0 -0
  110. package/skills/business/brag/assets/sfx/impact/footstep_snow_002.ogg +0 -0
  111. package/skills/business/brag/assets/sfx/impact/footstep_snow_003.ogg +0 -0
  112. package/skills/business/brag/assets/sfx/impact/footstep_snow_004.ogg +0 -0
  113. package/skills/business/brag/assets/sfx/impact/footstep_wood_000.ogg +0 -0
  114. package/skills/business/brag/assets/sfx/impact/footstep_wood_001.ogg +0 -0
  115. package/skills/business/brag/assets/sfx/impact/footstep_wood_002.ogg +0 -0
  116. package/skills/business/brag/assets/sfx/impact/footstep_wood_003.ogg +0 -0
  117. package/skills/business/brag/assets/sfx/impact/footstep_wood_004.ogg +0 -0
  118. package/skills/business/brag/assets/sfx/impact/impactBell_heavy_000.ogg +0 -0
  119. package/skills/business/brag/assets/sfx/impact/impactBell_heavy_003.ogg +0 -0
  120. package/skills/business/brag/assets/sfx/impact/impactBell_heavy_004.ogg +0 -0
  121. package/skills/business/brag/assets/sfx/impact/impactGeneric_light_000.ogg +0 -0
  122. package/skills/business/brag/assets/sfx/impact/impactGeneric_light_001.ogg +0 -0
  123. package/skills/business/brag/assets/sfx/impact/impactGeneric_light_002.ogg +0 -0
  124. package/skills/business/brag/assets/sfx/impact/impactGeneric_light_003.ogg +0 -0
  125. package/skills/business/brag/assets/sfx/impact/impactGeneric_light_004.ogg +0 -0
  126. package/skills/business/brag/assets/sfx/impact/impactGlass_heavy_002.ogg +0 -0
  127. package/skills/business/brag/assets/sfx/impact/impactGlass_light_001.ogg +0 -0
  128. package/skills/business/brag/assets/sfx/impact/impactGlass_light_002.ogg +0 -0
  129. package/skills/business/brag/assets/sfx/impact/impactGlass_light_003.ogg +0 -0
  130. package/skills/business/brag/assets/sfx/impact/impactGlass_medium_000.ogg +0 -0
  131. package/skills/business/brag/assets/sfx/impact/impactGlass_medium_002.ogg +0 -0
  132. package/skills/business/brag/assets/sfx/impact/impactGlass_medium_004.ogg +0 -0
  133. package/skills/business/brag/assets/sfx/impact/impactMetal_heavy_000.ogg +0 -0
  134. package/skills/business/brag/assets/sfx/impact/impactMetal_heavy_002.ogg +0 -0
  135. package/skills/business/brag/assets/sfx/impact/impactMetal_heavy_004.ogg +0 -0
  136. package/skills/business/brag/assets/sfx/impact/impactMetal_light_002.ogg +0 -0
  137. package/skills/business/brag/assets/sfx/impact/impactMetal_light_003.ogg +0 -0
  138. package/skills/business/brag/assets/sfx/impact/impactMetal_medium_000.ogg +0 -0
  139. package/skills/business/brag/assets/sfx/impact/impactMetal_medium_001.ogg +0 -0
  140. package/skills/business/brag/assets/sfx/impact/impactMetal_medium_002.ogg +0 -0
  141. package/skills/business/brag/assets/sfx/impact/impactMetal_medium_003.ogg +0 -0
  142. package/skills/business/brag/assets/sfx/impact/impactMetal_medium_004.ogg +0 -0
  143. package/skills/business/brag/assets/sfx/impact/impactMining_001.ogg +0 -0
  144. package/skills/business/brag/assets/sfx/impact/impactPlank_medium_000.ogg +0 -0
  145. package/skills/business/brag/assets/sfx/impact/impactPlank_medium_001.ogg +0 -0
  146. package/skills/business/brag/assets/sfx/impact/impactPlank_medium_002.ogg +0 -0
  147. package/skills/business/brag/assets/sfx/impact/impactPlank_medium_003.ogg +0 -0
  148. package/skills/business/brag/assets/sfx/impact/impactPlank_medium_004.ogg +0 -0
  149. package/skills/business/brag/assets/sfx/impact/impactPlate_heavy_000.ogg +0 -0
  150. package/skills/business/brag/assets/sfx/impact/impactPlate_heavy_001.ogg +0 -0
  151. package/skills/business/brag/assets/sfx/impact/impactPlate_heavy_002.ogg +0 -0
  152. package/skills/business/brag/assets/sfx/impact/impactPlate_heavy_003.ogg +0 -0
  153. package/skills/business/brag/assets/sfx/impact/impactPlate_heavy_004.ogg +0 -0
  154. package/skills/business/brag/assets/sfx/impact/impactPlate_light_000.ogg +0 -0
  155. package/skills/business/brag/assets/sfx/impact/impactPlate_light_001.ogg +0 -0
  156. package/skills/business/brag/assets/sfx/impact/impactPlate_light_002.ogg +0 -0
  157. package/skills/business/brag/assets/sfx/impact/impactPlate_light_003.ogg +0 -0
  158. package/skills/business/brag/assets/sfx/impact/impactPlate_light_004.ogg +0 -0
  159. package/skills/business/brag/assets/sfx/impact/impactPlate_medium_000.ogg +0 -0
  160. package/skills/business/brag/assets/sfx/impact/impactPlate_medium_001.ogg +0 -0
  161. package/skills/business/brag/assets/sfx/impact/impactPlate_medium_002.ogg +0 -0
  162. package/skills/business/brag/assets/sfx/impact/impactPlate_medium_003.ogg +0 -0
  163. package/skills/business/brag/assets/sfx/impact/impactPlate_medium_004.ogg +0 -0
  164. package/skills/business/brag/assets/sfx/impact/impactPunch_heavy_000.ogg +0 -0
  165. package/skills/business/brag/assets/sfx/impact/impactPunch_heavy_001.ogg +0 -0
  166. package/skills/business/brag/assets/sfx/impact/impactPunch_heavy_002.ogg +0 -0
  167. package/skills/business/brag/assets/sfx/impact/impactPunch_heavy_003.ogg +0 -0
  168. package/skills/business/brag/assets/sfx/impact/impactPunch_heavy_004.ogg +0 -0
  169. package/skills/business/brag/assets/sfx/impact/impactPunch_medium_000.ogg +0 -0
  170. package/skills/business/brag/assets/sfx/impact/impactPunch_medium_001.ogg +0 -0
  171. package/skills/business/brag/assets/sfx/impact/impactPunch_medium_002.ogg +0 -0
  172. package/skills/business/brag/assets/sfx/impact/impactPunch_medium_003.ogg +0 -0
  173. package/skills/business/brag/assets/sfx/impact/impactPunch_medium_004.ogg +0 -0
  174. package/skills/business/brag/assets/sfx/impact/impactSoft_heavy_000.ogg +0 -0
  175. package/skills/business/brag/assets/sfx/impact/impactSoft_heavy_001.ogg +0 -0
  176. package/skills/business/brag/assets/sfx/impact/impactSoft_heavy_002.ogg +0 -0
  177. package/skills/business/brag/assets/sfx/impact/impactSoft_heavy_003.ogg +0 -0
  178. package/skills/business/brag/assets/sfx/impact/impactSoft_heavy_004.ogg +0 -0
  179. package/skills/business/brag/assets/sfx/impact/impactSoft_medium_000.ogg +0 -0
  180. package/skills/business/brag/assets/sfx/impact/impactSoft_medium_001.ogg +0 -0
  181. package/skills/business/brag/assets/sfx/impact/impactSoft_medium_002.ogg +0 -0
  182. package/skills/business/brag/assets/sfx/impact/impactSoft_medium_003.ogg +0 -0
  183. package/skills/business/brag/assets/sfx/impact/impactSoft_medium_004.ogg +0 -0
  184. package/skills/business/brag/assets/sfx/impact/impactTin_medium_000.ogg +0 -0
  185. package/skills/business/brag/assets/sfx/impact/impactTin_medium_001.ogg +0 -0
  186. package/skills/business/brag/assets/sfx/impact/impactTin_medium_002.ogg +0 -0
  187. package/skills/business/brag/assets/sfx/impact/impactTin_medium_003.ogg +0 -0
  188. package/skills/business/brag/assets/sfx/impact/impactTin_medium_004.ogg +0 -0
  189. package/skills/business/brag/assets/sfx/impact/impactWood_heavy_000.ogg +0 -0
  190. package/skills/business/brag/assets/sfx/impact/impactWood_heavy_001.ogg +0 -0
  191. package/skills/business/brag/assets/sfx/impact/impactWood_heavy_002.ogg +0 -0
  192. package/skills/business/brag/assets/sfx/impact/impactWood_heavy_003.ogg +0 -0
  193. package/skills/business/brag/assets/sfx/impact/impactWood_heavy_004.ogg +0 -0
  194. package/skills/business/brag/assets/sfx/impact/impactWood_light_000.ogg +0 -0
  195. package/skills/business/brag/assets/sfx/impact/impactWood_light_001.ogg +0 -0
  196. package/skills/business/brag/assets/sfx/impact/impactWood_light_002.ogg +0 -0
  197. package/skills/business/brag/assets/sfx/impact/impactWood_light_003.ogg +0 -0
  198. package/skills/business/brag/assets/sfx/impact/impactWood_light_004.ogg +0 -0
  199. package/skills/business/brag/assets/sfx/impact/impactWood_medium_000.ogg +0 -0
  200. package/skills/business/brag/assets/sfx/impact/impactWood_medium_001.ogg +0 -0
  201. package/skills/business/brag/assets/sfx/impact/impactWood_medium_002.ogg +0 -0
  202. package/skills/business/brag/assets/sfx/impact/impactWood_medium_003.ogg +0 -0
  203. package/skills/business/brag/assets/sfx/impact/impactWood_medium_004.ogg +0 -0
  204. package/skills/business/brag/assets/sfx/interface/bong_001.ogg +0 -0
  205. package/skills/business/brag/assets/sfx/interface/click_001.ogg +0 -0
  206. package/skills/business/brag/assets/sfx/interface/click_002.ogg +0 -0
  207. package/skills/business/brag/assets/sfx/interface/click_003.ogg +0 -0
  208. package/skills/business/brag/assets/sfx/interface/click_004.ogg +0 -0
  209. package/skills/business/brag/assets/sfx/interface/click_005.ogg +0 -0
  210. package/skills/business/brag/assets/sfx/interface/drop_001.ogg +0 -0
  211. package/skills/business/brag/assets/sfx/interface/drop_002.ogg +0 -0
  212. package/skills/business/brag/assets/sfx/interface/drop_003.ogg +0 -0
  213. package/skills/business/brag/assets/sfx/interface/error_005.ogg +0 -0
  214. package/skills/business/brag/assets/sfx/interface/error_006.ogg +0 -0
  215. package/skills/business/brag/assets/sfx/interface/glitch_002.ogg +0 -0
  216. package/skills/business/brag/assets/sfx/interface/glitch_004.ogg +0 -0
  217. package/skills/business/brag/assets/sfx/interface/select_008.ogg +0 -0
  218. package/skills/business/brag/assets/sfx/interface/switch_001.ogg +0 -0
  219. package/skills/business/brag/assets/sfx/interface/switch_002.ogg +0 -0
  220. package/skills/business/brag/assets/sfx/interface/switch_004.ogg +0 -0
  221. package/skills/business/brag/assets/sfx/interface/switch_005.ogg +0 -0
  222. package/skills/business/brag/assets/sfx/interface/switch_006.ogg +0 -0
  223. package/skills/business/brag/assets/sfx/interface/switch_007.ogg +0 -0
  224. package/skills/business/brag/assets/sfx/keyboard/keypress-001.wav +0 -0
  225. package/skills/business/brag/assets/sfx/keyboard/keypress-002.wav +0 -0
  226. package/skills/business/brag/assets/sfx/keyboard/keypress-003.wav +0 -0
  227. package/skills/business/brag/assets/sfx/keyboard/keypress-004.wav +0 -0
  228. package/skills/business/brag/assets/sfx/keyboard/keypress-005.wav +0 -0
  229. package/skills/business/brag/assets/sfx/keyboard/keypress-006.wav +0 -0
  230. package/skills/business/brag/assets/sfx/keyboard/keypress-007.wav +0 -0
  231. package/skills/business/brag/assets/sfx/keyboard/keypress-008.wav +0 -0
  232. package/skills/business/brag/assets/sfx/keyboard/keypress-009.wav +0 -0
  233. package/skills/business/brag/assets/sfx/keyboard/keypress-010.wav +0 -0
  234. package/skills/business/brag/assets/sfx/keyboard/keypress-011.wav +0 -0
  235. package/skills/business/brag/assets/sfx/keyboard/keypress-012.wav +0 -0
  236. package/skills/business/brag/assets/sfx/keyboard/keypress-013.wav +0 -0
  237. package/skills/business/brag/assets/sfx/keyboard/keypress-014.wav +0 -0
  238. package/skills/business/brag/assets/sfx/keyboard/keypress-015.wav +0 -0
  239. package/skills/business/brag/assets/sfx/keyboard/keypress-016.wav +0 -0
  240. package/skills/business/brag/assets/sfx/keyboard/keypress-017.wav +0 -0
  241. package/skills/business/brag/assets/sfx/keyboard/keypress-018.wav +0 -0
  242. package/skills/business/brag/assets/sfx/keyboard/keypress-019.wav +0 -0
  243. package/skills/business/brag/assets/sfx/keyboard/keypress-020.wav +0 -0
  244. package/skills/business/brag/assets/sfx/keyboard/keypress-021.wav +0 -0
  245. package/skills/business/brag/assets/sfx/keyboard/keypress-022.wav +0 -0
  246. package/skills/business/brag/assets/sfx/keyboard/keypress-023.wav +0 -0
  247. package/skills/business/brag/assets/sfx/keyboard/keypress-024.wav +0 -0
  248. package/skills/business/brag/assets/sfx/keyboard/keypress-025.wav +0 -0
  249. package/skills/business/brag/assets/sfx/keyboard/keypress-026.wav +0 -0
  250. package/skills/business/brag/assets/sfx/keyboard/keypress-027.wav +0 -0
  251. package/skills/business/brag/assets/sfx/keyboard/keypress-028.wav +0 -0
  252. package/skills/business/brag/assets/sfx/keyboard/keypress-029.wav +0 -0
  253. package/skills/business/brag/assets/sfx/keyboard/keypress-030.wav +0 -0
  254. package/skills/business/brag/assets/sfx/keyboard/keypress-031.wav +0 -0
  255. package/skills/business/brag/assets/sfx/keyboard/keypress-032.wav +0 -0
  256. package/skills/business/brag/assets/sfx/sfx-analysis.json +15584 -0
  257. package/skills/business/brag/assets/sfx/sfx-analysis.md +75 -0
  258. package/skills/business/brag/assets/sfx/ui/click1.ogg +0 -0
  259. package/skills/business/brag/assets/sfx/ui/click2.ogg +0 -0
  260. package/skills/business/brag/assets/sfx/ui/click3.ogg +0 -0
  261. package/skills/business/brag/assets/sfx/ui/click4.ogg +0 -0
  262. package/skills/business/brag/assets/sfx/ui/click5.ogg +0 -0
  263. package/skills/business/brag/assets/sfx/ui/mouseclick1.ogg +0 -0
  264. package/skills/business/brag/assets/sfx/ui/rollover1.ogg +0 -0
  265. package/skills/business/brag/assets/sfx/ui/rollover2.ogg +0 -0
  266. package/skills/business/brag/assets/sfx/ui/rollover4.ogg +0 -0
  267. package/skills/business/brag/assets/sfx/ui/rollover5.ogg +0 -0
  268. package/skills/business/brag/assets/sfx/ui/switch1.ogg +0 -0
  269. package/skills/business/brag/assets/sfx/ui/switch10.ogg +0 -0
  270. package/skills/business/brag/assets/sfx/ui/switch11.ogg +0 -0
  271. package/skills/business/brag/assets/sfx/ui/switch12.ogg +0 -0
  272. package/skills/business/brag/assets/sfx/ui/switch13.ogg +0 -0
  273. package/skills/business/brag/assets/sfx/ui/switch14.ogg +0 -0
  274. package/skills/business/brag/assets/sfx/ui/switch15.ogg +0 -0
  275. package/skills/business/brag/assets/sfx/ui/switch16.ogg +0 -0
  276. package/skills/business/brag/assets/sfx/ui/switch17.ogg +0 -0
  277. package/skills/business/brag/assets/sfx/ui/switch18.ogg +0 -0
  278. package/skills/business/brag/assets/sfx/ui/switch19.ogg +0 -0
  279. package/skills/business/brag/assets/sfx/ui/switch2.ogg +0 -0
  280. package/skills/business/brag/assets/sfx/ui/switch20.ogg +0 -0
  281. package/skills/business/brag/assets/sfx/ui/switch21.ogg +0 -0
  282. package/skills/business/brag/assets/sfx/ui/switch22.ogg +0 -0
  283. package/skills/business/brag/assets/sfx/ui/switch23.ogg +0 -0
  284. package/skills/business/brag/assets/sfx/ui/switch24.ogg +0 -0
  285. package/skills/business/brag/assets/sfx/ui/switch25.ogg +0 -0
  286. package/skills/business/brag/assets/sfx/ui/switch26.ogg +0 -0
  287. package/skills/business/brag/assets/sfx/ui/switch27.ogg +0 -0
  288. package/skills/business/brag/assets/sfx/ui/switch28.ogg +0 -0
  289. package/skills/business/brag/assets/sfx/ui/switch29.ogg +0 -0
  290. package/skills/business/brag/assets/sfx/ui/switch3.ogg +0 -0
  291. package/skills/business/brag/assets/sfx/ui/switch30.ogg +0 -0
  292. package/skills/business/brag/assets/sfx/ui/switch31.ogg +0 -0
  293. package/skills/business/brag/assets/sfx/ui/switch32.ogg +0 -0
  294. package/skills/business/brag/assets/sfx/ui/switch33.ogg +0 -0
  295. package/skills/business/brag/assets/sfx/ui/switch34.ogg +0 -0
  296. package/skills/business/brag/assets/sfx/ui/switch35.ogg +0 -0
  297. package/skills/business/brag/assets/sfx/ui/switch36.ogg +0 -0
  298. package/skills/business/brag/assets/sfx/ui/switch37.ogg +0 -0
  299. package/skills/business/brag/assets/sfx/ui/switch38.ogg +0 -0
  300. package/skills/business/brag/assets/sfx/ui/switch4.ogg +0 -0
  301. package/skills/business/brag/assets/sfx/ui/switch5.ogg +0 -0
  302. package/skills/business/brag/assets/sfx/ui/switch6.ogg +0 -0
  303. package/skills/business/brag/assets/sfx/ui/switch7.ogg +0 -0
  304. package/skills/business/brag/assets/sfx/ui/switch8.ogg +0 -0
  305. package/skills/business/brag/assets/sfx/ui/switch9.ogg +0 -0
  306. package/skills/business/brag/references/audio.md +307 -0
  307. package/skills/business/brag/references/step-1-inspect.md +133 -0
  308. package/skills/business/brag/references/step-2-plan.md +234 -0
  309. package/skills/business/brag/references/step-3-compose.md +210 -0
  310. package/skills/business/brag/references/step-4-deliver.md +168 -0
  311. package/skills/business/brag/references/tones.md +231 -0
  312. package/skills/business/brag/scripts/analyze_music_cues.py +309 -0
  313. package/skills/business/brag/scripts/pyproject.toml +14 -0
  314. package/skills/business/brag/scripts/uv.lock +819 -0
  315. package/skills/business/copywriting/SKILL.md +178 -0
  316. package/skills/business/customer-research/SKILL.md +259 -0
  317. package/skills/business/freelancing/SKILL.md +679 -0
  318. package/skills/business/launch/SKILL.md +273 -0
  319. package/skills/business/marketing-psychology/SKILL.md +364 -0
  320. package/skills/business/pricing/SKILL.md +233 -0
  321. package/skills/business/resume-revamp-jake-ats/SKILL.md +64 -0
  322. package/skills/business/startup-founder/SKILL.md +400 -0
  323. package/skills/business/startup-founder/references/funding.md +48 -0
  324. package/skills/business/startup-founder/references/further-reading.md +132 -0
  325. package/skills/business/startup-founder/references/gtm-tools.md +57 -0
  326. package/skills/business/startup-founder/references/legal-india.md +36 -0
  327. package/skills/business/startup-founder/references/marketing/README.md +16 -0
  328. package/skills/business/startup-founder/references/marketing/cold-email.md +172 -0
  329. package/skills/business/startup-founder/references/marketing/competitors.md +245 -0
  330. package/skills/business/startup-founder/references/marketing/customer-research.md +276 -0
  331. package/skills/business/startup-founder/references/marketing/free-tools.md +168 -0
  332. package/skills/business/startup-founder/references/marketing/launch.md +343 -0
  333. package/skills/business/startup-founder/references/marketing/marketing-plan.md +270 -0
  334. package/skills/business/startup-founder/references/marketing/pricing.md +237 -0
  335. package/skills/business/startup-founder/references/marketing/product-marketing.md +250 -0
  336. package/skills/business/startup-founder/references/validation.md +267 -0
  337. package/skills/devops/deployments/SKILL.md +339 -0
  338. package/skills/devops/git-ci/SKILL.md +372 -0
  339. package/skills/devops/performance/SKILL.md +523 -0
  340. package/skills/devops/testing/SKILL.md +558 -0
  341. package/skills/engineering/code-review/SKILL.md +149 -0
  342. package/skills/engineering/coding-standards/SKILL.md +229 -0
  343. package/skills/engineering/product-engineering/SKILL.md +219 -0
  344. package/skills/engineering/senior-engineer-prompts/SKILL.md +145 -0
  345. package/skills/engineering/tdd-workflow/SKILL.md +178 -0
  346. package/skills/engineering/verification-loop/SKILL.md +199 -0
  347. package/skills/nerdev/nerdev-abstraction/SKILL.md +412 -0
  348. package/skills/nerdev/nerdev-docs/SKILL.md +527 -0
  349. package/skills/nerdev/nerdev-docs/templates/adr-template.md +39 -0
  350. package/skills/nerdev/nerdev-docs/templates/contributing-template.md +140 -0
  351. package/skills/nerdev/nerdev-docs/templates/deploy-template.md +426 -0
  352. package/skills/nerdev/nerdev-docs/templates/design-template.md +105 -0
  353. package/skills/nerdev/nerdev-docs/templates/incident-template.md +66 -0
  354. package/skills/nerdev/nerdev-monorepo/SKILL.md +522 -0
  355. package/skills/quality/output-skill/SKILL.md +49 -0
  356. package/skills/quality/unslop/SKILL.md +67 -0
  357. package/skills/ui/animaxxing/SKILL.md +58 -0
  358. package/skills/ui/animaxxing/agents/openai.yaml +7 -0
  359. package/skills/ui/animaxxing/references/effect-restoration.md +26 -0
  360. package/skills/ui/animaxxing/references/motion-vocabulary.md +142 -0
  361. package/skills/ui/animaxxing/references/recipes/blast-off.md +119 -0
  362. package/skills/ui/animaxxing/references/recipes/particle-effects.md +718 -0
  363. package/skills/ui/animaxxing/references/recipes/particle-field.md +428 -0
  364. package/skills/ui/animaxxing/references/recipes/route-letters.md +205 -0
  365. package/skills/ui/animaxxing/references/recipes/speak-in.md +181 -0
  366. package/skills/ui/animaxxing/references/recipes/split-entrances.md +345 -0
  367. package/skills/ui/animaxxing/references/recipes/wave.md +144 -0
  368. package/skills/ui/animaxxing/references/text-stability.md +68 -0
  369. package/skills/ui/animaxxing/references/verification.md +44 -0
  370. package/skills/ui/ask-animation/SKILL.md +178 -0
  371. package/skills/ui/ask-base-ui/SKILL.md +400 -0
  372. package/skills/ui/ask-cmdk/SKILL.md +315 -0
  373. package/skills/ui/ask-command-menu/SKILL.md +262 -0
  374. package/skills/ui/ask-drawer/SKILL.md +235 -0
  375. package/skills/ui/ask-form/SKILL.md +229 -0
  376. package/skills/ui/ask-gsap/SKILL.md +435 -0
  377. package/skills/ui/ask-modal/SKILL.md +145 -0
  378. package/skills/ui/ask-motion/SKILL.md +339 -0
  379. package/skills/ui/ask-rhf/SKILL.md +388 -0
  380. package/skills/ui/ask-sonner/API.md +64 -0
  381. package/skills/ui/ask-sonner/SKILL.md +88 -0
  382. package/skills/ui/ask-table/SKILL.md +253 -0
  383. package/skills/ui/ask-tanstack-table/SKILL.md +490 -0
  384. package/skills/ui/ask-toast/SKILL.md +163 -0
  385. package/skills/ui/ask-vaul/SKILL.md +291 -0
  386. package/skills/ui/ask-virtual-list/SKILL.md +304 -0
  387. package/skills/ui/ask-virtuoso/SKILL.md +332 -0
  388. package/skills/ui/email-craft/SKILL.md +258 -0
  389. package/skills/ui/frontend-craftsmanship/SKILL.md +462 -0
  390. package/skills/ui/gsap-astro/SKILL.md +56 -0
  391. package/skills/ui/gsap-astro/agents/openai.yaml +7 -0
  392. package/skills/ui/gsap-astro/references/client-router-navigation.md +139 -0
  393. package/skills/ui/gsap-astro/references/initialization.md +87 -0
  394. package/skills/ui/gsap-astro/references/motion-system.md +152 -0
  395. package/skills/ui/gsap-astro/references/scripts-and-islands.md +65 -0
  396. package/skills/ui/gsap-astro/references/verification.md +44 -0
  397. package/skills/ui/gsap-core/SKILL.md +254 -0
  398. package/skills/ui/gsap-frameworks/SKILL.md +266 -0
  399. package/skills/ui/gsap-nextjs/SKILL.md +51 -0
  400. package/skills/ui/gsap-nextjs/agents/openai.yaml +7 -0
  401. package/skills/ui/gsap-nextjs/references/app-router-navigation.md +189 -0
  402. package/skills/ui/gsap-nextjs/references/initialization.md +87 -0
  403. package/skills/ui/gsap-nextjs/references/motion-system.md +158 -0
  404. package/skills/ui/gsap-nextjs/references/verification.md +40 -0
  405. package/skills/ui/gsap-nuxt/SKILL.md +56 -0
  406. package/skills/ui/gsap-nuxt/agents/openai.yaml +7 -0
  407. package/skills/ui/gsap-nuxt/references/initialization.md +87 -0
  408. package/skills/ui/gsap-nuxt/references/motion-system.md +161 -0
  409. package/skills/ui/gsap-nuxt/references/navigation.md +145 -0
  410. package/skills/ui/gsap-nuxt/references/page-transitions.md +107 -0
  411. package/skills/ui/gsap-nuxt/references/verification.md +42 -0
  412. package/skills/ui/gsap-performance/SKILL.md +79 -0
  413. package/skills/ui/gsap-plugins/SKILL.md +433 -0
  414. package/skills/ui/gsap-react/SKILL.md +136 -0
  415. package/skills/ui/gsap-react-router/SKILL.md +55 -0
  416. package/skills/ui/gsap-react-router/agents/openai.yaml +7 -0
  417. package/skills/ui/gsap-react-router/references/initialization.md +87 -0
  418. package/skills/ui/gsap-react-router/references/motion-system.md +158 -0
  419. package/skills/ui/gsap-react-router/references/react-router-navigation.md +165 -0
  420. package/skills/ui/gsap-react-router/references/route-lifetime.md +74 -0
  421. package/skills/ui/gsap-react-router/references/verification.md +42 -0
  422. package/skills/ui/gsap-scrolltrigger/SKILL.md +296 -0
  423. package/skills/ui/gsap-sveltekit/SKILL.md +54 -0
  424. package/skills/ui/gsap-sveltekit/agents/openai.yaml +7 -0
  425. package/skills/ui/gsap-sveltekit/references/initialization.md +87 -0
  426. package/skills/ui/gsap-sveltekit/references/motion-system.md +156 -0
  427. package/skills/ui/gsap-sveltekit/references/page-lifetime.md +63 -0
  428. package/skills/ui/gsap-sveltekit/references/sveltekit-navigation.md +160 -0
  429. package/skills/ui/gsap-sveltekit/references/verification.md +41 -0
  430. package/skills/ui/gsap-tanstack-router/SKILL.md +56 -0
  431. package/skills/ui/gsap-tanstack-router/agents/openai.yaml +7 -0
  432. package/skills/ui/gsap-tanstack-router/references/initialization.md +87 -0
  433. package/skills/ui/gsap-tanstack-router/references/motion-system.md +160 -0
  434. package/skills/ui/gsap-tanstack-router/references/route-lifetime.md +66 -0
  435. package/skills/ui/gsap-tanstack-router/references/tanstack-navigation.md +156 -0
  436. package/skills/ui/gsap-tanstack-router/references/verification.md +41 -0
  437. package/skills/ui/gsap-timeline/SKILL.md +107 -0
  438. package/skills/ui/gsap-utils/SKILL.md +284 -0
  439. package/skills/ui/gsap-vanilla/SKILL.md +63 -0
  440. package/skills/ui/gsap-vanilla/agents/openai.yaml +7 -0
  441. package/skills/ui/gsap-vanilla/references/cross-document-navigation.md +54 -0
  442. package/skills/ui/gsap-vanilla/references/initialization.md +87 -0
  443. package/skills/ui/gsap-vanilla/references/motion-system.md +150 -0
  444. package/skills/ui/gsap-vanilla/references/page-load.md +96 -0
  445. package/skills/ui/gsap-vanilla/references/spa-navigation.md +70 -0
  446. package/skills/ui/gsap-vanilla/references/verification.md +41 -0
  447. package/skills/ui/house-style/SKILL.md +366 -0
  448. package/skills/ui/imagegen/SKILL.md +49 -0
  449. package/skills/ui/imagegen/references/brandkit.md +48 -0
  450. package/skills/ui/imagegen/references/image-to-code.md +56 -0
  451. package/skills/ui/imagegen/references/mobile.md +58 -0
  452. package/skills/ui/imagegen/references/web.md +63 -0
  453. package/skills/ui/impeccable/SKILL.md +116 -0
  454. package/skills/ui/impeccable/reference/adapt.md +318 -0
  455. package/skills/ui/impeccable/reference/adapt.native.md +58 -0
  456. package/skills/ui/impeccable/reference/android.md +46 -0
  457. package/skills/ui/impeccable/reference/animate.md +89 -0
  458. package/skills/ui/impeccable/reference/audit.md +137 -0
  459. package/skills/ui/impeccable/reference/audit.native.md +139 -0
  460. package/skills/ui/impeccable/reference/bolder.md +33 -0
  461. package/skills/ui/impeccable/reference/clarify.md +94 -0
  462. package/skills/ui/impeccable/reference/colorize.md +86 -0
  463. package/skills/ui/impeccable/reference/craft-floor.md +44 -0
  464. package/skills/ui/impeccable/reference/craft.md +5 -0
  465. package/skills/ui/impeccable/reference/critique.md +806 -0
  466. package/skills/ui/impeccable/reference/degraded/asset-producer.md +37 -0
  467. package/skills/ui/impeccable/reference/degraded/documenter.md +24 -0
  468. package/skills/ui/impeccable/reference/degraded/finish-reviewer.md +38 -0
  469. package/skills/ui/impeccable/reference/degraded/manual-edit-applier.md +92 -0
  470. package/skills/ui/impeccable/reference/delight.md +70 -0
  471. package/skills/ui/impeccable/reference/distill.md +111 -0
  472. package/skills/ui/impeccable/reference/doctor.md +54 -0
  473. package/skills/ui/impeccable/reference/document.md +416 -0
  474. package/skills/ui/impeccable/reference/extract.md +69 -0
  475. package/skills/ui/impeccable/reference/generate.md +101 -0
  476. package/skills/ui/impeccable/reference/harden.md +345 -0
  477. package/skills/ui/impeccable/reference/hooks.md +111 -0
  478. package/skills/ui/impeccable/reference/init.md +131 -0
  479. package/skills/ui/impeccable/reference/ios.md +51 -0
  480. package/skills/ui/impeccable/reference/layout.md +84 -0
  481. package/skills/ui/impeccable/reference/live-setup.md +104 -0
  482. package/skills/ui/impeccable/reference/live.md +325 -0
  483. package/skills/ui/impeccable/reference/new-work.md +145 -0
  484. package/skills/ui/impeccable/reference/onboard.md +234 -0
  485. package/skills/ui/impeccable/reference/operate.md +61 -0
  486. package/skills/ui/impeccable/reference/optimize.md +258 -0
  487. package/skills/ui/impeccable/reference/overdrive.md +127 -0
  488. package/skills/ui/impeccable/reference/polish.md +105 -0
  489. package/skills/ui/impeccable/reference/quieter.md +99 -0
  490. package/skills/ui/impeccable/reference/routing.md +24 -0
  491. package/skills/ui/impeccable/reference/shape.md +59 -0
  492. package/skills/ui/impeccable/reference/typeset.md +80 -0
  493. package/skills/ui/impeccable/reference/visualize.md +46 -0
  494. package/skills/ui/impeccable/scripts/VERSION +1 -0
  495. package/skills/ui/impeccable/scripts/command-metadata.json +98 -0
  496. package/skills/ui/impeccable/scripts/data/font-index-failures.json +121 -0
  497. package/skills/ui/impeccable/scripts/data/font-index.json +1 -0
  498. package/skills/ui/impeccable/scripts/impeccable +206 -0
  499. package/skills/ui/impeccable/scripts/impeccable.cmd +214 -0
  500. package/skills/ui/impeccable/scripts/live-browser-dom.js +167 -0
  501. package/skills/ui/impeccable/scripts/live-browser-ignores.js +242 -0
  502. package/skills/ui/impeccable/scripts/live-browser-session.js +144 -0
  503. package/skills/ui/impeccable/scripts/live-browser.js +13510 -0
  504. package/skills/ui/impeccable/scripts/modern-screenshot.umd.js +14 -0
  505. package/skills/ui/motion/SKILL.md +207 -0
  506. package/skills/ui/pick-ui-library/SKILL.md +85 -0
  507. package/skills/ui/product-messaging/SKILL.md +163 -0
  508. package/skills/ui/product-site/SKILL.md +465 -0
  509. package/skills/ui/prototype/PICKER.md +197 -0
  510. package/skills/ui/prototype/SKILL.md +98 -0
  511. package/skills/ui/refero-design/SKILL.md +534 -0
  512. package/skills/ui/refero-design/agents/openai.yaml +4 -0
  513. package/skills/ui/refero-design/references/anti-ai-slop.md +292 -0
  514. package/skills/ui/refero-design/references/color.md +563 -0
  515. package/skills/ui/refero-design/references/copywriting.md +222 -0
  516. package/skills/ui/refero-design/references/craft-details.md +525 -0
  517. package/skills/ui/refero-design/references/example-workflow.md +264 -0
  518. package/skills/ui/refero-design/references/icons.md +166 -0
  519. package/skills/ui/refero-design/references/mcp-tools.md +258 -0
  520. package/skills/ui/refero-design/references/motion.md +504 -0
  521. package/skills/ui/refero-design/references/typography.md +737 -0
  522. package/skills/ui/refero-design/references/visual-workflow.md +105 -0
  523. package/skills/ui/style-animaxxing/SKILL.md +48 -0
  524. package/skills/ui/style-animaxxing/agents/openai.yaml +7 -0
  525. package/skills/ui/style-animaxxing/references/motion-vocabulary.md +51 -0
  526. package/skills/ui/style-animaxxing/references/tokens.md +288 -0
  527. package/skills/ui/style-animaxxing/references/typography-and-layout.md +149 -0
  528. package/skills/ui/style-animaxxing/references/verification.md +38 -0
  529. package/skills/ui/typography-picker/SKILL.md +305 -0
  530. package/skills/ui/ui-core/SKILL.md +345 -0
  531. package/skills/ui/ui-core/references/accessibility.md +85 -0
  532. package/skills/ui/ui-core/references/build.md +666 -0
  533. package/skills/ui/ui-core/references/copy.md +70 -0
  534. package/skills/ui/ui-core/references/dark-mode.md +76 -0
  535. package/skills/ui/ui-core/references/direction.md +99 -0
  536. package/skills/ui/ui-core/references/redesign.md +74 -0
  537. package/skills/ui/ui-core/references/responsive.md +77 -0
  538. package/skills/ui/ui-core/references/styles.md +42 -0
  539. package/skills/ui/ui-core/references/system.md +190 -0
  540. package/skills/ui/ui-core/references/verify.md +37 -0
  541. package/skills/ui/ui-engineering/SKILL.md +436 -0
  542. package/skills/ui/ui-engineering/references/avatars.md +74 -0
  543. package/skills/ui/ui-engineering/references/card-patterns.md +66 -0
  544. package/skills/ui/ui-engineering/references/floating-actions.md +65 -0
  545. package/skills/ui/ui-engineering/references/navigation.md +81 -0
  546. package/skills/ui/ui-engineering/references/overlays.md +74 -0
  547. package/skills/ui/ui-engineering/references/pagination.md +71 -0
  548. package/skills/ui/ui-engineering/references/progress-indicators.md +77 -0
  549. package/skills/ui/ui-engineering/references/quality-bar.md +293 -0
  550. package/skills/ui/ui-engineering/references/search.md +68 -0
  551. package/skills/ui/ui-engineering/references/tailwind-architecture.md +81 -0
  552. package/skills/ui/ui-theme-picker/SKILL.md +522 -0
  553. package/skills/ui/ui-theme-picker/references/component-sources.md +223 -0
  554. package/skills/ui/ui-theme-picker/references/styles.md +298 -0
  555. package/skills/ui/vitepress-docs/SKILL.md +439 -0
  556. package/skills/ui/vitepress-docs/references/components.md +333 -0
  557. package/skills/ui/vitepress-docs/references/layout.md +128 -0
  558. package/skills/ui/vitepress-docs/references/tokens.md +87 -0
  559. package/skills/ui/vitepress-docs/references/typography.md +76 -0
  560. package/skills/workflow/careful/SKILL.md +48 -0
  561. package/skills/workflow/context-restore/SKILL.md +69 -0
  562. package/skills/workflow/context-save/SKILL.md +96 -0
  563. package/skills/workflow/health/SKILL.md +99 -0
  564. package/skills/workflow/learn/SKILL.md +95 -0
  565. package/skills/workflow/office-hours/SKILL.md +136 -0
  566. package/skills/workflow/ship/SKILL.md +95 -0
  567. package/vendor.json +80 -0
@@ -0,0 +1,718 @@
1
+ # Recipe: particle effects
2
+
3
+ Failure contract: apply [effect restoration](../effect-restoration.md) when adapting this module. The framework controller chooses recovery timing; this effect must undo even partial setup.
4
+
5
+ Five treatments on the [particle field](particle-field.md). Each defines how its element assembles on entrance, what plays while it idles, what happens under the pointer, and what a blast looks like. The framework skill's controller runs the phases through `attachParticleEffect`; these modules never decide when.
6
+
7
+ | Effect | Element | Layer | Bleed | Idle |
8
+ |---|---|---|---|---|
9
+ | `marquee` | secondary call to action (outline) | over | 160 | six lights chase the outline; glints |
10
+ | `reactor` | primary call to action (solid) | under | 180 | embers rise from the button; a slow heartbeat outline |
11
+ | `resolve` | card | over | 140 | one light wanders the outline; glints |
12
+ | `slipstream` | an onward link (Next →) | over | 160 | hairlines drift past left to right |
13
+ | `ignite` | text on a rule: a command, a giant field | over | 200 | embers and glints off the underline |
14
+
15
+ Dependencies: `gsap`, `./field`.
16
+
17
+ ```ts
18
+ import gsap from "gsap";
19
+ import { perimeterLength, perimeterPoint, type ParticleField } from "./field";
20
+ import type { ParticleEffectDefinition, ParticleEffectInstance } from "./attach";
21
+
22
+ const rnd = gsap.utils.random;
23
+ export type ButtonEffect = ParticleEffectDefinition<ParticleEffectInstance> & { layer: "under" | "over" };
24
+ ```
25
+
26
+ ## marquee
27
+
28
+ Lights gather from all around and land evenly along the outline, the button pops in, the sign ignites with a burst, and the runners set off.
29
+
30
+ ```ts
31
+ const RUNNERS = 6;
32
+ /** Runner speed along the outline, px/s. */
33
+ const RUNNER_SPEED = 110;
34
+ /** How much faster the runners go under the pointer. */
35
+ const RUNNER_RUSH = 4;
36
+
37
+ export const marquee: ButtonEffect = {
38
+ layer: "over",
39
+ bleed: 160,
40
+ create(field, button) {
41
+ const runners: { t: number }[] = [];
42
+ const state = { rush: 1, hovering: false };
43
+ let twinkleIn = 0.4;
44
+ let sprayAcc = 0;
45
+
46
+ const ambient = (dt: number) => {
47
+ const { box, radius } = field;
48
+ const total = perimeterLength(box, radius);
49
+ for (const run of runners) {
50
+ run.t = (run.t + (RUNNER_SPEED * state.rush * dt) / total) % 1;
51
+ const pt = perimeterPoint(box, radius, run.t);
52
+ // The head is bright; each frame leaves a dot that fades, so the faster the runner the longer its tail.
53
+ field.spawn({ x: pt.x, y: pt.y, size: 2.8, alpha: 1, life: 0.4 * (0.6 + state.rush / RUNNER_RUSH), shrink: true });
54
+ }
55
+ if (state.hovering) {
56
+ // Sparkler: fine sparks fly off the outline while the pointer is on it.
57
+ sprayAcc += 90 * dt;
58
+ while (sprayAcc >= 1) {
59
+ sprayAcc -= 1;
60
+ const pt = perimeterPoint(box, radius, Math.random());
61
+ const speed = rnd(60, 180);
62
+ const jitter = rnd(-0.6, 0.6);
63
+ field.spawn({
64
+ x: pt.x, y: pt.y,
65
+ vx: (pt.nx + jitter * pt.ny) * speed, vy: (pt.ny - jitter * pt.nx) * speed,
66
+ size: rnd(0.8, 1.6), alpha: rnd(0.6, 1), life: rnd(0.3, 0.7), drag: 0.08, gravity: 60, shape: "spark",
67
+ });
68
+ }
69
+ }
70
+ twinkleIn -= dt;
71
+ if (twinkleIn <= 0) {
72
+ twinkleIn = rnd(0.25, 0.7) / (state.hovering ? 3 : 1);
73
+ twinkle(perimeterPoint(box, radius, Math.random()));
74
+ }
75
+ };
76
+
77
+ function twinkle(pt: { x: number; y: number }) {
78
+ const size = rnd(4, 9);
79
+ field.spawn({
80
+ x: pt.x, y: pt.y, size, shape: "star", rotation: rnd(0, Math.PI), spin: rnd(-1.5, 1.5), life: rnd(0.45, 0.8), fade: false,
81
+ update: (p) => { const k = Math.sin((p.age / p.life) * Math.PI); p.size = size * k; p.alpha = k; },
82
+ });
83
+ }
84
+
85
+ function idle() {
86
+ runners.length = 0;
87
+ for (let k = 0; k < RUNNERS; k++) runners.push({ t: k / RUNNERS });
88
+ field.addEmitter(ambient);
89
+ }
90
+
91
+ function flash(count: number, speed: [number, number]) {
92
+ const { box, radius } = field;
93
+ for (let i = 0; i < count; i++) {
94
+ const pt = perimeterPoint(box, radius, i / count + rnd(-0.01, 0.01));
95
+ const v = rnd(speed[0], speed[1]);
96
+ field.spawn({ x: pt.x, y: pt.y, vx: pt.nx * v, vy: pt.ny * v, size: rnd(1, 2.2), life: rnd(0.35, 0.7), drag: 0.04, shape: "spark" });
97
+ }
98
+ }
99
+
100
+ return {
101
+ enter(delay) {
102
+ field.sync();
103
+ field.particles.length = 0;
104
+ runners.length = 0;
105
+ const { box, radius } = field;
106
+ const cx = box.x + box.w / 2;
107
+ const cy = box.y + box.h / 2;
108
+ const tl = gsap.timeline({ delay });
109
+ gsap.set(button, { autoAlpha: 0, scale: 0.5, transformOrigin: "50% 50%" });
110
+
111
+ const count = 72;
112
+ for (let i = 0; i < count; i++) {
113
+ const target = perimeterPoint(box, radius, i / count);
114
+ const angle = rnd(0, Math.PI * 2);
115
+ const dist = rnd(180, 340);
116
+ const p = field.spawn({ x: cx + Math.cos(angle) * dist, y: cy + Math.sin(angle) * dist, size: rnd(1.4, 2.8), alpha: 0, life: Infinity, fade: false });
117
+ tl.to(
118
+ p,
119
+ {
120
+ x: target.x, y: target.y, alpha: 1, duration: rnd(0.55, 0.85), ease: "power3.inOut",
121
+ onComplete: () => { p.life = 0.5; p.age = 0; p.fade = true; p.shrink = true; },
122
+ },
123
+ rnd(0, 0.25),
124
+ );
125
+ }
126
+ tl.to(button, { autoAlpha: 1, scale: 1, duration: 0.5, ease: "back.out(1.7)" }, 0.55);
127
+ tl.call(() => { flash(40, [80, 200]); idle(); }, [], 0.95);
128
+ return tl;
129
+ },
130
+ exit() { field.removeEmitter(ambient); runners.length = 0; field.release(0.25); },
131
+ blast() {
132
+ field.removeEmitter(ambient);
133
+ runners.length = 0;
134
+ flash(90, [350, 900]);
135
+ for (let i = 0; i < 12; i++) twinkle(perimeterPoint(field.box, field.radius, Math.random()));
136
+ },
137
+ idle,
138
+ hover(on) {
139
+ state.hovering = on;
140
+ gsap.to(state, { rush: on ? RUNNER_RUSH : 1, duration: on ? 0.35 : 0.8, ease: on ? "power3.out" : "power2.inOut", overwrite: true });
141
+ if (on) flash(36, [120, 260]);
142
+ },
143
+ destroy() { field.removeEmitter(ambient); gsap.killTweensOf(state); },
144
+ };
145
+ },
146
+ };
147
+ ```
148
+
149
+ ## reactor
150
+
151
+ Sparks spiral in from a wide ring and collapse into the button; it pops in and the collapse rebounds outward as a shockwave. Idle, it breathes embers.
152
+
153
+ ```ts
154
+ const EMBER_RATE = { idle: 9, hover: 70 };
155
+ const EMBER_RISE = { idle: 55, hover: 220 };
156
+ const PULSE_EVERY = 2.6;
157
+
158
+ export const reactor: ButtonEffect = {
159
+ layer: "under",
160
+ bleed: 180,
161
+ create(field, button) {
162
+ const state = { rate: EMBER_RATE.idle, rise: EMBER_RISE.idle, hovering: false };
163
+ let emberAcc = 0;
164
+ let pulseIn = 1.2;
165
+
166
+ const ambient = (dt: number) => {
167
+ const { box } = field;
168
+ // Embers start inside the button, hidden behind it, and surface as they rise past its top edge.
169
+ emberAcc += state.rate * dt;
170
+ while (emberAcc >= 1) {
171
+ emberAcc -= 1;
172
+ field.spawn({
173
+ x: box.x + rnd(4, box.w - 4), y: box.y + rnd(2, box.h - 2),
174
+ vx: rnd(-10, 10) * (state.hovering ? 3 : 1), vy: -rnd(state.rise * 0.6, state.rise * 1.3),
175
+ size: rnd(1.2, 2.8), alpha: rnd(0.7, 1), life: rnd(1.1, 2.1), gravity: -12,
176
+ wobble: rnd(6, 16), wobbleFreq: rnd(1.5, 4), phase: rnd(0, Math.PI * 2),
177
+ });
178
+ }
179
+ pulseIn -= dt;
180
+ if (pulseIn <= 0) {
181
+ pulseIn = PULSE_EVERY;
182
+ if (!state.hovering) shockwave(28, 1.3, 0.35);
183
+ }
184
+ };
185
+
186
+ function shockwave(spread: number, duration: number, alpha: number) {
187
+ field.spawn({
188
+ x: 0, y: 0, shape: "outline", size: 0, alpha, life: duration,
189
+ update: (p) => { p.size = spread * gsap.parseEase("power2.out")(p.age / p.life); },
190
+ });
191
+ }
192
+
193
+ function erupt(count: number, speed: [number, number]) {
194
+ const { box, radius } = field;
195
+ for (let i = 0; i < count; i++) {
196
+ const pt = perimeterPoint(box, radius, Math.random());
197
+ const v = rnd(speed[0], speed[1]);
198
+ const jitter = rnd(-0.5, 0.5);
199
+ const square = Math.random() < 0.35;
200
+ field.spawn({
201
+ x: pt.x, y: pt.y,
202
+ vx: (pt.nx + jitter * pt.ny) * v, vy: (pt.ny - jitter * pt.nx) * v - 40,
203
+ size: square ? rnd(1.5, 3) : rnd(1, 2.4), shape: square ? "square" : "dot",
204
+ rotation: rnd(0, Math.PI), spin: rnd(-12, 12), life: rnd(0.5, 1.1), drag: 0.12, gravity: 320,
205
+ });
206
+ }
207
+ }
208
+
209
+ return {
210
+ enter(delay) {
211
+ field.sync();
212
+ field.particles.length = 0;
213
+ const { box } = field;
214
+ const cx = box.x + box.w / 2;
215
+ const cy = box.y + box.h / 2;
216
+ const tl = gsap.timeline({ delay });
217
+ gsap.set(button, { autoAlpha: 0, scale: 0.5, transformOrigin: "50% 50%" });
218
+
219
+ const count = 110;
220
+ for (let i = 0; i < count; i++) {
221
+ const orbit = { radius: rnd(200, 320), angle: rnd(0, Math.PI * 2) };
222
+ const landing = { x: rnd(-box.w * 0.4, box.w * 0.4), y: rnd(-box.h * 0.4, box.h * 0.4) };
223
+ const p = field.spawn({ x: cx + Math.cos(orbit.angle) * orbit.radius, y: cy + Math.sin(orbit.angle) * orbit.radius, size: rnd(1, 2.4), alpha: 0, life: Infinity, fade: false, shape: "spark" });
224
+ const turn = rnd(1.2, 2.4) * (Math.random() < 0.5 ? -1 : 1);
225
+ tl.to(
226
+ orbit,
227
+ {
228
+ radius: 0, angle: orbit.angle + turn, duration: rnd(0.6, 0.9), ease: "power3.in",
229
+ onUpdate() {
230
+ const k = 1 - orbit.radius / 320;
231
+ const nx = cx + landing.x * k + Math.cos(orbit.angle) * orbit.radius;
232
+ const ny = cy + landing.y * k + Math.sin(orbit.angle) * orbit.radius;
233
+ // Velocity only orients the spark's tail; position is set here.
234
+ p.vx = (nx - p.x) * 60; p.vy = (ny - p.y) * 60;
235
+ p.x = nx; p.y = ny;
236
+ p.alpha = Math.min(1, this.progress() * 3);
237
+ },
238
+ onComplete: () => { p.life = 0.12; p.age = 0; p.fade = true; p.vx = 0; p.vy = 0; },
239
+ },
240
+ rnd(0, 0.2),
241
+ );
242
+ }
243
+ tl.to(button, { autoAlpha: 1, scale: 1, duration: 0.45, ease: "back.out(2.2)" }, 0.82);
244
+ tl.call(() => { shockwave(60, 0.7, 0.9); erupt(44, [140, 320]); }, [], 0.84);
245
+ tl.call(() => shockwave(90, 0.9, 0.5), [], 0.96);
246
+ tl.call(() => field.addEmitter(ambient), [], 1.05);
247
+ return tl;
248
+ },
249
+ exit() { field.removeEmitter(ambient); field.release(0.25); },
250
+ blast() {
251
+ field.removeEmitter(ambient);
252
+ shockwave(120, 0.6, 1);
253
+ gsap.delayedCall(0.08, () => shockwave(160, 0.7, 0.6));
254
+ gsap.delayedCall(0.16, () => shockwave(200, 0.8, 0.3));
255
+ erupt(140, [400, 1000]);
256
+ },
257
+ idle() { field.addEmitter(ambient); },
258
+ hover(on) {
259
+ state.hovering = on;
260
+ gsap.to(state, { rate: on ? EMBER_RATE.hover : EMBER_RATE.idle, rise: on ? EMBER_RISE.hover : EMBER_RISE.idle, duration: on ? 0.3 : 0.9, ease: on ? "power3.out" : "power2.inOut", overwrite: true });
261
+ gsap.to(button, { scale: on ? 1.04 : 1, duration: 0.3, ease: on ? "back.out(2)" : "power2.out", overwrite: "auto" });
262
+ if (on) { shockwave(40, 0.5, 0.8); erupt(56, [160, 360]); }
263
+ },
264
+ destroy() { field.removeEmitter(ambient); gsap.killTweensOf(state); },
265
+ };
266
+ },
267
+ };
268
+ ```
269
+
270
+ ## resolve
271
+
272
+ Particles stream in from far off and settle into a dot grid over the card's face, row by row like a scan; the card fades up beneath them and the grid dissolves.
273
+
274
+ ```ts
275
+ /** Spacing of the dot grid the card resolves from, in px. */
276
+ const GRID = 16;
277
+ const CARD_RUNNERS = { idle: 1, hover: 4 };
278
+ const CARD_RUNNER_SPEED = { idle: 50, hover: 260 };
279
+
280
+ export const resolve: ButtonEffect = {
281
+ layer: "over",
282
+ bleed: 140,
283
+ create(field, card) {
284
+ const runners: { t: number }[] = [];
285
+ const state = { speed: CARD_RUNNER_SPEED.idle, hovering: false };
286
+ let glintIn = 0.6;
287
+ let sprayAcc = 0;
288
+
289
+ const ambient = (dt: number) => {
290
+ const { box, radius } = field;
291
+ const total = perimeterLength(box, radius);
292
+ for (const run of runners) {
293
+ run.t = (run.t + (state.speed * dt) / total) % 1;
294
+ const pt = perimeterPoint(box, radius, run.t);
295
+ field.spawn({ x: pt.x, y: pt.y, size: state.hovering ? 2.4 : 1.8, life: state.hovering ? 0.45 : 0.6, shrink: true });
296
+ }
297
+ if (state.hovering) {
298
+ sprayAcc += 40 * dt;
299
+ while (sprayAcc >= 1) {
300
+ sprayAcc -= 1;
301
+ const pt = perimeterPoint(box, radius, Math.random());
302
+ const speed = rnd(40, 120);
303
+ field.spawn({ x: pt.x, y: pt.y, vx: pt.nx * speed, vy: pt.ny * speed, size: rnd(0.8, 1.4), life: rnd(0.3, 0.6), drag: 0.1, shape: "spark" });
304
+ }
305
+ }
306
+ glintIn -= dt;
307
+ if (glintIn <= 0) {
308
+ glintIn = rnd(0.6, 1.6) / (state.hovering ? 4 : 1);
309
+ glint(perimeterPoint(box, radius, Math.random()));
310
+ }
311
+ };
312
+
313
+ function glint(pt: { x: number; y: number }) {
314
+ const size = rnd(3, 7);
315
+ field.spawn({
316
+ x: pt.x, y: pt.y, size, shape: "star", rotation: rnd(0, Math.PI), spin: rnd(-1, 1), life: rnd(0.4, 0.7), fade: false,
317
+ update: (p) => { const k = Math.sin((p.age / p.life) * Math.PI); p.size = size * k; p.alpha = k; },
318
+ });
319
+ }
320
+
321
+ function setRunners(count: number) {
322
+ runners.length = 0;
323
+ for (let k = 0; k < count; k++) runners.push({ t: k / count });
324
+ }
325
+
326
+ function flash(count: number, speed: [number, number]) {
327
+ const { box, radius } = field;
328
+ for (let i = 0; i < count; i++) {
329
+ const pt = perimeterPoint(box, radius, i / count + rnd(-0.01, 0.01));
330
+ const v = rnd(speed[0], speed[1]);
331
+ field.spawn({ x: pt.x, y: pt.y, vx: pt.nx * v, vy: pt.ny * v, size: rnd(1, 2), life: rnd(0.3, 0.6), drag: 0.05, shape: "spark" });
332
+ }
333
+ }
334
+
335
+ function idle() { setRunners(CARD_RUNNERS.idle); field.addEmitter(ambient); }
336
+
337
+ return {
338
+ enter(delay) {
339
+ field.sync();
340
+ field.particles.length = 0;
341
+ runners.length = 0;
342
+ const { box } = field;
343
+ const cx = box.x + box.w / 2;
344
+ const cy = box.y + box.h / 2;
345
+ const tl = gsap.timeline({ delay });
346
+ gsap.set(card, { autoAlpha: 0, scale: 0.94, transformOrigin: "50% 50%" });
347
+
348
+ const cols = Math.max(2, Math.floor(box.w / GRID));
349
+ const rows = Math.max(2, Math.floor(box.h / GRID));
350
+ const padX = (box.w - (cols - 1) * GRID) / 2;
351
+ const padY = (box.h - (rows - 1) * GRID) / 2;
352
+ const settle = 0.75;
353
+ for (let r = 0; r < rows; r++) {
354
+ for (let c = 0; c < cols; c++) {
355
+ const tx = box.x + padX + c * GRID;
356
+ const ty = box.y + padY + r * GRID;
357
+ const angle = rnd(0, Math.PI * 2);
358
+ const dist = rnd(160, 420);
359
+ const p = field.spawn({ x: cx + Math.cos(angle) * dist, y: cy + Math.sin(angle) * dist, size: rnd(1, 1.8), alpha: 0, life: Infinity, fade: false });
360
+ tl.to(
361
+ p,
362
+ {
363
+ x: tx, y: ty, alpha: rnd(0.6, 1), duration: rnd(0.45, 0.7), ease: "power3.inOut",
364
+ onComplete: () => { p.life = 0.45; p.age = 0; p.fade = true; p.shrink = true; },
365
+ },
366
+ (r / rows) * 0.25 + rnd(0, 0.06),
367
+ );
368
+ }
369
+ }
370
+ tl.to(card, { autoAlpha: 1, scale: 1, duration: 0.45, ease: "power3.out" }, settle);
371
+ tl.call(() => { flash(24, [40, 120]); idle(); }, [], settle + 0.25);
372
+ return tl;
373
+ },
374
+ exit() { field.removeEmitter(ambient); runners.length = 0; field.release(0.25); },
375
+ blast() { field.removeEmitter(ambient); runners.length = 0; flash(60, [250, 700]); },
376
+ idle,
377
+ hover(on) {
378
+ state.hovering = on;
379
+ setRunners(on ? CARD_RUNNERS.hover : CARD_RUNNERS.idle);
380
+ gsap.to(state, { speed: on ? CARD_RUNNER_SPEED.hover : CARD_RUNNER_SPEED.idle, duration: on ? 0.3 : 0.7, ease: on ? "power3.out" : "power2.inOut", overwrite: true });
381
+ if (on) flash(28, [80, 200]);
382
+ },
383
+ destroy() { field.removeEmitter(ambient); gsap.killTweensOf(state); },
384
+ };
385
+ },
386
+ };
387
+ ```
388
+
389
+ Cards in a grid enter with `index * 0.09` stagger on `idle`, so the route's own items have landed before the grid resolves.
390
+
391
+ ## slipstream
392
+
393
+ The link rides a slipstream: hairlines rush past it from left to right. It arrives from the left through a gust and docks with a puff of sparks off the arrow. Idle, a line or two drifts past; under the pointer the wind picks up and the link leans into it. Pressed, everything blows off to the right.
394
+
395
+ ```ts
396
+ const STREAK_RATE = { idle: 5, hover: 60 };
397
+ const STREAK_SPEED = { idle: 90, hover: 520 };
398
+ /** How far, in px, the link leans into the wind under the pointer. */
399
+ const LEAN = 4;
400
+
401
+ export const slipstream: ButtonEffect = {
402
+ layer: "over",
403
+ bleed: 160,
404
+ create(field, button) {
405
+ const state = { rate: STREAK_RATE.idle, speed: STREAK_SPEED.idle, hovering: false };
406
+ let streakAcc = 0;
407
+ let sparkIn = 1;
408
+
409
+ /** One level hairline crossing the band, left to right. */
410
+ function streak(x: number, speed: number, life = rnd(0.5, 0.9), alpha = rnd(0.3, 0.7)) {
411
+ const { box } = field;
412
+ field.spawn({ x, y: box.y + rnd(-6, box.h + 6), vx: speed, size: gsap.utils.clamp(3, 28, speed * 0.04), alpha, life, shape: "streak", shrink: true });
413
+ }
414
+
415
+ /** Sparks leaving the arrow's tip at the right edge. */
416
+ function tip(count: number, speed: [number, number]) {
417
+ const { box } = field;
418
+ for (let i = 0; i < count; i++) {
419
+ const v = rnd(speed[0], speed[1]);
420
+ field.spawn({ x: box.x + box.w - 4, y: box.y + box.h / 2 + rnd(-4, 4), vx: v, vy: rnd(-v * 0.18, v * 0.18), size: rnd(0.8, 1.6), life: rnd(0.3, 0.6), drag: 0.1, shape: "spark" });
421
+ }
422
+ }
423
+
424
+ const ambient = (dt: number) => {
425
+ const { box } = field;
426
+ streakAcc += state.rate * dt;
427
+ while (streakAcc >= 1) {
428
+ streakAcc -= 1;
429
+ streak(box.x - rnd(20, 120), state.speed * rnd(0.7, 1.3));
430
+ }
431
+ sparkIn -= dt;
432
+ if (sparkIn <= 0) {
433
+ sparkIn = rnd(0.8, 2.2) / (state.hovering ? 8 : 1);
434
+ tip(state.hovering ? 3 : 1, [state.speed, state.speed * 2]);
435
+ }
436
+ };
437
+
438
+ function idle() { field.addEmitter(ambient); }
439
+
440
+ return {
441
+ enter(delay) {
442
+ field.sync();
443
+ field.particles.length = 0;
444
+ const { box } = field;
445
+ const tl = gsap.timeline({ delay });
446
+ gsap.set(button, { autoAlpha: 0, x: -48 });
447
+ for (let i = 0; i < 44; i++) {
448
+ tl.call(() => streak(box.x - rnd(40, 160), rnd(500, 900), rnd(0.35, 0.6), rnd(0.4, 0.9)), [], rnd(0, 0.35));
449
+ }
450
+ tl.to(button, { autoAlpha: 1, x: 0, duration: 0.55, ease: "power4.out" }, 0.1);
451
+ tl.call(() => { tip(14, [200, 480]); idle(); }, [], 0.5);
452
+ return tl;
453
+ },
454
+ exit() { field.removeEmitter(ambient); field.release(0.25); },
455
+ blast() {
456
+ field.removeEmitter(ambient);
457
+ const { box } = field;
458
+ for (let i = 0; i < 60; i++) streak(box.x + rnd(-80, box.w), rnd(700, 1400), rnd(0.3, 0.6), rnd(0.5, 1));
459
+ tip(50, [500, 1200]);
460
+ },
461
+ idle,
462
+ hover(on) {
463
+ state.hovering = on;
464
+ gsap.to(state, { rate: on ? STREAK_RATE.hover : STREAK_RATE.idle, speed: on ? STREAK_SPEED.hover : STREAK_SPEED.idle, duration: on ? 0.3 : 0.8, ease: on ? "power3.out" : "power2.inOut", overwrite: true });
465
+ gsap.to(button, { x: on ? LEAN : 0, duration: 0.3, ease: on ? "back.out(2)" : "power2.out", overwrite: "auto" });
466
+ if (on) tip(10, [200, 500]);
467
+ },
468
+ destroy() { field.removeEmitter(ambient); gsap.killTweensOf(state); },
469
+ };
470
+ },
471
+ };
472
+ ```
473
+
474
+ On press, `blast()` and tween the link `x: 64, autoAlpha: 0` while the framework skill's navigation proceeds.
475
+
476
+ ## ignite
477
+
478
+ A block of text set on a rule, such as an install command or a large form field. On entrance a runner streaks along the underline and the text is revealed in its wake. Idle, glints drift up off the rule; under the pointer or focus it runs hot. `blast()` lights the whole rule at once and throws sparks skyward. It reads its baseline from the bottom of the target, so give the target the rule as a bottom border.
479
+
480
+ ```ts
481
+ import type { Particle } from "./field";
482
+
483
+ /** Embers per second off the underline, idle and hot. */
484
+ const EMBER_RATE = { idle: 4, hot: 26 };
485
+ /** Seconds between idle glints. */
486
+ const GLINT_EVERY: [number, number] = [0.5, 1.4];
487
+
488
+ export const ignite: ParticleEffectDefinition<ParticleEffectInstance> = {
489
+ bleed: 200,
490
+ create(field: ParticleField, target: HTMLElement) {
491
+ const state = { rate: EMBER_RATE.idle, hot: false };
492
+ let emberAcc = 0;
493
+ let glintIn = 0.6;
494
+
495
+ function baseline(): number {
496
+ const { box } = field;
497
+ return box.y + box.h;
498
+ }
499
+
500
+ const ambient = (dt: number) => {
501
+ const { box } = field;
502
+ const y = baseline();
503
+ emberAcc += state.rate * dt;
504
+ while (emberAcc >= 1) {
505
+ emberAcc -= 1;
506
+ field.spawn({
507
+ x: box.x + rnd(0, box.w),
508
+ y: y + rnd(-1, 1),
509
+ vx: rnd(-6, 6),
510
+ vy: -rnd(20, 60) * (state.hot ? 2 : 1),
511
+ size: rnd(0.8, 2),
512
+ alpha: rnd(0.5, 1),
513
+ life: rnd(0.9, 1.8),
514
+ gravity: -10,
515
+ wobble: rnd(4, 12),
516
+ wobbleFreq: rnd(1.5, 3.5),
517
+ phase: rnd(0, Math.PI * 2),
518
+ });
519
+ }
520
+ glintIn -= dt;
521
+ if (glintIn <= 0) {
522
+ glintIn = rnd(GLINT_EVERY[0], GLINT_EVERY[1]) / (state.hot ? 3 : 1);
523
+ glint(box.x + rnd(0, box.w), y);
524
+ }
525
+ };
526
+
527
+ function glint(x: number, y: number) {
528
+ const size = rnd(4, 10);
529
+ field.spawn({
530
+ x,
531
+ y,
532
+ size,
533
+ shape: "star",
534
+ rotation: rnd(0, Math.PI),
535
+ spin: rnd(-1.5, 1.5),
536
+ life: rnd(0.4, 0.8),
537
+ fade: false,
538
+ update: (p) => {
539
+ const k = Math.sin((p.age / p.life) * Math.PI);
540
+ p.size = size * k;
541
+ p.alpha = k;
542
+ },
543
+ });
544
+ }
545
+
546
+ /** Sparks fanning up and out from a point, as if struck. */
547
+ function strike(x: number, y: number, count: number, speed: [number, number]) {
548
+ for (let i = 0; i < count; i++) {
549
+ const angle = -Math.PI / 2 + rnd(-1.1, 1.1);
550
+ const v = rnd(speed[0], speed[1]);
551
+ field.spawn({
552
+ x,
553
+ y,
554
+ vx: Math.cos(angle) * v,
555
+ vy: Math.sin(angle) * v,
556
+ size: rnd(0.9, 2.2),
557
+ alpha: rnd(0.7, 1),
558
+ life: rnd(0.35, 0.8),
559
+ drag: 0.06,
560
+ gravity: 420,
561
+ shape: "spark",
562
+ });
563
+ }
564
+ glint(x, y);
565
+ }
566
+
567
+ /** A hairline flare that spreads outward along the rule from a point. */
568
+ function ripple(x: number, y: number, reach: number, duration: number) {
569
+ for (const dir of [-1, 1]) {
570
+ field.spawn({
571
+ x,
572
+ y,
573
+ size: 0,
574
+ alpha: 1,
575
+ shape: "streak",
576
+ life: duration,
577
+ update: (p) => {
578
+ const k = gsap.parseEase("power3.out")(p.age / p.life);
579
+ p.size = 6 + reach * k * 0.5;
580
+ p.x = x + dir * reach * k * 0.5;
581
+ },
582
+ });
583
+ }
584
+ }
585
+
586
+ function idle() {
587
+ field.addEmitter(ambient);
588
+ }
589
+
590
+ return {
591
+ enter(delay) {
592
+ field.sync();
593
+ field.particles.length = 0;
594
+ const { box } = field;
595
+ const y = baseline();
596
+ const tl = gsap.timeline({ delay });
597
+ gsap.set(target, { autoAlpha: 0, clipPath: "inset(-20% 100% -20% 0)" });
598
+
599
+ // A runner streaks the length of the rule and the command appears
600
+ // behind it, with sparks kicked up as it goes.
601
+ const run = { x: box.x };
602
+ let head: Particle | null = null;
603
+ tl.set(target, { autoAlpha: 1 }, 0);
604
+ tl.to(
605
+ run,
606
+ {
607
+ x: box.x + box.w,
608
+ duration: 0.7,
609
+ ease: "power2.inOut",
610
+ onStart() {
611
+ head = field.spawn({
612
+ x: box.x,
613
+ y,
614
+ size: 2.6,
615
+ alpha: 1,
616
+ life: Infinity,
617
+ fade: false,
618
+ shape: "spark",
619
+ });
620
+ },
621
+ onUpdate() {
622
+ if (!head) {
623
+ return;
624
+ }
625
+ head.vx = (run.x - head.x) * 60;
626
+ head.x = run.x;
627
+ for (let i = 0; i < 3; i++) {
628
+ field.spawn({
629
+ x: run.x + rnd(-6, 0),
630
+ y: y + rnd(-1, 1),
631
+ vx: -rnd(20, 120),
632
+ vy: -rnd(20, 140),
633
+ size: rnd(0.8, 1.8),
634
+ life: rnd(0.3, 0.7),
635
+ drag: 0.08,
636
+ gravity: 240,
637
+ shape: "spark",
638
+ });
639
+ }
640
+ gsap.set(target, {
641
+ clipPath: `inset(-20% ${(1 - this.progress()) * 100}% -20% 0)`,
642
+ });
643
+ },
644
+ onComplete: () => {
645
+ if (head) {
646
+ head.life = 0.1;
647
+ head.age = 0;
648
+ head.fade = true;
649
+ head.vx = 0;
650
+ }
651
+ },
652
+ },
653
+ 0,
654
+ );
655
+ tl.set(target, { clearProps: "clipPath" }, 0.72);
656
+ tl.call(
657
+ () => {
658
+ ripple(box.x + box.w, y, 240, 0.5);
659
+ for (let i = 0; i < 6; i++) {
660
+ glint(box.x + rnd(0, box.w), y);
661
+ }
662
+ idle();
663
+ },
664
+ [],
665
+ 0.72,
666
+ );
667
+ return tl;
668
+ },
669
+ exit() {
670
+ field.removeEmitter(ambient);
671
+ field.release(0.25);
672
+ },
673
+ blast() {
674
+ field.removeEmitter(ambient);
675
+ const { box } = field;
676
+ const y = baseline();
677
+ // The rule lights end to end and the command is thrown skyward.
678
+ for (let i = 0; i < 6; i++) {
679
+ ripple(box.x + rnd(0, box.w), y, 300, 0.6);
680
+ }
681
+ const columns = Math.max(6, Math.round(box.w / 18));
682
+ for (let i = 0; i <= columns; i++) {
683
+ const x = box.x + (box.w * i) / columns;
684
+ strike(x, y - rnd(0, box.h * 0.6), 9, [260, 720]);
685
+ }
686
+ strike(box.x + box.w, y, 40, [300, 900]);
687
+ for (let i = 0; i < 14; i++) {
688
+ glint(box.x + rnd(0, box.w), y + rnd(-box.h, 0));
689
+ }
690
+ },
691
+ idle,
692
+ hover(on) {
693
+ state.hot = on;
694
+ gsap.to(state, {
695
+ rate: on ? EMBER_RATE.hot : EMBER_RATE.idle,
696
+ duration: on ? 0.3 : 0.8,
697
+ overwrite: true,
698
+ });
699
+ if (on) {
700
+ ripple(field.box.x + field.box.w / 2, baseline(), 200, 0.45);
701
+ }
702
+ },
703
+ destroy() {
704
+ field.removeEmitter(ambient);
705
+ gsap.killTweensOf(state);
706
+ },
707
+ };
708
+ },
709
+ };
710
+ ```
711
+
712
+ On copy or submit, `blast()`, kick the block with a short `x`/`y` jitter, and call `idle()` about a second later.
713
+
714
+ ## Controller contract
715
+
716
+ Pass the selected definition to `attachParticleEffect(wrapper, canvas, target, effect)` from [particle-field.md](particle-field.md). The owner calls the returned controls for the relevant phase and reveals a hidden wrapper once its target is prepared.
717
+
718
+ A new treatment supplies `layer`, `bleed`, and `create(field, target)` returning `enter`, `exit`, `blast`, `idle`, `hover`, and `destroy`. Keep it monochrome and inexpensive at rest.