diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..06c6ac0 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,15 @@ +# GSAP — Repository-wide instructions for GitHub Copilot + +When writing or suggesting GSAP (GreenSock Animation Platform) code in this repository: + +- **Imports:** Use `import { gsap } from "gsap"` (or named plugin imports, e.g. `import { ScrollTrigger } from "gsap/ScrollTrigger"`). Register plugins once with `gsap.registerPlugin(ScrollTrigger)` before use. +- **Sequencing:** Prefer `gsap.timeline()` for multi-step animations instead of chained `delay` values. Use the position parameter (e.g. `"+=0.5"`, `"<"`, `"label"`) to place tweens on the timeline. +- **Transforms:** Prefer GSAP transform properties (`x`, `y`, `scale`, `rotation`, `xPercent`, `yPercent`) over animating raw CSS `transform` or layout properties (`top`, `left`, `width`, `height`) for movement and scale — better performance and consistent order of operations. +- **Opacity:** Prefer `autoAlpha` over `opacity` for fade in/out so elements get `visibility: hidden` at 0 and do not block clicks. +- **from() / fromTo():** `gsap.from()` animates from the given values to the element’s current state. When **multiple from() or fromTo()** tweens target the same property of the same element, set **immediateRender: false** on the later one(s) so the first tween’s end state is not overwritten before it runs. +- **Scroll-based animation:** When scroll-driven or scroll-linked animation is requested, use ScrollTrigger (register the plugin, then use `scrollTrigger: { trigger, start, end, scrub }` or attach to a timeline). Do **not** put a ScrollTrigger on a tween that is a child of a timeline — put it on the timeline or a top-level tween. +- **ScrollTrigger:** Use **scrub** for scroll-linked progress or **toggleActions** for discrete play/reverse, not both. Call **ScrollTrigger.refresh()** after DOM/layout changes that affect trigger positions. Create ScrollTriggers in top-to-bottom page order or set **refreshPriority** so they refresh in that order. +- **React:** In React projects, prefer `useGSAP()` (from `@gsap/react`) or `gsap.context()` with cleanup so animations and ScrollTriggers are reverted when the component unmounts. +- **Cleanup:** When elements are removed or routes change (e.g. SPAs), kill associated ScrollTrigger instances or revert SplitText/Draggable so nothing runs on stale elements. Use **clearProps** when a tween should not leave inline styles after it completes (e.g. so CSS classes can take over). + +**More detail:** The `skills/` directory in this repo contains full SKILL.md guidance (core, timeline, ScrollTrigger, plugins, React, performance). For agents that support the Agent Skills format (Cursor, Claude Code, etc.), install this repo as a skill for the complete reference. diff --git a/.github/instructions/react.instructions.md b/.github/instructions/react.instructions.md new file mode 100644 index 0000000..b58dd30 --- /dev/null +++ b/.github/instructions/react.instructions.md @@ -0,0 +1,15 @@ +--- +applyTo: ["**/*.tsx", "**/*.jsx", "**/components/**"] +--- + +# GSAP in React — path-specific instructions + +When writing or suggesting GSAP code in React/JSX/TSX files: + +- Prefer **useGSAP()** from `@gsap/react` over raw `useEffect` for GSAP setup. Register the hook once: `gsap.registerPlugin(useGSAP)` (and any other plugins used). +- Pass a **scope** (ref to container) so selectors are scoped: `useGSAP(() => { ... }, { scope: containerRef })`. This avoids animating elements outside the component. +- When targeting by ref, pass the **DOM element** (e.g. `ref.current`), not the ref object. Wrong: `gsap.to(myRef, ...)`. Right: `gsap.to(myRef.current, ...)`. With useGSAP, the scope ref limits selectors to that subtree. +- Rely on useGSAP’s automatic **cleanup** on unmount (reverts animations and kills ScrollTriggers). Do not leave ScrollTriggers or timelines running after unmount. +- Use **contextSafe** (returned from useGSAP) to wrap callbacks like `onComplete` so they no-op after unmount and avoid React state-update warnings. +- By default useGSAP runs once (empty dependency array). To re-run when deps change, pass `{ dependencies: [dep1, dep2] }` or `{ revertOnUpdate: true }` as the second argument. Put animation logic in the callback, not in a separate effect. +- If not using useGSAP, use **gsap.context()** in useEffect and return a cleanup that calls `ctx.revert()`. diff --git a/.github/instructions/scrolltrigger.instructions.md b/.github/instructions/scrolltrigger.instructions.md new file mode 100644 index 0000000..f51c055 --- /dev/null +++ b/.github/instructions/scrolltrigger.instructions.md @@ -0,0 +1,18 @@ +--- +applyTo: ["**/*scroll*", "**/*ScrollTrigger*", "**/scroll*"] +--- + +# ScrollTrigger — path-specific instructions + +When writing or suggesting scroll-linked GSAP code (ScrollTrigger): + +- Register the plugin once: `gsap.registerPlugin(ScrollTrigger)`. +- Use **scrub: true** (or a number) for scroll-driven progress; use **toggleActions** for discrete play/reverse. Do not use both on the same trigger. +- Put ScrollTrigger on the **timeline** or a **top-level tween**, not on a tween that is a child of a timeline (wrong: `tl.to(..., { scrollTrigger: {... } })`; right: `gsap.timeline({ scrollTrigger: {... } }).to(...)`). +- When **pinning**, do not animate the pinned element itself; animate its children. Use **pinSpacing: true** (default) so layout does not collapse. +- Prefer **transform** properties (`x`, `y`, `scale`, `rotation`) for the animated element; avoid animating layout properties when possible. +- For “fake” horizontal scroll (pin a section, content moves horizontally while scrolling vertically), use **containerAnimation** and set **ease: "none"** on the horizontal tween so scroll and position stay in sync. +- **start** / **end** format: `"triggerPosition viewportPosition"` (e.g. `"top center"`, `"bottom top"`). Use `endTrigger` if the end is based on a different element. +- Call **ScrollTrigger.refresh()** after DOM or layout changes that affect trigger positions (e.g. new content, fonts loaded). Viewport resize is handled automatically. +- Create ScrollTriggers in **top-to-bottom** page order, or set **refreshPriority** so they refresh in that order when creation order differs. +- When removing elements or changing routes (SPAs), **kill** ScrollTrigger instances: `ScrollTrigger.getAll().forEach(t => t.kill())` or kill by id. diff --git a/README.md b/README.md index cc00695..e520ed3 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,14 @@ Clone this repo and copy the skill folders into the appropriate directory for yo gsap-skills/ README.md AGENTS.md # Guidance for agents editing this repo + .github/ + copilot-instructions.md # Repo-wide instructions for GitHub Copilot + instructions/ # Path-specific Copilot instructions + react.instructions.md + scrolltrigger.instructions.md + .claude-plugin/ # Claude Code plugin config (plugin.json, marketplace.json) + .cursor-plugin/ # Cursor plugin config (plugin.json, marketplace.json) + assets/ # Logo and icon assets (e.g. gsap-green.svg, gsap-icon-square.svg) skills/ gsap-core/ SKILL.md gsap-timeline/ SKILL.md @@ -68,10 +76,22 @@ gsap-skills/ gsap-performance/ SKILL.md ``` +## GitHub Copilot + +**Copilot does not automatically use Cursor/Claude skill files.** To give GitHub Copilot GSAP-aware guidance in a repo that uses this project (or in this repo), use Copilot’s own instruction files: + +- **Repository-wide:** [`.github/copilot-instructions.md`](.github/copilot-instructions.md) — imports, timelines, transforms, from()/fromTo(), ScrollTrigger, React, cleanup. +- **Path-specific:** [`.github/instructions/`](.github/instructions/) — `react.instructions.md` (React/JSX/TSX), `scrolltrigger.instructions.md` (scroll-related files). These apply when the file path matches the configured globs. + +The instruction files are kept **concise and actionable** (bullets, not full reference) so Copilot can use them reliably; GitHub’s code review feature only uses the first 4,000 characters of each file. For full guidance (config tables, examples, Do Not / Best practices), use the **skills** in `skills/` with an agent that supports the Agent Skills format (Cursor, Claude Code, etc.), or install this repo as a skill. + +Copy or adapt the `.github/` files into your repository so Copilot uses them. See [GitHub’s custom instructions docs](https://docs.github.com/en/copilot/concepts/response-customization). + ## Compatibility - **[skills CLI](https://github.com/vercel-labs/skills)** — installs into Cursor, Claude Code, Codex, Windsurf, GitHub Copilot, and [40+ agents](https://github.com/vercel-labs/skills#supported-agents) - **[Agent Skills spec](https://agentskills.io/specification.md)** — SKILL.md format and discovery +- **GitHub Copilot** — use `.github/copilot-instructions.md` and optional `.github/instructions/*.instructions.md` (included in this repo) ## License diff --git a/skills/gsap-core/SKILL.md b/skills/gsap-core/SKILL.md index 3794899..403b96e 100644 --- a/skills/gsap-core/SKILL.md +++ b/skills/gsap-core/SKILL.md @@ -12,6 +12,29 @@ Apply when writing or reviewing GSAP animations that use the core engine: single **Context:** GSAP was acquired by Webflow and powers **Webflow Interactions**. Code generated or run by Webflow’s interaction system is GSAP-based; when users ask about Webflow animations or interactions not behaving as expected, GSAP docs and patterns (e.g. tweens, ScrollTrigger) are relevant for debugging or customizing. +## When to Use GSAP + +Use GSAP when an application requires: + +- ✅ complex animation sequencing +- ✅ timeline-based animation control +- ✅ performant UI animation +- ✅ scroll-driven animation +- ✅ SVG animation, especially morphing between shapes +- ✅ coordinated animations across multiple elements + +GSAP is particularly useful when animations must be synchronized, interrupted, reversed, or dynamically controlled. + +### Prefer GSAP Instead of CSS Animations When + +CSS animations are useful for very simple transitions. Prefer GSAP when you need: + +- ✅ timeline sequencing +- ✅ runtime control (pause, reverse, seek) +- ✅ complex easing +- ✅ scroll-based animation (ScrollTrigger) +- ✅ dynamic values calculated in JavaScript + ## Core Tween Methods - **gsap.to(targets, vars)** — animate from current state to `vars`. Most common. @@ -172,12 +195,13 @@ Set project-wide Tween defaults with **gsap.defaults()**: gsap.defaults({ duration: 0.6, ease: "power2.out" }); ``` -## Best practices +## Official GSAP best practices - ✅ Use **property names in camelCase** in vars (e.g. `backgroundColor`, `rotationX`). - ✅ Prefer **transform aliases** (`x`, `y`, `scale`, `rotation`, `xPercent`, `yPercent`, etc.) over animating the raw `transform` string; use **autoAlpha** instead of `opacity` for fade in/out when elements should be hidden and non-interactive at 0. -- ✅ Use documented built-in eases; use CustomEase when a custom curve is needed. +- ✅ Use documented built-in eases; use CustomEase only when a custom curve is needed. - ✅ Store the tween/timeline return value when controlling playback (pause, play, reverse, kill). +- ✅ Prefer timelines instead of chaining animations using `delay`. ## Do Not diff --git a/skills/gsap-react/SKILL.md b/skills/gsap-react/SKILL.md index d17592a..2e728a5 100644 --- a/skills/gsap-react/SKILL.md +++ b/skills/gsap-react/SKILL.md @@ -118,7 +118,7 @@ GSAP runs in the browser. Do not call gsap or ScrollTrigger during SSR. ## Best practices -- ✅ Use **useGSAP()** when @gsap/react is available; use **gsap.context()** + **ctx.revert()** in useEffect when it is not. +- ✅ Prefer **useGSAP()** from `@gsap/react` rather than `useEffect()`/`useLayoutEffect()`; use **gsap.context()** + **ctx.revert()** in `useEffect` when `useGSAP` is not an option. - ✅ Use refs for targets and pass a **scope** so selectors are limited to the component. - ✅ Run GSAP only on the client (useGSAP or useEffect); do not call gsap or ScrollTrigger during SSR. diff --git a/skills/gsap-scrolltrigger/SKILL.md b/skills/gsap-scrolltrigger/SKILL.md index 70377ee..2461a6d 100644 --- a/skills/gsap-scrolltrigger/SKILL.md +++ b/skills/gsap-scrolltrigger/SKILL.md @@ -267,7 +267,7 @@ ScrollTrigger.getById("my-id")?.kill(); In React, use the `useGSAP()` hook (@gsap/react NPM package) to ensure proper cleanup automatically, or manually kill in a cleanup (e.g. in useEffect return) when the component unmounts. -## Best practices +## Official GSAP best practices - ✅ **gsap.registerPlugin(ScrollTrigger)** once before any ScrollTrigger usage. - ✅ Call **ScrollTrigger.refresh()** after DOM/layout changes (new content, images, fonts) that affect trigger positions. Whenever the viewport is resized, `ScrollTrigger.refresh()` is automatically called (debounced 200ms) diff --git a/skills/gsap-timeline/SKILL.md b/skills/gsap-timeline/SKILL.md index c4c74e7..b7c33ff 100644 --- a/skills/gsap-timeline/SKILL.md +++ b/skills/gsap-timeline/SKILL.md @@ -14,9 +14,9 @@ Apply when building multi-step animations, coordinating several tweens in sequen ```javascript const tl = gsap.timeline(); -tl.to(".a", { x: 100, duration: 1 }); -tl.to(".b", { y: 50, duration: 0.5 }); -tl.to(".c", { opacity: 0, duration: 0.3 }); +tl.to(".a", { x: 100, duration: 1 }) + .to(".b", { y: 50, duration: 0.5 }) + .to(".c", { opacity: 0, duration: 0.3 }); ``` By default, tweens are **appended** one after another. Use the **position parameter** to place tweens at specific times or relative to other tweens. @@ -89,8 +89,9 @@ master.to(".c", { opacity: 0 }, "+=0.2"); - **tl.progress(0.5)** — seek to 50%. - **tl.kill()** — kill timeline and (by default) its children. -## Best practices +## Official GSAP Best practices +- ✅ Prefer timelines for sequencing - ✅ Use the **position parameter** (third argument) to place tweens at specific times or relative to labels. - ✅ Add **labels** with `addLabel()` for readable, maintainable sequencing. - ✅ Pass **defaults** into the timeline constructor so child tweens inherit duration, ease, etc.