Communitygithub.com

motion-patterns

適用於 React / Next.js 的生產級動畫模式 — 按鈕、模態框、通知、交錯、頁面切換、退出動畫、滾動和佈局 — 基於 motion-foundations 的 token 和彈簧。在 React 或 Next.js 中為特定 UI 元素添加動畫時使用。

motion-patterns 是什麼?

Motion Patterns 是一個專為 React 和 Next.js 開發者設計的 AI 助手技能,提供可直接複製貼上的 UI 動畫模式。它涵蓋了最常見的需求,如按鈕回饋、交錯列表、模態框、通知堆疊、頁面切換、滾動揭示、展開卡片和共享元素過渡。所有模式均基於 `motion-foundations` 的設計 token 和彈簧,避免硬編碼數值,確保一致性。此技能強調可訪問性與 SSR 安全性,並在 Next.js App Router 中無縫運作。它不僅提供程式碼範例,還包含嚴格的規則(如 `AnimatePresence` 的 `key` 和 `exit` 必須同時定義),以及常見反模式的修復建議,幫助開發者產出高品質、可維護的動畫程式碼。

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

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Motion Patterns

針對最常見的 UI 動畫需求,提供可複製貼上的模式。 此處的每個模式皆建構於 motion-foundations 的 token 和彈簧之上。 請勿在此定義新的持續時間或緩動值——請從該處匯入。

何時啟用

  • 為按鈕、卡片、模態框或通知加入動畫
  • 建立具有交錯效果(stagger)的列表進場動畫
  • 在 Next.js App Router 中設定頁面切換動畫
  • 為條件性內容添加入場或出場動畫
  • 實作滾動揭示、滾動連結進度或黏性章節區塊
  • 建立展開卡片、手風琴或共享元素過渡

產出

此技能可產出:

  • 針對所有標準 UI 組件,具備可訪問性且 SSR 安全的動畫
  • 以 AnimatePresence 包裹的條件渲染,並包含正確的出場行為
  • Next.js App Router 的頁面切換包裝組件
  • 使用 useScroll + useTransform 的滾動揭示與滾動連結模式
  • 用於展開和交叉淡變元素的佈局動畫模式(layout、layoutId)

原則

  • 每個模式都從 motion-foundations 匯入。不使用原始數值。
  • 每個條件渲染都以 AnimatePresence 包裹,並附帶 key。
  • 出場動畫必須與入場動畫同時定義——絕非事後才想到。
  • layout 僅用於小型、孤立的位移。大型子樹使用明確的 transform。

規則

  1. 始終將條件渲染包裹在 AnimatePresence 中,並在直接子元素上加上 key。沒有 key,出場動畫就不會觸發。
  2. 在定義 initial + animate 時,始終同時定義 exit。沒有出場的動畫是不完整的。
  3. 在頁面切換中使用 mode="wait"。入場必須等待出場完成才能開始。
  4. 不要對超過約 5 個子元素或深層巢狀 DOM 的子樹使用 layout。改用明確的 x/y transform。
  5. 交錯間隔必須保持在 0.05s 到 0.10s 之間。低於此範圍感覺機械化;高於此範圍感覺遲緩。
  6. 模態框必須始終包含: focus trap、Escape 鍵關閉、滾動鎖定、role="dialog"、aria-modal="true"。
  7. 滾動揭示使用 viewport={{ once: true }}。在滾出時重複播放會分散注意力,而非提供資訊。
  8. 所有 token 值都從 motion-foundations 匯入。不使用內聯數值。

決策指引

選擇正確的模式

情況模式
元素出現 / 消失AnimatePresence
項目列表依序載入Stagger variants
在路由之間導航頁面切換包裝組件
元素在原地改變大小layout prop
同一元素在不同頁面上下文中移動layoutId
元素在滾入視埠時出現whileInView
數值與滾動位置連動useScroll + useTransform

何時使用 mode="wait" 與 mode="sync"

模式使用時機
wait頁面切換、內容交換(一次一個)
sync堆疊通知、列表項目(重疊沒問題)
popLayout從重新排版的列表中移除項目

核心概念

AnimatePresence 契約

三件事必須始終成立:

  1. AnimatePresence 包裹條件渲染
  2. 直接子元素有 key
  3. 子元素有 exit prop

缺少任何一項,出場動畫就會無聲失敗。

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}
      />

      {/* 面板 — 無障礙要求:focus trap、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>

端到端範例

一個在掛載時進場、處理條件性存在、並尊重減少動畫偏好的交錯列表——結合了 token、彈簧、AnimatePresence 以及來自 motion-foundations 的無障礙 hook:

"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}>Remove</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>
  )
}

限制 / 非目標

此技能不包含:

  • Token 與彈簧定義 → 請見 motion-foundations
  • 拖曳互動、滑動手勢、可排序列表 → 請見 motion-advanced
  • 文字動畫(單詞/字元揭示、計數器) → 請見 motion-advanced
  • SVG 路徑繪製或形變 → 請見 motion-advanced
  • 自訂動畫 hook → 請見 motion-advanced
  • 不使用 motion/react 的純 CSS 過渡

反模式

反模式違反規則修正方式
AnimatePresence 的子元素缺少 key規則 1為直接子元素加上穩定的 key
定義 initial + animate 但沒有 exit規則 2始終一起定義這三者
頁面切換沒有使用 mode="wait"規則 3在 AnimatePresence 加上 mode="wait"
在一個有 50 個項目的列表上使用 layout規則 4使用 mode="popLayout" 或明確的 transform
在一個 10 項列表上使用 staggerChildren: 0.2規則 5限制在 0.08–0.10
模態框沒有 focus trap規則 6加入 focus-trap-react 或 Radix Dialog
whileInView 沒有搭配 viewport={{ once: true }}規則 7重複進場會分散注意力,而非提供資訊
內聯 transition={{ duration: 0.3 }}規則 8使用 motionTokens.duration.normal

相關技能

  • motion-foundations — 定義此處所有模式匯入的 token、彈簧、useSafeMotion hook 以及 SSR 防護。必須優先設定好。
  • motion-advanced — 透過拖曳、手勢、SVG、文字、自訂 hook 和命令式序列化來擴展這些模式。不會重新定義此技能的任何模式。

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.

相關技能