Catalog
affaan-m/motion-foundations

affaan-m

motion-foundations

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.

global
0installs0uses~2.4k
v1.0Saved May 15, 2026

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.

  1. Use motion/react only. Never import from framer-motion. Never mix the two in the same tree.
  2. initial must match server output. If the server renders opacity: 1, the initial prop must also be opacity: 1. No exceptions.
  3. 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.
  4. Never animate layout properties. width, height, top, left, margin, padding are banned from animate. Use transform and opacity only.
  5. All token values come from motionTokens. Hardcoded durations and easings in component files are forbidden.
  6. All spring configs come from the springs map. Inline stiffness/damping values are forbidden.
  7. "use client" is required on every file that imports from motion/react.
  8. 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):

  1. prefers-reduced-motion: reduce — disables all transforms, limits opacity transitions to ≤ 0.2s
  2. Low-end device detection — reduces duration, removes non-essential animations
  3. 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"
  • 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.
Files1
1 files · 1.0 KB

Select a file to preview

Overall Score

88/100

Grade

A

Excellent

Safety

90

Quality

88

Clarity

85

Completeness

87

Summary

Motion Foundations is a foundational skill that defines motion tokens, spring presets, performance rules, device adaptation, accessibility enforcement, and SSR safety for React/Next.js using motion/react. It establishes 8 core rules and token systems that all downstream motion skills must inherit and follow, ensuring consistent, performant, and accessible animations across an application.

Detected Capabilities

code generationfile writing to lib/ and hooks/ directoriesreact/nextjs component guidanceconfiguration definitionaccessibility pattern definition

Trigger Keywords

Phrases that MCP clients use to match this skill to user intent.

motion tokens setupprefers-reduced-motionanimation performance rulesspring presetsSSR hydration animationmotion/react foundation

Risk Signals

INFO

Environment-specific runtime checks (navigator.hardwareConcurrency, window.matchMedia)

motion-config.ts code block, rule 8
INFO

Client-side conditional rendering based on mounted state for SSR safety

fade-in-card.tsx example, useEffect guard

Use Cases

  • Setting up a new animated component system from scratch with consistent motion tokens and spring presets
  • Implementing prefers-reduced-motion accessibility support for motion/react components
  • Debugging hydration mismatches caused by animation initial state mismatches on SSR pages
  • Establishing performance guardrails for animations on low-end devices
  • Creating a shared motion design system for a team with enforceable rules and patterns

Quality Notes

  • Excellent documentation structure with clear hierarchy: principles, rules, decision tables, code examples, and anti-patterns
  • Comprehensive decision guidance with tables mapping token choices to use cases (duration table, spring preset table)
  • Strong accessibility integration — priority-ordered rules with `prefers-reduced-motion` enforcement and low-end device detection
  • Well-defined scope boundaries: explicitly lists what the skill does NOT cover (UI patterns, drag/gestures, CSS-only animations)
  • Token system and springs presets are concrete and copy-paste ready with full TypeScript examples
  • Anti-patterns table directly maps violations back to rules, making compliance testable
  • SSR safety guidance is explicit with WRONG vs CORRECT examples showing hydration mismatch risks
  • Related skills section clarifies dependency relationships and prevents scope creep
  • One minor clarity issue: the relationship between `useSafeMotion()` and `useReducedMotion()` from motion/react could be slightly clearer — it's well explained but dense
Model: claude-haiku-4-5-20251001Analyzed: May 15, 2026

Reviews

Add this skill to your library to leave a review.

No reviews yet

Be the first to share your experience.

Add affaan-m/motion-foundations to your library

Command Palette

Search for a command to run...