Communitygithub.com

motion-patterns

为 React / Next.js 提供可直接投入生产的动画模式 —— 按钮、模态框、通知、逐项显示、页面过渡、退出动画、滚动和布局,均基于 motion-foundations 的令牌和弹簧。在 React 或 Next.js 中为特定 UI 元素(按钮、模态框、通知、逐项显示、页面过渡或滚动)添加动画时使用。

motion-patterns 是什么?

Motion Patterns 是一个聚焦于常见 UI 动画需求的技能,为开发者提供了可直接复制粘贴的动画模式。其核心价值在于所有模式均基于 `motion-foundations` 的令牌和弹簧值,禁止直接定义新的时长或缓动值,确保了代码的一致性和可维护性。该技能覆盖了按钮反馈、列表逐项显示、模态框、通知堆栈、页面过渡(Next.js App Router)、滚动揭示、展开卡片、手风琴和共享元素过渡等场景,并附带了大量完整的 TypeScript 代码示例。 该技能适用于 React 和 Next.js 项目,特别是使用 Chemotion 的项目。它强调可访问性(如模态框的焦点陷阱、`role="dialog"`)、SSR 安全性,并严格遵循 `AnimatePresence` 的契约(必须包裹条件渲染、子元素必须有 `key` 和 `exit` 属性)。此外,它还提供了清晰的决策指导和反模式列表,帮助开发者在选择 `AnimatePresence`、`layout`、`layoutId`、`whileInView` 等模式时做出正确选择,并避免常见的动画陷阱。

兼容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/motion-patterns

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

动画模式

针对最常见 UI 动画需求的复制粘贴方案。 这里的每一个模式都基于 motion-foundations 的令牌和弹簧构建。 请勿在此定义新的时长或缓动值——请直接导入它们。

何时激活

  • 为按钮、卡片、模态框或通知添加动画
  • 构建带逐项显示的列表入场
  • 在 Next.js App Router 中设置页面过渡
  • 为条件渲染内容添加入场或出场动画
  • 实现滚动揭示、滚动关联进度或粘性故事章节
  • 构建展开卡片、手风琴或共享元素过渡

输出

此技能可生成:

  • 符合无障碍标准、SSR 安全的所有标准 UI 组件动画
  • 包裹了 AnimatePresence 的条件渲染,并包含正确的退出行为
  • 用于 Next.js App Router 的页面过渡包装组件
  • 使用 useScroll + useTransform 的滚动揭示和滚动关联模式
  • 用于展开和交叉淡入元素的布局动画模式(layout、layoutId)

原则

  • 每个模式都从 motion-foundations 导入。不允许使用原始数字。
  • 每个条件渲染都必须用 AnimatePresence 包裹,并附带一个 key。
  • 退出动画始终与入场动画一起定义——绝不能事后添加。
  • layout 仅用于小型、孤立的尺寸变化。大型子树将使用显式变换。

规则

  1. 始终将条件渲染包裹在带有 key 的 AnimatePresence 中,key 加在直接子元素上。没有键,退出动画永远不会触发。
  2. 在定义 initial + animate 时,始终同时定义 exit。 没有退出动画的动画是不完整的。
  3. 在页面过渡中使用 mode="wait"。 入场必须等到退出完成才能开始。
  4. 不要对包含超过约 5 个子元素或深度嵌套 DOM 的子树使用 layout。 请改用显式的 x/y 变换。
  5. 逐项间隔必须保持在 0.05s 到 0.10s 之间。 低于此值感觉生硬;高于此值感觉迟缓。
  6. 模态框必须始终包含: 焦点陷阱、Escape 键关闭、滚动锁定、role="dialog"、aria-modal="true"。
  7. 滚动揭示使用 viewport={{ once: true }}。 在滚动出视口时重复播放会分散注意力,而非提供信息。
  8. 所有令牌值均从 motion-foundations 导入。 不允许内联数字。

决策指导

选择合适的模式

情况模式
元素出现 / 消失AnimatePresence
列表项按顺序加载逐项变体
在路由间导航页面过渡包装器
元素在原地改变大小layout 属性
同一元素在不同页面上下文中移动layoutId
滚动到视口时元素进入whileInView
值与滚动位置关联useScroll + useTransform

何时使用 mode="wait" 与 mode="sync"

模式使用场景
wait页面过渡、内容切换(一次一个)
sync堆叠通知、列表项(重叠没问题)
popLayout从浮动列表中移除的项目

核心概念

AnimatePresence 契约

必须始终满足三件事:

  1. AnimatePresence 包裹条件渲染
  2. 直接子元素有一个 key
  3. 子元素有一个 exit 属性

缺少其中任何一项,退出动画都会静默失败。

layout 与 layoutId 对比

  • layout —— 动画化元素自身的尺寸/位置变化(在原位)
  • layoutId —— 连接两个独立的元素,在渲染之间交叉淡入

在展开容器内的文本上使用 layout="position",以防止文本回流动画化。

代码示例

按钮反馈

"use client"
import { motion } from "motion/react"
import { springs, motionTokens } from "@/lib/motion-tokens"

<motion.button
  whileHover={{ scale: motionTokens.scale.pop }}
  whileTap={{ scale: motionTokens.scale.press }}
  transition={springs.snappy}
/>

逐项列表

"use client"
import { motion } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"

const container = {
  hidden: {},
  visible: {
    transition: {
      staggerChildren: 0.08,   // 符合 0.05–0.10 规则
      delayChildren: 0.1,
    },
  },
}

const item = {
  hidden:  { opacity: 0, y: motionTokens.distance.md },
  visible: { opacity: 1, y: 0, transition: springs.gentle },
}

<motion.ul variants={container} initial="hidden" animate="visible">
  {items.map((i) => (
    <motion.li key={i.id} variants={item} />
  ))}
</motion.ul>

模态框

"use client"
import { motion, AnimatePresence } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"

// 在调用处包裹:
// <AnimatePresence>{isOpen && <Modal key="modal" />}</AnimatePresence>

export function Modal({ onClose }: { onClose: () => void }) {
  return (
    <>
      {/* 遮罩层 */}
      <motion.div
        className="fixed inset-0 bg-black/50"
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        onClick={onClose}
      />

      {/* 面板 — 无障碍要求:焦点陷阱、Escape 关闭、
          滚动锁定、role="dialog"、aria-modal="true" */}
      <motion.div
        role="dialog"
        aria-modal="true"
        className="fixed inset-x-4 top-1/2 -translate-y-1/2 rounded-xl bg-white p-6"
        initial={{
          opacity: 0,
          scale: motionTokens.scale.press,
          y: motionTokens.distance.sm,
        }}
        animate={{ opacity: 1, scale: 1, y: 0 }}
        exit={{
          opacity: 0,
          scale: motionTokens.scale.press,
          y: motionTokens.distance.sm,
        }}
        transition={springs.gentle}
      />
    </>
  )
}

通知堆栈

"use client"
import { motion, AnimatePresence } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"

<AnimatePresence mode="sync">
  {toasts.map((t) => (
    <motion.div
      key={t.id}
      layout
      initial={{
        opacity: 0,
        x: motionTokens.distance.xl,
        scale: motionTokens.scale.subtle,
      }}
      animate={{ opacity: 1, x: 0, scale: 1 }}
      exit={{
        opacity: 0,
        x: motionTokens.distance.xl,
        scale: motionTokens.scale.subtle,
      }}
      transition={springs.snappy}
    />
  ))}
</AnimatePresence>

页面过渡(Next.js App Router)

// components/page-transition.tsx
"use client"
import { motion, AnimatePresence } from "motion/react"
import { usePathname } from "next/navigation"
import { motionTokens } from "@/lib/motion-tokens"

const variants = {
  initial: { opacity: 0, y: motionTokens.distance.sm },
  enter:   { opacity: 1, y: 0 },
  exit:    { opacity: 0, y: -motionTokens.distance.sm },
}

export function PageTransition({ children }: { children: React.ReactNode }) {
  const pathname = usePathname()
  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={pathname}
        variants={variants}
        initial="initial"
        animate="enter"
        exit="exit"
        transition={{
          duration: motionTokens.duration.normal,
          ease: motionTokens.easing.smooth,
        }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  )
}

滚动揭示

"use client"
import { motion } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"

<motion.div
  initial={{ opacity: 0, y: motionTokens.distance.lg }}
  whileInView={{ opacity: 1, y: 0 }}
  viewport={{ once: true, margin: "-80px" }}   // once: true — 规则 7
  transition={{ duration: motionTokens.duration.slow, ease: motionTokens.easing.smooth }}
/>

滚动进度条

"use client"
import { motion, useScroll } from "motion/react"

export function ScrollProgress() {
  const { scrollYProgress } = useScroll()
  return (
    <motion.div
      className="fixed top-0 left-0 h-1 bg-indigo-500 origin-left w-full"
      style={{ scaleX: scrollYProgress }}
    />
  )
}

展开卡片

"use client"
import { useState } from "react"
import { motion, AnimatePresence } from "motion/react"
import { springs, motionTokens } from "@/lib/motion-tokens"

