7 Forbidden Animation Patterns in Taste Skill: A Complete Guide to Motion Anti-Patterns
Taste Skill explicitly bans seven specific animation anti-patterns—including infinite-loop micro-animations, raw scroll listeners, and unmotivated "cool-look" effects—to enforce performant, accessible, and purposeful UI motion.
Taste Skill is a strict front-end motion specification defined in skills/taste-skill/SKILL.md within the Leonxlnx/taste-skill repository. These forbidden animation patterns live in Section 5.D and are designed to prevent GPU waste, layout thrashing, and accessibility violations. Violating any of these rules triggers a pre-flight build failure.
The Seven Forbidden Animation Patterns
Infinite-Loop Micro-Animations Everywhere
Taste Skill prohibits endless pulsing, rotating, or floating elements that never stop. According to Section 1 of SKILL.md (line 39), these create visual noise, waste GPU cycles, and break the "motion must be motivated" rule. Any perpetual motion without user interaction or state change is considered a violation.
Raw Window Scroll Event Listeners
Using window.addEventListener("scroll", …) is banned at line 511 of SKILL.md. This pattern runs on every scroll frame, causes jank, and bypasses React batching. The specification mandates using Motion’s useScroll(), GSAP ScrollTrigger, IntersectionObserver, or native CSS scroll-driven animations instead.
Scroll-Progress Calculations in React State
Storing window.scrollY in React state (lines 512-513) forces full component re-renders on every pixel scroll, crushing FPS on mobile devices. The skill requires leveraging Motion values (useMotionValue, useTransform) to handle scroll progress without triggering React’s render cycle.
requestAnimationFrame State Updates
Writing to React state inside requestAnimationFrame loops (lines 513-514) creates the same re-render bottleneck as scroll listeners. Taste Skill enforces using Motion values for imperative animation updates, keeping calculations off the main thread and out of React’s state management.
Layout Transition Misuse on Static Content
Wrapping purely static markup in layout or layoutId props "just to look fancy" is forbidden (lines 514-515). This adds unnecessary measurement work for the browser’s layout engine without any real UI benefit or state transition purpose.
Multiple Horizontal Marquees
The "MARQUEE MAX-ONE-PER-PAGE" rule (line 361) limits kinetic marquee strips to a single instance per page. Multiple marquees are considered lazy filler content that distracts from meaningful hierarchy.
Unmotivated "Cool-Look" Animations
Any animation that cannot be explained in one sentence as serving hierarchy, storytelling, feedback, or state transition is banned (lines 360-362). Taste Skill insists every motion has a clear communication purpose; decorative motion without motivation violates the core "motion must be motivated" principle.
Performance-First Alternatives Allowed in Taste Skill
Motion whileInView for Scroll Reveals
Replace scroll listeners with Motion’s declarative viewport detection:
"use client";
import { motion, useReducedMotion } from "motion/react";
export function RevealStagger({ items }: { items: string[] }) {
const reduce = useReducedMotion();
return (
<ul className="grid gap-6">
{items.map((item, i) => (
<motion.li
key={item}
initial={reduce ? false : { opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{
duration: 0.6,
delay: i * 0.06,
ease: [0.16, 1, 0.3, 1],
}}
>
{item}
</motion.li>
))}
</ul>
);
}
This approach respects prefers-reduced-motion and never touches window.scroll.
GSAP ScrollTrigger for Complex Effects
For imperative scroll orchestration, GSAP ScrollTrigger is the only accepted library (Section 5.A):
"use client";
import { useRef, useEffect } from "react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
export function StickyStack({ cards }: { cards: React.ReactNode[] }) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const ctx = gsap.context(() => {
const sections = gsap.utils.toArray<HTMLElement>(".stack-card");
sections.forEach((el, i) => {
if (i === sections.length - 1) return;
ScrollTrigger.create({
trigger: el,
start: "top top",
pin: true,
pinSpacing: false,
});
gsap.to(el, {
opacity: 0.55,
scale: 0.92,
scrollTrigger: {
trigger: sections[i + 1],
start: "top bottom",
end: "top top",
scrub: true,
},
});
});
}, ref);
return () => ctx.revert();
}, []);
return (
<div ref={ref}>
{cards.map((c, i) => (
<div key={i} className="stack-card min-h-[100dvh] flex items-center justify-center">
{c}
</div>
))}
</div>
);
}
This implementation batches DOM reads/writes and cleans up on unmount.
Single-Use Marquee Implementation
When a marquee is justified by the design narrative, implement exactly one per page:
"use client";
import { motion } from "motion/react";
export function SingleMarquee({ items }: { items: string[] }) {
return (
<motion.div
className="whitespace-nowrap overflow-hidden"
animate={{ x: "-100%" }}
transition={{ repeat: Infinity, ease: "linear", duration: 12 }}
>
{items.map((t, i) => (
<span key={i} className="inline-block mr-8">{t}</span>
))}
</motion.div>
);
}
This satisfies the Section 5.D constraint that only one horizontal marquee may exist per view.
Summary
- Section 5.D of
skills/taste-skill/SKILL.mddefines seven specific animation anti-patterns that cause pre-flight failures. - Infinite loops, raw scroll listeners, and state-based scroll calculations are banned to prevent GPU and main-thread saturation.
- Layout transitions must serve actual UI state changes, not static decoration.
- Marquees are capped at one per page to avoid kinetic filler.
- Motion values and GSAP ScrollTrigger are the approved alternatives to React state for scroll-based animations.
- All animations must be motivated by hierarchy, storytelling, feedback, or state transition.
Frequently Asked Questions
What happens if I use a forbidden animation pattern in Taste Skill?
The build system triggers a pre-flight failure. According to the specification in SKILL.md, these rules are enforced by higher-level motion guards that scan for banned patterns like window.addEventListener("scroll") or unmotivated layout transitions before deployment.
Can I use CSS scroll-driven animations with Taste Skill?
Yes. Native CSS scroll-driven animations are explicitly allowed as alternatives to JavaScript scroll listeners. They run on the compositor thread, avoid React re-renders, and satisfy the performance requirements outlined in Section 5.D.
Why does Taste Skill limit marquees to one per page?
Multiple horizontal marquees are classified as lazy filler content that creates visual noise without communication purpose. The "MARQUEE MAX-ONE-PER-PAGE" rule (line 361) enforces editorial discipline, ensuring kinetic motion is used sparingly and with intent.
How do I handle scroll-based animations without window.addEventListener?
Use Motion’s useScroll() hook for React-based scroll tracking, GSAP ScrollTrigger for complex pinning and scrubbing, or CSS scroll-driven animations for native performance. These approaches read scroll position outside React’s render cycle, preventing the FPS drops caused by state updates on every scroll frame.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →