motion-design
Use this skill when implementing animations, transitions, micro-interactions, or motion design in web applications. Triggers on CSS animations, Framer Motion, GSAP, keyframes, transitions, spring animations, scroll-driven animations, page transitions, loading states, and any task requiring motion or animation implementation.
design animationmotionframer-motiongsapcss-animationstransitionsWhat is motion-design?
Use this skill when implementing animations, transitions, micro-interactions, or motion design in web applications. Triggers on CSS animations, Framer Motion, GSAP, keyframes, transitions, spring animations, scroll-driven animations, page transitions, loading states, and any task requiring motion or animation implementation.
motion-design
motion-design is a production-ready AI agent skill for claude-code, gemini-cli, openai-codex. Implementing animations, transitions, micro-interactions, or motion design in web applications.
Quick Facts
| Field | Value |
|---|---|
| Category | design |
| Version | 0.1.0 |
| Platforms | claude-code, gemini-cli, openai-codex |
| License | MIT |
How to Install
- Make sure you have Node.js installed on your machine.
- Run the following command in your terminal:
npx skills add AbsolutelySkilled/AbsolutelySkilled --skill motion-design- The motion-design skill is now available in your AI coding agent (Claude Code, Gemini CLI, OpenAI Codex, etc.).
Overview
A focused, opinionated knowledge base for implementing animations and motion in web applications. Covers CSS transitions and keyframes, Framer Motion, GSAP, scroll-driven animations, and micro-interactions - with concrete code for each pattern. Every recommendation prioritizes 60fps performance, accessibility, and purposeful motion over decoration.
The difference between good and bad animation is restraint. Most UIs need fewer animations, not more. When motion exists, it must be fast, smooth, and respect user preferences.
Tags
animation motion framer-motion gsap css-animations transitions
Platforms
- claude-code
- gemini-cli
- openai-codex
Related Skills
Pair motion-design with these complementary skills:
Frequently Asked Questions
What is motion-design?
Use this skill when implementing animations, transitions, micro-interactions, or motion design in web applications. Triggers on CSS animations, Framer Motion, GSAP, keyframes, transitions, spring animations, scroll-driven animations, page transitions, loading states, and any task requiring motion or animation implementation.
How do I install motion-design?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill motion-design in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support motion-design?
This skill works with claude-code, gemini-cli, openai-codex. Install it once and use it across any supported AI coding agent.
Maintainers
Generated from AbsolutelySkilled
SKILL.md
Motion Design
A focused, opinionated knowledge base for implementing animations and motion in web applications. Covers CSS transitions and keyframes, Framer Motion, GSAP, scroll-driven animations, and micro-interactions - with concrete code for each pattern. Every recommendation prioritizes 60fps performance, accessibility, and purposeful motion over decoration.
The difference between good and bad animation is restraint. Most UIs need fewer animations, not more. When motion exists, it must be fast, smooth, and respect user preferences.
When to use this skill
Trigger this skill when the user:
- Asks to add animations or transitions to any UI element
- Needs enter/exit animations for components mounting or unmounting
- Wants to implement page transitions or route-change animations
- Asks about Framer Motion, GSAP, or CSS animation APIs
- Needs scroll-driven animations or parallax effects
- Wants loading states with skeleton screens or spinners
- Asks about spring physics, easing curves, or animation timing
- Needs a GSAP timeline for complex multi-step sequences
- Asks about micro-interactions (hover, press, toggle, checkbox states)
Do NOT trigger this skill for:
- Pure CSS layout or styling with no motion (use ultimate-ui instead)
- Canvas or WebGL rendering (use a graphics-specific resource instead)
Key principles
Motion should have purpose - Every animation must communicate something: state change, spatial relationship, feedback, or hierarchy. Decoration-only motion is noise. Ask "what does this animation tell the user?" before adding it.
Respect
prefers-reduced-motion- Always wrap animations in aprefers-reduced-motioncheck. Users with vestibular disorders or epilepsy can be harmed by motion. This is a WCAG 2.1 AA requirement, not a suggestion.Animate transforms and opacity only -
transformandopacityare the only properties the browser can animate on the compositor thread without triggering layout or paint. Animatingwidth,height,top,left,margin, orpaddingcauses jank. Usetransform: scale/translateinstead.Spring > linear easing - Natural motion uses physics-based easing, not uniform speed. Spring animations feel alive.
linearfeels robotic. Useease-outfor entrances,ease-infor exits, spring/bounce for interactive elements that respond to user input.60fps or nothing - If an animation drops frames, remove it. A janky animation is worse than no animation. Test on a throttled CPU (4x slowdown in Chrome DevTools). If it drops below 60fps, simplify or cut it.
Core concepts
Animation properties
- Duration: 100-150ms for micro (button hover), 200-300ms for UI (modal, dropdown), 300-500ms for layout (page transitions). Never over 500ms for interactive feedback.
- Easing:
ease-out(fast start, soft land) for elements entering the screen.ease-in(slow start, fast end) for elements leaving.ease-in-outfor elements moving across the screen. Spring for interactive/playful elements. - Delay: Use sparingly. Stagger children by 50-75ms max. Total stagger sequence should not exceed 400ms or users feel they are waiting.
CSS vs JS animations - decision guide
- Use CSS transitions for simple state changes triggered by class or pseudo-class (hover, focus, active). Zero JS overhead.
- Use CSS keyframes for looping animations (spinners, pulses) and choreographed sequences not tied to interaction.
- Use Framer Motion (React) for enter/exit animations tied to component mount/unmount, gesture-driven motion, or layout animations.
- Use GSAP for complex multi-step timelines, SVG path animations, scroll-triggered sequences, or when you need precise programmatic control.
Spring physics
A spring has two key parameters: stiffness (how fast it accelerates) and
damping (how quickly it settles). High stiffness + high damping = snappy.
Low stiffness + low damping = bouncy and slow. For UI: stiffness 300-500,
damping 25-35 gives a natural feel without excessive bounce.
Performance - compositor vs main thread
The browser renders in two stages: main thread (layout, paint) and compositor
thread (transform, opacity). Animations on the compositor thread run at 60fps
even when the main thread is busy. Always use transform and opacity. Add
will-change: transform only for elements you know will animate - overusing
will-change wastes GPU memory.
Common tasks
CSS transitions and keyframes
/* Reusable easing tokens */
:root {
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--duration-fast: 100ms;
--duration-normal: 200ms;
--duration-slow: 300ms;
}
/* Fade in up - content appearing */
@keyframes fade-in-up {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
/* Scale in - modals, popovers */
@keyframes scale-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
.modal {
animation: scale-in var(--duration-normal) var(--ease-out);
}
/* Card hover - lift effect */
.card {
transition: transform var(--duration-fast) var(--ease-out),
box-shadow var(--duration-fast) var(--ease-out);
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}Framer Motion enter/exit animations
import { motion, AnimatePresence } from 'framer-motion';
// Reusable animation variants
const fadeUp = {
initial: { opacity: 0, y: 12 },
animate: { opacity: 1, y: 0 },
exit: { opacity: 0, y: -8 },
transition: { duration: 0.2, ease: [0, 0, 0.2, 1] },
};
const scaleIn = {
initial: { opacity: 0, scale: 0.95 },
animate: { opacity: 1, scale: 1 },
exit: { opacity: 0, scale: 0.95 },
transition: { duration: 0.15, ease: [0, 0, 0.2, 1] },
};
// Component with enter/exit
function Notification({ show, message }: { show: boolean; message: string }) {
return (
<AnimatePresence>
{show && (
<motion.div
key="notification"
{...fadeUp}
className="toast"
>
{message}
</motion.div>
)}
</AnimatePresence>
);
}
// Staggered list
function AnimatedList({ items }: { items: string[] }) {
return (
<motion.ul
initial="hidden"
animate="visible"
variants={{
hidden: {},
visible: { transition: { staggerChildren: 0.06 } },
}}
>
{items.map((item) => (
<motion.li
key={item}
variants={{
hidden: { opacity: 0, x: -12 },
visible: { opacity: 1, x: 0, transition: { duration: 0.2 } },
}}
>
{item}
</motion.li>
))}
</motion.ul>
);
}Scroll-driven animations with CSS
/* Native CSS scroll-driven animations (Chrome 115+) */
@keyframes reveal {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.scroll-reveal {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 25%;
}
/* Progress bar tied to page scroll */
.scroll-progress {
position: fixed;
top: 0;
left: 0;
height: 3px;
background: var(--color-primary-500);
transform-origin: left;
animation: scaleX linear;
animation-timeline: scroll(root);
}
@keyframes scaleX {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
/* IntersectionObserver fallback for broader browser support */// IntersectionObserver - works in all browsers
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('in-view');
observer.unobserve(entry.target); // animate once
}
});
},
{ threshold: 0.15 }
);
document.querySelectorAll('[data-reveal]').forEach((el) => observer.observe(el));[data-reveal] {
opacity: 0;
transform: translateY(16px);
transition: opacity 0.3s var(--ease-out), transform 0.3s var(--ease-out);
}
[data-reveal].in-view {
opacity: 1;
transform: translateY(0);
}Page transitions with AnimatePresence
import { AnimatePresence, motion } from 'framer-motion';
import { usePathname } from 'next/navigation';
const pageVariants = {
initial: { opacity: 0, y: 8 },
animate: { opacity: 1, y: 0, transition: { duration: 0.25, ease: [0, 0, 0.2, 1] } },
exit: { opacity: 0, y: -8, transition: { duration: 0.15, ease: [0.4, 0, 1, 1] } },
};
export function PageTransition({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
return (
<AnimatePresence mode="wait">
<motion.div key={pathname} {...pageVariants}>
{children}
</motion.div>
</AnimatePresence>
);
}Use
mode="wait"so the exiting page fully animates out before the new one enters.mode="sync"(default) can cause overlap. Keep page transitions under 250ms - users are waiting to see new content.
Micro-interactions and GSAP timelines
For detailed micro-interaction patterns (hover, press, toggle, accordion) and GSAP timeline examples (hero sequences, scroll-triggered cards), see references/advanced-patterns.md.
Respect prefers-reduced-motion
/* CSS - blanket rule as safety net */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}// Framer Motion - useReducedMotion hook
import { useReducedMotion } from 'framer-motion';
function AnimatedCard({ children }: { children: React.ReactNode }) {
const prefersReduced = useReducedMotion();
return (
<motion.div
initial={prefersReduced ? false : { opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={prefersReduced ? { duration: 0 } : { duration: 0.2 }}
>
{children}
</motion.div>
);
}// Vanilla JS - check preference before running GSAP
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!prefersReduced) {
animateHero();
}Anti-patterns
| Mistake | Why it's wrong | What to do instead |
|---|---|---|
Animating width, height, top, or left |
Triggers layout recalculation every frame, causes jank | Use transform: scale() or transform: translate() instead |
transition: all |
Catches unexpected properties, hard to predict, performance risk | List specific properties: transition: transform 200ms, opacity 200ms |
| Duration over 500ms for interactive feedback | Users feel the UI is lagging or broken | Keep button/hover/toggle under 200ms, modal under 300ms |
| Using GSAP for simple hover effects | Massive overhead for something CSS handles natively | Use CSS transition for state changes, GSAP for timelines only |
| Stagger delay total over 500ms | Users wait for content instead of seeing it appear | Cap per-item delay at 75ms, total stagger at 400ms |
will-change: transform on everything |
Each will-change creates a GPU layer - excessive use wastes VRAM |
Only add to elements you know will animate, remove after animation |
Gotchas
AnimatePresencerequires a stablekeyprop on its direct child - Without a uniquekey, Framer Motion cannot differentiate between the exiting and entering component, so exit animations never play. Thekeymust change when the content changes (e.g.,key={pathname}for page transitions,key={item.id}for list items). Usingkey={Math.random()}or omitting it are the two most common causes of broken exit animations.CSS scroll-driven animations (
animation-timeline: scroll()) have no Safari support as of early 2026 - The native CSS scroll timeline API is Chromium-only. Shipping it without anIntersectionObserverfallback means Safari users see no scroll animations at all. Always implement theIntersectionObserverapproach as the baseline and treat scroll-driven CSS as progressive enhancement.will-change: transformon many elements simultaneously tanks GPU memory - Each element withwill-changegets promoted to its own GPU layer. Applying it to 20+ card elements, a background, a header, and navigation simultaneously can exhaust GPU memory on low-end devices and cause more jank than having nowill-changeat all. Apply it only immediately before an animation starts (via JS class add/remove) and remove it after the animation ends.Framer Motion
layoutanimations conflict with CSStransition- When amotionelement has bothlayoutprop and a CSStransitionapplied totransform, the two systems fight over the same property. The CSS transition animates the pre-layout position, while Framer Motion tries to animate to the post-layout position, causing a flash or jump. Remove CSStransition: transformfrom any element that uses the Framer Motionlayoutprop.GSAP timelines in React components leak if not cleaned up - A GSAP timeline created in a
useEffectwithout a cleanup function continues running after the component unmounts, animating elements that no longer exist in the DOM and throwing warnings. Always return a cleanup function fromuseEffectthat callstl.kill()to stop and garbage-collect the timeline.
References
For detailed guidance on specific motion topics, read the relevant file
from the references/ folder:
references/easing-library.md- Easing functions, spring configs, duration guidelines, named presetsreferences/advanced-patterns.md- Micro-interactions (hover, press, toggle, accordion) and GSAP timeline patterns
Only load a references file if the current task requires it - they are long and will consume context.
References
advanced-patterns.md
Advanced Motion Patterns
Micro-interactions - hover, press, toggle
import { motion } from 'framer-motion';
// Button with press feedback
function Button({ children, onClick }: React.ComponentProps<'button'>) {
return (
<motion.button
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.97 }}
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
onClick={onClick}
>
{children}
</motion.button>
);
}
// Animated toggle switch (CSS).toggle {
position: relative;
width: 44px;
height: 24px;
background: var(--color-gray-300);
border-radius: 12px;
transition: background 150ms var(--ease-in-out);
cursor: pointer;
}
.toggle[aria-checked="true"] {
background: var(--color-primary-500);
}
.toggle::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
border-radius: 50%;
background: white;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
transition: transform 200ms var(--ease-spring);
}
.toggle[aria-checked="true"]::after {
transform: translateX(20px);
}
/* Accordion with grid-template-rows trick */
.accordion-content {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 200ms var(--ease-out);
}
.accordion-content[data-open="true"] {
grid-template-rows: 1fr;
}
.accordion-content > div {
overflow: hidden;
}GSAP timeline for complex sequences
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
// Hero entrance sequence
function animateHero() {
const tl = gsap.timeline({ defaults: { ease: 'power2.out' } });
tl.from('.hero-badge', { opacity: 0, y: 16, duration: 0.4 })
.from('.hero-headline', { opacity: 0, y: 20, duration: 0.5 }, '-=0.2')
.from('.hero-subtext', { opacity: 0, y: 16, duration: 0.4 }, '-=0.3')
.from('.hero-cta', { opacity: 0, scale: 0.95, duration: 0.35 }, '-=0.2');
return tl;
}
// Scroll-triggered feature cards
gsap.utils.toArray<HTMLElement>('.feature-card').forEach((card, i) => {
gsap.from(card, {
opacity: 0,
y: 32,
duration: 0.5,
delay: i * 0.08,
ease: 'power2.out',
scrollTrigger: {
trigger: card,
start: 'top 85%',
once: true,
},
});
});The
'-=0.2'offset in GSAP timelines creates overlap between steps for a fluid, cohesive sequence. Without it each step feels disconnected. Overlap by 20-40% of the previous step's duration.
easing-library.md
Easing Library
A reference for easing functions, spring configurations, and duration guidelines. Use this to pick the right motion personality for any interaction.
Named easing presets
These CSS cubic-bezier values cover the full range of UI motion needs.
Define them as custom properties in :root for consistent use across a project.
:root {
/* Standard material-design inspired set */
--ease-standard: cubic-bezier(0.4, 0.0, 0.2, 1); /* elements moving within screen */
--ease-decelerate: cubic-bezier(0.0, 0.0, 0.2, 1); /* elements entering from off-screen */
--ease-accelerate: cubic-bezier(0.4, 0.0, 1.0, 1); /* elements exiting to off-screen */
/* Common shorthand aliases */
--ease-out: cubic-bezier(0.0, 0.0, 0.2, 1); /* same as decelerate */
--ease-in: cubic-bezier(0.4, 0.0, 1.0, 1); /* same as accelerate */
--ease-in-out: cubic-bezier(0.4, 0.0, 0.2, 1); /* same as standard */
/* Expressive / playful */
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); /* gentle overshoot, feels alive */
--ease-bounce: cubic-bezier(0.68, -0.55, 0.27, 1.55); /* strong overshoot, use rarely */
--ease-anticipate: cubic-bezier(0.36, 0, 0.66, -0.56); /* pull back before forward motion */
/* Linear - only for color, opacity at constant speed */
--ease-linear: linear;
}When to use each curve
| Curve | Use case | Avoid for |
|---|---|---|
--ease-decelerate (ease-out) |
Elements entering the screen, dropdowns opening, modals appearing | Exit animations |
--ease-accelerate (ease-in) |
Elements leaving the screen, modals closing | Entrances - feels abrupt |
--ease-standard (ease-in-out) |
Elements moving from one position to another, drawer sliding | Entrances and exits |
--ease-spring |
Buttons, toggles, interactive feedback, anything user-triggered | Long sequences, background transitions |
--ease-bounce |
Celebratory moments (confetti landing, success state), one per page max | Navigation, everyday interactions |
--ease-anticipate |
Menus that "pull back" before expanding, expressive hero elements | Any subtle or professional UI |
linear |
Progress bars, color stops in gradients, opacity at uniform speed | Shape transforms (looks robotic) |
Spring configurations (Framer Motion)
Spring replaces duration + easing with physics parameters. The formula:
stiffness controls acceleration speed, damping controls how quickly
oscillation stops, mass affects inertia.
Named spring presets
export const springs = {
// Snappy - responds immediately, settles fast. Good for buttons, toggles.
snappy: {
type: 'spring' as const,
stiffness: 500,
damping: 35,
mass: 1,
},
// Smooth - gentle acceleration, clean landing. Good for panels, cards.
smooth: {
type: 'spring' as const,
stiffness: 300,
damping: 30,
mass: 1,
},
// Bouncy - visible overshoot. Good for menus, celebratory UI, popovers.
bouncy: {
type: 'spring' as const,
stiffness: 400,
damping: 20,
mass: 0.8,
},
// Slow - heavy, deliberate. Good for drawers, large modals.
slow: {
type: 'spring' as const,
stiffness: 200,
damping: 28,
mass: 1.2,
},
// Instant - effectively no animation. Use when prefers-reduced-motion.
instant: {
type: 'spring' as const,
stiffness: 9999,
damping: 9999,
mass: 1,
},
} as const;Tuning guide
| Parameter | Lower value | Higher value |
|---|---|---|
stiffness |
Slower to start, floaty | Faster to start, snappy |
damping |
More oscillation, bouncy | Less oscillation, settles fast |
mass |
Lighter, faster overall | Heavier, slower overall |
- To make a spring faster without adding bounce: increase stiffness and damping proportionally.
- To make a spring bouncier: lower damping without changing stiffness.
- To make a spring heavier: increase mass, adjust stiffness up to compensate.
Duration guidelines
By interaction type
| Interaction | Duration | Notes |
|---|---|---|
| Button hover/active | 100-150ms | Faster feels more responsive |
| Checkbox / radio | 150ms | Instant-feeling but perceptible |
| Toggle switch thumb | 200ms | Thumb travels visible distance |
| Tooltip appear | 150ms | Longer disappear feels more natural |
| Tooltip disappear | 100ms | |
| Dropdown open | 150-200ms | |
| Dropdown close | 100-150ms | |
| Modal / dialog open | 200-250ms | |
| Modal / dialog close | 150-200ms | Exit faster than enter |
| Drawer / sheet open | 250-350ms | Larger surface needs more time |
| Drawer / sheet close | 200-250ms | |
| Toast / notification enter | 200ms | |
| Toast / notification exit | 150ms | |
| Accordion expand | 200-250ms | |
| Accordion collapse | 150-200ms | |
| Page / route transition | 200-300ms | Keep short - users wait |
| Skeleton pulse loop | 1500ms | Slow pulse feels calmer |
| Loading spinner rotation | 750-900ms | |
| Scroll reveal | 250-350ms | |
| Stagger per child | 50-75ms delay | Cap total stagger at 400ms |
Rules of thumb
- Enter animations are always slower than exit animations for the same element. Exits should feel quick and decisive.
- When in doubt, go faster. 200ms feels fine to the designer; 300ms feels sluggish to the user.
- Duration above 500ms is only acceptable for page-level transitions or carefully choreographed marketing sequences. Never for interactive UI feedback.
GSAP named eases
GSAP uses string names for its easing library. Key ones to know:
// Standard eases
'power1.out' // gentle ease-out, similar to CSS ease
'power2.out' // medium ease-out, good default for most UI
'power3.out' // strong ease-out, snappy and decisive
'power4.out' // very strong, reserve for dramatic entrances
// Natural motion
'expo.out' // starts extremely fast, glides to rest - hero animations
'circ.out' // circular, very smooth deceleration
// Playful
'back.out(1.7)' // slight overshoot on arrival - menus, popovers
'elastic.out(1, 0.3)' // spring-like oscillation - celebrations only
'bounce.out' // physical bounce - very expressive, use once per page
// Exits
'power2.in' // standard exit acceleration
'power3.in' // strong exit, dramatic
// Symmetric
'power2.inOut' // smooth symmetric - elements moving across screen
'sine.inOut' // very gentle S-curve - subtle background animationsCSS scroll-driven animation ranges
With native CSS scroll-driven animations (animation-timeline: scroll() or
animation-timeline: view()), animation-range controls when the animation
starts and ends relative to the scroll position.
/* Reveal as element enters viewport */
.reveal {
animation: fade-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 30%;
}
/* Parallax effect tied to full page scroll */
.parallax-bg {
animation: parallax linear;
animation-timeline: scroll(root);
animation-range: 0% 100%;
}
/* Sticky header fades in after scrolling 100px */
.sticky-header {
animation: fade-in linear both;
animation-timeline: scroll(root);
animation-range: 80px 140px;
}Range keywords
| Keyword | Meaning |
|---|---|
entry 0% |
Element's top edge enters viewport |
entry 100% |
Element's bottom edge enters viewport |
exit 0% |
Element's top edge exits viewport |
exit 100% |
Element's bottom edge exits viewport |
cover 0% |
Element starts covering the viewport |
cover 100% |
Element stops covering the viewport |
Browser support: Chrome 115+, Edge 115+. Use
@supportsor IntersectionObserver as a fallback for Safari and Firefox until support improves.
Frequently Asked Questions
What is motion-design?
Use this skill when implementing animations, transitions, micro-interactions, or motion design in web applications. Triggers on CSS animations, Framer Motion, GSAP, keyframes, transitions, spring animations, scroll-driven animations, page transitions, loading states, and any task requiring motion or animation implementation.
How do I install motion-design?
Run npx skills add AbsolutelySkilled/AbsolutelySkilled --skill motion-design in your terminal. The skill will be immediately available in your AI coding agent.
What AI agents support motion-design?
motion-design works with claude-code, gemini-cli, openai-codex. Install it once and use it across any supported AI coding agent.