export function ExpandingCard({ title, body }: { title: string; body: string }) {
  const [expanded, setExpanded] = useState(false)

  return (
    <motion.div layout onClick={() => setExpanded(!expanded)} className="cursor-pointer">
      {/* layout="position" 防止文本回流动画化 */}
      <motion.h2 layout="position" className="font-semibold">
        {title}
      </motion.h2>

      <AnimatePresence>
        {expanded && (
          <motion.p
            key="body"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            transition={{ duration: motionTokens.duration.fast }}
          >
            {body}
          </motion.p>
        )}
      </AnimatePresence>
    </motion.div>
  )
}

共享元素交叉淡入

// 源上下文
<motion.img layoutId="hero-image" src={src} className="w-16 h-16 rounded" />

// 目标上下文(相同的 layoutId —— motion 处理过渡)
<motion.img layoutId="hero-image" src={src} className="w-full rounded-xl" />

手风琴

<motion.div
  initial={false}
  animate={{ opacity: open ? 1 : 0, scaleY: open ? 1 : 0 }}
  style={{ transformOrigin: "top", overflow: "hidden" }}
  transition={{
    duration: motionTokens.duration.normal,
    ease: motionTokens.easing.smooth,
  }}
> {children}
</motion.div>

端到端示例

一个在挂载时进入的逐项列表,处理条件存在,并 尊重减少运动 —— 结合了令牌、弹簧、AnimatePresence 以及 来自 motion-foundations 的无障碍钩子:

"use client"
import { useState } from "react"
import { motion, AnimatePresence } from "motion/react"
import { motionTokens, springs } from "@/lib/motion-tokens"
import { useSafeMotion } from "@/hooks/use-reduced-motion"

const containerVariants = {
  hidden: {},
  visible: {
    transition: { staggerChildren: 0.08, delayChildren: 0.1 },
  },
}

function ListItem({ label, onRemove }: { label: string; onRemove: () => void }) {
  const safe = useSafeMotion(motionTokens.distance.sm)
  return (
    <motion.li
      variants={{
        hidden:  safe.initial,
        visible: safe.animate,
      }}
      exit={safe.exit}
      transition={springs.gentle}
      className="flex items-center justify-between p-3 rounded-lg bg-white shadow-sm"
    >
      <span>{label}</span>
      <button onClick={onRemove}>移除</button>
    </motion.li>
  )
}

export function AnimatedList({ items, onRemove }: {
  items: { id: string; label: string }[]
  onRemove: (id: string) => void
}) {
  return (
    <motion.ul
      variants={containerVariants}
      initial="hidden"
      animate="visible"
      className="space-y-2"
    >
      <AnimatePresence mode="popLayout">
        {items.map((item) => (
          <ListItem
            key={item.id}
            label={item.label}
            onRemove={() => onRemove(item.id)}
          />
        ))}
      </AnimatePresence>
    </motion.ul>
  )
}

约束 / 非目标

此技能 不 包含:

  • 令牌和弹簧定义 → 请见 motion-foundations
  • 拖拽交互、滑动手势、可排序列表 → 请见 motion-advanced
  • 文本动画(单词/字符揭示、计数器) → 请见 motion-advanced
  • SVG 路径绘制或变形 → 请见 motion-advanced
  • 自定义动画钩子 → 请见 motion-advanced
  • 不基于 motion/react 的纯 CSS 过渡

反模式

反模式违反的规则修复
AnimatePresence 的子元素缺少 key规则 1为直接子元素添加稳定的 key
定义了 initial + animate 但没有 exit规则 2始终同时定义三者
页面过渡未使用 mode="wait"规则 3在 AnimatePresence 中添加 mode="wait"
在包含 50 个项目的列表上使用 layout规则 4使用 mode="popLayout" 或显式变换
在包含 10 个项目的列表上使用 staggerChildren: 0.2规则 5限制在 0.08–0.10
模态框没有焦点陷阱规则 6添加 focus-trap-react 或 Radix Dialog
whileInView 未搭配 viewport={{ once: true }}规则 7重复的入场会分散注意力,而非提供信息
行内使用 transition={{ duration: 0.3 }}规则 8使用 motionTokens.duration.normal

相关技能

  • motion-foundations —— 定义了所有令牌、弹簧、useSafeMotion 钩子和 SSR 守卫,此处的每个模式都依赖它们。必须先设置好。
  • motion-advanced —— 通过拖拽、手势、SVG、文本、自定义钩子和命令式排序扩展了这些模式。不会重新定义此技能中的任何模式。

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相关技能