{
    "name": "motion-foundations",
    "version": "1.0.0",
    "description": "Motion tokens, spring presets, performance rules, device adaptation, accessibility enforcement, and SSR safety for React / Next.js using motion/react. Foundation layer — all other motion skills depend on this. Use when setting up motion tokens, spring presets, reduced-motion handling, or SSR-safe animation in React or Next.js.",
    "system_prompt": "name motion-foundations description Motion tokens, spring presets, performance rules, device adaptation, accessibility enforcement, and SSR safety for React / Next.js using motion/react. Foundation layer — all other motion skills depend on this. Use when setting up motion tokens, spring presets, reduced-motion handling, or SSR-safe animation in React or Next.js. tags [\"motion\",\"animation\",\"performance\",\"accessibility\"] category frontend author jeff metadata {\"version\":\"1.0.0\"} Motion Foundations The base layer of the motion system. Defines every value, constraint, and rule that downstream skills ( motion-patterns , motion-advanced ) inherit. Load this skill before any animation work begins. When to Activate Starting any animated component from scratch Setting up tokens, spring presets, or easing values Implementing prefers-reduced-motion support Debugging hydration mismatches from animation initial states Evaluating whether an animation should exist at all Outputs This skill produces: A shared motionTokens object (duration, easing, distance, scale) A shared springs preset map (5 named configs) A shouldAnimate() gate used by all components Accessibility-compliant animation defaults via useReducedMotion SSR-safe initial states with zero hydration warnings Principles Motion must do at least one of the following or it must be removed: Guide attention Communicate state Preserve spatial continuity Responsiveness always outranks smoothness. A 60 fps animation that causes input delay is worse than no animation. Rules These are non-negotiable. They apply to every component in the system. Use motion/react only. Never import from framer-motion . Never mix the two in the same tree. initial must match server output. If the server renders opacity: 1 , the initial prop must also be opacity: 1 . No exceptions. Reduced motion overrides everything. When useReducedMotion() returns true or prefersReduced is true , all transforms are disabled. Opacity-only fades at ≤ 0.2s are the only permitted fallback. Never animate layout properties. width , height , top , left , margin , padding are banned from animate . Use transform and opacity only. All token values come from motionTokens . Hardcoded durations and easings in component files are forbidden. All spring configs come from the springs map. Inline stiffness / damping values are forbidden. \"use client\" is required on every file that imports from motion/react . Never read window or navigator at module level. Always guard with typeof window !== \"undefined\" . Decision Guidance Choosing a duration Token Use when instant Tooltip show/hide, focus ring, badge update fast Button feedback, icon swap, chip toggle normal Modal open, card expand, page element enter slow Hero entrance, full-page transition crawl Deliberate storytelling; use sparingly Choosing a spring Preset Use when snappy Default UI — buttons, chips, nav items gentle Cards, modals, panels landing softly bouncy Playful moments — empty states, onboarding instant Tooltips, popovers, dropdowns release Drag release — natural physics feel When to disable animation entirely Disable (make shouldAnimate() return false ) when: prefersReduced is true isLowEnd is true and the animation is non-essential The element is off-screen and will never enter the viewport The animation is purely decorative with no UX purpose Core Concepts Token system // lib/motion-tokens.ts export const motionTokens = { duration : { instant : 0.08 , fast : 0.18 , normal : 0.35 , slow : 0.6 , crawl : 1.0 , }, easing : { smooth : [ 0.22 , 1 , 0.36 , 1 ], sharp : [ 0.4 , 0 , 0.2 , 1 ], bounce : [ 0.34 , 1.56 , 0.64 , 1 ], linear : [ 0 , 0 , 1 , 1 ], }, distance : { xs : 4 , sm : 8 , md : 16 , lg : 24 , xl : 48 , }, scale : { subtle : 0.98 , press : 0.95 , pop : 1.04 , }, } export const springs = { snappy : { type : \"spring\" , stiffness : 300 , damping : 30 }, gentle : { type : \"spring\" , stiffness : 120 , damping : 14 }, bouncy : { type : \"spring\" , stiffness : 400 , damping : 10 }, instant : { type : \"spring\" , stiffness : 600 , damping : 35 }, release : { type : \"spring\" , stiffness : 200 , damping : 20 , restDelta : 0.001 }, } Runtime flags // lib/motion-config.ts export const motionConfig = { isLowEnd ( ) { return ( typeof navigator !== \"undefined\" && navigator. hardwareConcurrency <= 4 ) }, prefersReduced ( ) { return ( typeof window !== \"undefined\" && window . matchMedia ( \"(prefers-reduced-motion: reduce)\" ). matches ) }, shouldAnimate ( { essential = false } = {} ) { if ( this . prefersReduced ()) return false if (!essential && this . isLowEnd ()) return false return true }, duration ( ) { return this . isLowEnd () || this . prefersReduced () ? motionTokens. duration . instant : motionTokens. duration . normal }, } Accessibility Priority order (highest to lowest): prefers-reduced-motion: reduce — disables all transforms, limits opacity transitions to ≤ 0.2s Low-end device detection — reduces duration, removes non-essential animations Design preference — everything else Motion must degrade gracefully. It must never disappear abruptly in a way that causes layout shift or confuses orientation. // hooks/use-reduced-motion.tsx \"use client\" import { useReducedMotion } from \"motion/react\" export function useSafeMotion ( fullY : number = 16 ) { const reduce = useReducedMotion () return { initial : { opacity : 0 , y : reduce ? 0 : fullY }, animate : { opacity : 1 , y : 0 }, exit : { opacity : 0 , y : reduce ? 0 : -fullY }, } } /* globals.css */ @media ( prefers-reduced-motion : reduce) { .motion-safe-transition { transition : opacity 0.15s ; } .motion-reduce-transform { transform : none !important ; } } <!-- Tailwind --> < div class = \"motion-safe:animate-fade motion-reduce:opacity-100\" > </ div > SSR / hydration safety Rule: initial must always match what the server renders. // WRONG — server renders opacity:1 but initial says 0 → hydration mismatch <motion. div initial={{ opacity : 0 }} animate={{ opacity : 1 }} /> // CORRECT — use AnimatePresence or defer to client mount \"use client\" const [mounted, setMounted] = useState ( false ) useEffect ( () => setMounted ( true ), []) <motion. div initial={{ opacity : mounted ? 0 : 1 }} animate={{ opacity : 1 }} /> Code Examples End-to-end: tokens + springs + accessibility + SSR guard // components/fade-in-card.tsx \"use client\" import { useState, useEffect } from \"react\" import { motion } from \"motion/react\" import { motionTokens, springs } from \"@/lib/motion-tokens\" import { useSafeMotion } from \"@/hooks/use-reduced-motion\" import { motionConfig } from \"@/lib/motion-config\" interface FadeInCardProps { children : React . ReactNode delay ?: number } export function FadeInCard ( { children, delay = 0 }: FadeInCardProps ) { // SSR guard — initial must match server output (opacity: 1) const [mounted, setMounted] = useState ( false ) useEffect ( () => setMounted ( true ), []) // Accessibility — disables transform when reduced motion is preferred const safeMotion = useSafeMotion (motionTokens. distance . md ) // Device gate — skip animation on low-end hardware if (!motionConfig. shouldAnimate () || !mounted) { return < div > {children} </ div > } return ( < motion.div initial = {safeMotion.initial} animate = {safeMotion.animate} exit = {safeMotion.exit} transition = {{ ...springs.gentle , delay , }} whileHover = {{ scale: motionTokens.scale.pop }} whileTap = {{ scale: motionTokens.scale.press }} > {children} </ motion.div > ) } Constraints / Non-Goals This skill does not cover: UI component patterns (button, modal, stagger) → see motion-patterns Drag, gestures, SVG, text animations, custom hooks → see motion-advanced CSS-only animations or Tailwind animate-* classes without motion/react Third-party animation libraries (GSAP, anime.js, etc.) Motion design decisions (when to animate, what to emphasize) — that is a design concern, not a code constraint Anti-Patterns Anti-pattern Rule violated Fix import { motion } from \"framer-motion\" Rule 1 Use motion/react initial={{ opacity: 0 }} on SSR component Rule 2 Add mount guard Skipping useReducedMotion check Rule 3 Use useSafeMotion hook animate={{ width: \"100%\" }} Rule 4 Use scaleX transform instead transition={{ duration: 0.4 }} inline Rule 5 Use motionTokens.duration.normal { stiffness: 300, damping: 30 } inline Rule 6 Use springs.snappy Missing \"use client\" directive Rule 7 Add to top of file navigator.hardwareConcurrency at module level Rule 8 Wrap in typeof navigator !== \"undefined\" Related Skills motion-patterns — consumes tokens and springs defined here to build button, modal, stagger, page transition, and scroll patterns. Does not redefine any values. motion-advanced — consumes tokens and springs defined here for drag, SVG, text, and gesture patterns. Adds useAnimate sequences and custom hooks on top of this foundation.",
    "model_config": {
        "provider": "deepseek",
        "model": "deepseek-chat",
        "temperature": 0.7,
        "max_tokens": 4096,
        "top_p": 0.9
    },
    "trigger_words": [],
    "source": "DeepseekModel",
    "source_url": "https://deepseekmodel.com/skill?id=affaan-m-ecc-skills-motion-foundations-skill-md"
}