Token导航 LogoToken导航

Agent Skill · 待分类

css-animation-creator(CSS animation creator 视频)

用于辅助视频生成、动画合成、脚本化剪辑或 Remotion 等视频项目开发。它适合让 Agent 组织镜头、生成素材说明、维护合成代码或排查渲染问题。使用时需要确认分辨率、时长、素材路径和导出格式;涉及外部素材、人物肖像或商业发布时,应先核对版权授权和内容审核要求。

css-animation-creator 是待分类类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为只读;许可证信息还不明确,第三方扫描已通过,按安装量在待分类类的 11,209 个 Skill 里排第 989。

总安装 5,998周安装 245GitHub Stars 106下载 1,940核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:css-animation-creator(CSS animation creator 视频)
来源仓库:https://github.com/onewave-ai/claude-skills
仓库路径:skills/css-animation-creator
安装命令:
npx skills add https://github.com/onewave-ai/claude-skills --skill css-animation-creator
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/onewave-ai/claude-skills --skill css-animation-creator

这个 Skill 是做什么的

这个 Skill 的分类还没最终确定,用法以来源仓库的说明为准。

用于创建高性能、无障碍的 CSS 动画,遵循迪士尼动画十二原则。支持过渡、关键帧和 JavaScript 库等多种技术选型。优化渲染性能并确保动效符合用户偏好设置。

安装命令:npx skills add https://github.com/onewave-ai/claude-skills --skill css-animation-creator 适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。

来源仓库内路径:skills/css-animation-creator

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/onewave-ai/claude-skills --skill css-animation…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它标注为只读,先用只读任务试一次,确认行为符合预期再扩大使用范围。

常见问题

6 条

css-animation-creator 怎么安装?

在终端执行 npx skills add https://github.com/onewave-ai/claude-skills --skill css-animation-creator。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

css-animation-creator 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

css-animation-creator 需要什么权限?

权限级别是只读。该 Skill 主要提供规则、说明或参考内容,本身偏只读;真正读写文件、联网或执行命令仍取决于宿主 Agent 的任务。

css-animation-creator 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

css-animation-creator 用的人多吗?

公开数据是累计安装 5,998 次,近一周安装 245 次,下载 1,940 次,GitHub 106 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

css-animation-creator 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。
  • 标注为只读权限,试用阶段不容易误改本地文件。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理待分类类任务、又不想每次都手写提示词的人。
  • 只想先试试效果、不希望它改动本地文件的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

待分类类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
infsh-cli

infsh CLI · Codex、Claude

4,374,576382权限未标注未明确未收录
ai-video-generation

AI 视频生成 · Codex、Claude

4,313,376382调用外部服务未明确有提醒
lark-openapi-explorer

云雀 OpenAPI 浏览器 · Codex、Claude

2,064,1209,011需要联网未明确有提醒
lark-workflow-standup-report

Lark 工作流程站立报告 · Codex、Claude

2,041,6328,978需要联网未明确通过
use-my-browser

使用我的浏览器 · Codex、Claude

1,848,49140操作浏览器未明确未通过
css-animation-creator

CSS animation creator 视频 · 本页

5,998106只读未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

CSS Animation Creator

Instructions

When creating animations:

  1. Understand the purpose - Feedback, delight, guidance, or storytelling
  2. Choose the right technique - CSS transitions, keyframes, or JS libraries
  3. Optimize for performance - GPU-accelerated properties only
  4. Respect accessibility - Honor prefers-reduced-motion
  5. Keep timing natural - Use appropriate easing and duration

Animation Principles

The 12 Principles (Disney) Applied to UI

PrincipleUI Application
Squash & StretchButton press, elastic effects
AnticipationHover states before action
StagingFocus attention on important elements
Follow ThroughOvershoot then settle
Ease In/OutNatural acceleration/deceleration
ArcsCurved motion paths
Secondary ActionSupporting animations
TimingDuration conveys weight/importance
ExaggerationEmphasis for clarity
AppealPleasing, polished motion

Timing Guidelines

Animation TypeDurationEasing
Micro-interaction100-200msease-out
Button/hover150-250msease
Modal open200-300msease-out
Modal close150-200msease-in
Page transition300-500msease-in-out
Loading loop1000-2000mslinear/ease-in-out
Attention grab500-1000mselastic

CSS Transitions

Basic Syntax

.element {
  /* Single property */
  transition: opacity 0.3s ease;

  /* Multiple properties */
  transition:
    transform 0.3s ease,
    opacity 0.3s ease,
    background-color 0.2s ease;

  /* Shorthand: property duration timing-function delay */
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) 0s;
}

Easing Functions

/* Built-in */
transition-timing-function: linear;
transition-timing-function: ease;        /* Default - slow start, fast middle, slow end */
transition-timing-function: ease-in;     /* Slow start */
transition-timing-function: ease-out;    /* Slow end */
transition-timing-function: ease-in-out; /* Slow start and end */

/* Custom cubic-bezier */
/* Material Design standard */
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);

/* Decelerate (entering) */
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);

/* Accelerate (exiting) */
transition-timing-function: cubic-bezier(0.4, 0, 1, 1);

/* Bounce effect */
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);

/* Elastic */
transition-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.275);

Tailwind Transitions

// Duration
<div className="transition duration-150" />  // 150ms
<div className="transition duration-300" />  // 300ms
<div className="transition duration-500" />  // 500ms

// Timing function
<div className="transition ease-linear" />
<div className="transition ease-in" />
<div className="transition ease-out" />
<div className="transition ease-in-out" />

// Specific properties (better performance)
<div className="transition-opacity" />
<div className="transition-transform" />
<div className="transition-colors" />
<div className="transition-shadow" />
<div className="transition-all" />

// Combined
<button className="transition-all duration-200 ease-out hover:scale-105 hover:shadow-lg">
  Hover me
</button>

Keyframe Animations

Basic Syntax

@keyframes animationName {
  0% { /* starting state */ }
  50% { /* midpoint state */ }
  100% { /* ending state */ }
}

.element {
  animation: animationName 1s ease-in-out infinite;
  /* name | duration | timing | iteration-count */

  /* Full syntax */
  animation-name: animationName;
  animation-duration: 1s;
  animation-timing-function: ease-in-out;
  animation-delay: 0s;
  animation-iteration-count: infinite; /* or number */
  animation-direction: normal; /* reverse, alternate, alternate-reverse */
  animation-fill-mode: forwards; /* none, forwards, backwards, both */
  animation-play-state: running; /* paused */
}

Essential Animations Library

Fade Animations
/* Fade In */
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Fade In Up */
@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Fade In Down */
@keyframes fadeInDown {
  from {
    opacity: 0;
    transform: translateY(-20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Fade In Left */
@keyframes fadeInLeft {
  from {
    opacity: 0;
    transform: translateX(-20px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* Fade In Right */
@keyframes fadeInRight {
  from {
    opacity: 0;
    transform: translateX(20px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* Fade In Scale */
@keyframes fadeInScale {
  from {
    opacity: 0;
    transform: scale(0.9);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

/* Fade Out */
@keyframes fadeOut {
  from { opacity: 1; }
  to { opacity: 0; }
}
Scale Animations
/* Scale In */
@keyframes scaleIn {
  from { transform: scale(0); }
  to { transform: scale(1); }
}

/* Scale In Bounce */
@keyframes scaleInBounce {
  0% { transform: scale(0); }
  50% { transform: scale(1.1); }
  100% { transform: scale(1); }
}

/* Pop */
@keyframes pop {
  0% { transform: scale(1); }
  50% { transform: scale(1.1); }
  100% { transform: scale(1); }
}

/* Pulse */
@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.05); }
}

/* Heartbeat */
@keyframes heartbeat {
  0%, 100% { transform: scale(1); }
  14% { transform: scale(1.3); }
  28% { transform: scale(1); }
  42% { transform: scale(1.3); }
  70% { transform: scale(1); }
}
Bounce Animations
/* Bounce */
@keyframes bounce {
  0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
  40% { transform: translateY(-20px); }
  60% { transform: translateY(-10px); }
}

/* Bounce In */
@keyframes bounceIn {
  0% {
    opacity: 0;
    transform: scale(0.3);
  }
  50% {
    opacity: 1;
    transform: scale(1.05);
  }
  70% { transform: scale(0.9); }
  100% { transform: scale(1); }
}

/* Bounce In Down */
@keyframes bounceInDown {
  0% {
    opacity: 0;
    transform: translateY(-100px);
  }
  60% {
    opacity: 1;
    transform: translateY(20px);
  }
  80% { transform: translateY(-10px); }
  100% { transform: translateY(0); }
}

/* Rubber Band */
@keyframes rubberBand {
  0% { transform: scaleX(1); }
  30% { transform: scaleX(1.25) scaleY(0.75); }
  40% { transform: scaleX(0.75) scaleY(1.25); }
  50% { transform: scaleX(1.15) scaleY(0.85); }
  65% { transform: scaleX(0.95) scaleY(1.05); }
  75% { transform: scaleX(1.05) scaleY(0.95); }
  100% { transform: scaleX(1) scaleY(1); }
}
Rotate Animations
/* Spin */
@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

/* Spin Reverse */
@keyframes spinReverse {
  from { transform: rotate(360deg); }
  to { transform: rotate(0deg); }
}

/* Swing */
@keyframes swing {
  20% { transform: rotate(15deg); }
  40% { transform: rotate(-10deg); }
  60% { transform: rotate(5deg); }
  80% { transform: rotate(-5deg); }
  100% { transform: rotate(0deg); }
}

/* Wobble */
@keyframes wobble {
  0% { transform: translateX(0); }
  15% { transform: translateX(-15px) rotate(-5deg); }
  30% { transform: translateX(12px) rotate(3deg); }
  45% { transform: translateX(-9px) rotate(-3deg); }
  60% { transform: translateX(6px) rotate(2deg); }
  75% { transform: translateX(-3px) rotate(-1deg); }
  100% { transform: translateX(0); }
}

/* Flip */
@keyframes flipX {
  0% { transform: perspective(400px) rotateX(90deg); opacity: 0; }
  40% { transform: perspective(400px) rotateX(-20deg); }
  60% { transform: perspective(400px) rotateX(10deg); opacity: 1; }
  80% { transform: perspective(400px) rotateX(-5deg); }
  100% { transform: perspective(400px) rotateX(0deg); }
}
Slide Animations
/* Slide In Up */
@keyframes slideInUp {
  from {
    transform: translateY(100%);
    visibility: visible;
  }
  to { transform: translateY(0); }
}

/* Slide In Down */
@keyframes slideInDown {
  from {
    transform: translateY(-100%);
    visibility: visible;
  }
  to { transform: translateY(0); }
}

/* Slide In Left */
@keyframes slideInLeft {
  from {
    transform: translateX(-100%);
    visibility: visible;
  }
  to { transform: translateX(0); }
}

/* Slide In Right */
@keyframes slideInRight {
  from {
    transform: translateX(100%);
    visibility: visible;
  }
  to { transform: translateX(0); }
}

/* Slide Out */
@keyframes slideOutUp {
  from { transform: translateY(0); }
  to {
    transform: translateY(-100%);
    visibility: hidden;
  }
}
Attention Seekers
/* Shake */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
  20%, 40%, 60%, 80% { transform: translateX(5px); }
}

/* Shake Horizontal (stronger) */
@keyframes shakeX {
  0%, 100% { transform: translateX(0); }
  10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
  20%, 40%, 60%, 80% { transform: translateX(10px); }
}

/* Jello */
@keyframes jello {
  0%, 11.1%, 100% { transform: none; }
  22.2% { transform: skewX(-12.5deg) skewY(-12.5deg); }
  33.3% { transform: skewX(6.25deg) skewY(6.25deg); }
  44.4% { transform: skewX(-3.125deg) skewY(-3.125deg); }
  55.5% { transform: skewX(1.5625deg) skewY(1.5625deg); }
  66.6% { transform: skewX(-0.78125deg) skewY(-0.78125deg); }
  77.7% { transform: skewX(0.390625deg) skewY(0.390625deg); }
  88.8% { transform: skewX(-0.1953125deg) skewY(-0.1953125deg); }
}

/* Flash */
@keyframes flash {
  0%, 50%, 100% { opacity: 1; }
  25%, 75% { opacity: 0; }
}

/* Tada */
@keyframes tada {
  0% { transform: scale(1) rotate(0); }
  10%, 20% { transform: scale(0.9) rotate(-3deg); }
  30%, 50%, 70%, 90% { transform: scale(1.1) rotate(3deg); }
  40%, 60%, 80% { transform: scale(1.1) rotate(-3deg); }
  100% { transform: scale(1) rotate(0); }
}

Loading Animations

Spinners

// Simple spinner
<div className="w-8 h-8 border-4 border-gray-200 border-t-blue-600 rounded-full animate-spin" />

// Dual ring
<div className="relative w-12 h-12">
  <div className="absolute inset-0 border-4 border-blue-200 rounded-full" />
  <div className="absolute inset-0 border-4 border-transparent border-t-blue-600 rounded-full animate-spin" />
</div>

// Gradient spinner
<div className="w-10 h-10 rounded-full animate-spin"
  style={{
    background: 'conic-gradient(from 0deg, transparent, #3b82f6)',
    mask: 'radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px))'
  }}
/>
/* Pulsing ring */
@keyframes pingRing {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  75%, 100% {
    transform: scale(2);
    opacity: 0;
  }
}

.ping-ring {
  position: relative;
}

.ping-ring::before {
  content: '';
  position: absolute;
  inset: 0;
  border: 2px solid currentColor;
  border-radius: 50%;
  animation: pingRing 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
}

Dots Loading

// Bouncing dots
<div className="flex gap-1">
  <div className="w-2 h-2 bg-blue-600 rounded-full animate-bounce [animation-delay:-0.3s]" />
  <div className="w-2 h-2 bg-blue-600 rounded-full animate-bounce [animation-delay:-0.15s]" />
  <div className="w-2 h-2 bg-blue-600 rounded-full animate-bounce" />
</div>

// Pulsing dots
<div className="flex gap-1">
  {[0, 1, 2].map((i) => (
    <div
      key={i}
      className="w-2 h-2 bg-blue-600 rounded-full animate-pulse"
      style={{ animationDelay: `${i * 0.15}s` }}
    />
  ))}
</div>
/* Scaling dots */
@keyframes dotScale {
  0%, 80%, 100% { transform: scale(0); }
  40% { transform: scale(1); }
}

.dot-loader {
  display: flex;
  gap: 4px;
}

.dot-loader span {
  width: 8px;
  height: 8px;
  background: currentColor;
  border-radius: 50%;
  animation: dotScale 1.4s ease-in-out infinite;
}

.dot-loader span:nth-child(1) { animation-delay: -0.32s; }
.dot-loader span:nth-child(2) { animation-delay: -0.16s; }
.dot-loader span:nth-child(3) { animation-delay: 0s; }

Skeleton Loaders

// Basic skeleton
<div className="animate-pulse space-y-4">
  <div className="h-4 bg-gray-200 rounded w-3/4" />
  <div className="h-4 bg-gray-200 rounded w-1/2" />
  <div className="h-4 bg-gray-200 rounded w-5/6" />
</div>

// Card skeleton
<div className="animate-pulse">
  <div className="bg-gray-200 h-48 rounded-t-lg" />
  <div className="p-4 space-y-3">
    <div className="h-4 bg-gray-200 rounded w-3/4" />
    <div className="h-4 bg-gray-200 rounded w-1/2" />
  </div>
</div>

// Shimmer effect
<div className="relative overflow-hidden bg-gray-200 rounded">
  <div className="absolute inset-0 -translate-x-full animate-[shimmer_2s_infinite] bg-gradient-to-r from-transparent via-white/60 to-transparent" />
</div>
/* Shimmer keyframe */
@keyframes shimmer {
  100% { transform: translateX(100%); }
}

Progress Bars

// Indeterminate progress
<div className="h-1 w-full bg-gray-200 rounded overflow-hidden">
  <div className="h-full bg-blue-600 w-1/3 animate-[progress_1s_ease-in-out_infinite]" />
</div>

// Striped progress
<div className="h-2 w-full bg-gray-200 rounded overflow-hidden">
  <div
    className="h-full bg-blue-600 transition-all duration-300"
    style={{
      width: `${progress}%`,
      backgroundImage: 'linear-gradient(45deg, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%)',
      backgroundSize: '1rem 1rem',
      animation: 'progress-stripes 1s linear infinite'
    }}
  />
</div>
@keyframes progress {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(400%); }
}

@keyframes progress-stripes {
  from { background-position: 1rem 0; }
  to { background-position: 0 0; }
}

Micro-interactions

Button Effects

// Press effect
<button className="transition-transform duration-100 active:scale-95">
  Click me
</button>

// Ripple effect (React)
function RippleButton({ children, ...props }) {
  const [ripples, setRipples] = useState([]);

  const handleClick = (e) => {
    const rect = e.currentTarget.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;

    setRipples([...ripples, { x, y, id: Date.now() }]);
    setTimeout(() => setRipples(r => r.slice(1)), 600);
  };

  return (
    <button className="relative overflow-hidden" onClick={handleClick} {...props}>
      {ripples.map(ripple => (
        <span
          key={ripple.id}
          className="absolute bg-white/30 rounded-full animate-[ripple_0.6s_ease-out]"
          style={{
            left: ripple.x,
            top: ripple.y,
            transform: 'translate(-50%, -50%)'
          }}
        />
      ))}
      {children}
    </button>
  );
}
@keyframes ripple {
  from {
    width: 0;
    height: 0;
    opacity: 0.5;
  }
  to {
    width: 200px;
    height: 200px;
    opacity: 0;
  }
}

Hover Effects

// Lift effect
<div className="transition-all duration-300 hover:-translate-y-1 hover:shadow-lg">
  Card content
</div>

// Glow effect
<button className="transition-shadow duration-300 hover:shadow-[0_0_20px_rgba(59,130,246,0.5)]">
  Glow button
</button>

// Border animation
<div className="relative group">
  <div className="absolute -inset-0.5 bg-gradient-to-r from-pink-600 to-purple-600 rounded-lg blur opacity-0 group-hover:opacity-75 transition duration-300" />
  <div className="relative bg-white rounded-lg p-6">Content</div>
</div>

// Underline animation
<a className="relative inline-block after:absolute after:bottom-0 after:left-0 after:h-0.5 after:w-0 after:bg-current after:transition-all after:duration-300 hover:after:w-full">
  Animated link
</a>

// Fill animation
<a className="relative overflow-hidden group">
  <span className="relative z-10 transition-colors duration-300 group-hover:text-white">
    Hover me
  </span>
  <span className="absolute inset-0 bg-blue-600 transform -translate-x-full group-hover:translate-x-0 transition-transform duration-300" />
</a>

Icon Animations

// Rotate on hover
<button className="group">
  <SettingsIcon className="transition-transform duration-500 group-hover:rotate-180" />
</button>

// Bounce on hover
<button className="group">
  <ArrowIcon className="transition-transform group-hover:translate-x-1 group-hover:animate-bounce" />
</button>

// Scale + rotate
<button className="group">
  <PlusIcon className="transition-all duration-300 group-hover:scale-110 group-hover:rotate-90" />
</button>

Form Interactions

// Input focus effect
<div className="relative">
  <input
    className="peer w-full border-b-2 border-gray-300 focus:border-blue-600 outline-none py-2 transition-colors"
    placeholder=" "
  />
  <label className="absolute left-0 top-2 text-gray-500 transition-all peer-focus:-top-4 peer-focus:text-sm peer-focus:text-blue-600 peer-[:not(:placeholder-shown)]:-top-4 peer-[:not(:placeholder-shown)]:text-sm">
    Email
  </label>
</div>

// Checkbox animation
<label className="flex items-center gap-2 cursor-pointer">
  <div className="relative">
    <input type="checkbox" className="peer sr-only" />
    <div className="w-5 h-5 border-2 rounded transition-colors peer-checked:bg-blue-600 peer-checked:border-blue-600" />
    <CheckIcon className="absolute inset-0 m-auto w-3 h-3 text-white opacity-0 scale-0 transition-all peer-checked:opacity-100 peer-checked:scale-100" />
  </div>
  Label text
</label>

// Toggle switch
<button
  role="switch"
  aria-checked={enabled}
  onClick={() => setEnabled(!enabled)}
  className={cn(
    "relative w-11 h-6 rounded-full transition-colors",
    enabled ? "bg-blue-600" : "bg-gray-200"
  )}
>
  <span className={cn(
    "absolute top-0.5 left-0.5 w-5 h-5 bg-white rounded-full shadow transition-transform",
    enabled && "translate-x-5"
  )} />
</button>

Success/Error States

// Success checkmark
<div className="w-16 h-16 rounded-full bg-green-100 flex items-center justify-center">
  <svg className="w-8 h-8 text-green-600" viewBox="0 0 24 24">
    <path
      className="animate-[draw_0.5s_ease-out_forwards]"
      fill="none"
      stroke="currentColor"
      strokeWidth="3"
      strokeLinecap="round"
      strokeLinejoin="round"
      strokeDasharray="24"
      strokeDashoffset="24"
      d="M5 13l4 4L19 7"
    />
  </svg>
</div>

// Error shake
<input className="animate-[shake_0.5s_ease-in-out] border-red-500" />
@keyframes draw {
  to { stroke-dashoffset: 0; }
}

Page Transitions

CSS-only Transitions

/* View Transitions API (Chrome 111+) */
@view-transition {
  navigation: auto;
}

::view-transition-old(root) {
  animation: fadeOut 0.3s ease-out;
}

::view-transition-new(root) {
  animation: fadeIn 0.3s ease-in;
}

/* Specific element transitions */
.hero-image {
  view-transition-name: hero;
}

::view-transition-old(hero),
::view-transition-new(hero) {
  animation-duration: 0.5s;
}

Framer Motion

import { motion, AnimatePresence } from 'framer-motion';

// Fade transition
const pageVariants = {
  initial: { opacity: 0 },
  animate: { opacity: 1 },
  exit: { opacity: 0 },
};

function PageWrapper({ children }) {
  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={pathname}
        variants={pageVariants}
        initial="initial"
        animate="animate"
        exit="exit"
        transition={{ duration: 0.3 }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  );
}

// Slide transition
const slideVariants = {
  initial: { opacity: 0, x: 20 },
  animate: { opacity: 1, x: 0 },
  exit: { opacity: 0, x: -20 },
};

// Scale + fade
const scaleVariants = {
  initial: { opacity: 0, scale: 0.95 },
  animate: { opacity: 1, scale: 1 },
  exit: { opacity: 0, scale: 1.05 },
};

// Shared layout animations
function Gallery({ items, selectedId }) {
  return (
    <>
      {items.map(item => (
        <motion.div key={item.id} layoutId={item.id}>
          <img src={item.src} />
        </motion.div>
      ))}

      <AnimatePresence>
        {selectedId && (
          <motion.div layoutId={selectedId} className="modal">
            <img src={items.find(i => i.id === selectedId).src} />
          </motion.div>
        )}
      </AnimatePresence>
    </>
  );
}

Staggered Animations

// Framer Motion stagger
const containerVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1,
    },
  },
};

const itemVariants = {
  hidden: { opacity: 0, y: 20 },
  visible: { opacity: 1, y: 0 },
};

function StaggeredList({ items }) {
  return (
    <motion.ul
      variants={containerVariants}
      initial="hidden"
      animate="visible"
    >
      {items.map(item => (
        <motion.li key={item.id} variants={itemVariants}>
          {item.name}
        </motion.li>
      ))}
    </motion.ul>
  );
}

// CSS stagger with custom properties
<ul className="stagger-list">
  {items.map((item, i) => (
    <li
      key={item.id}
      style={{ '--i': i } as React.CSSProperties}
      className="animate-fadeInUp opacity-0"
    >
      {item.name}
    </li>
  ))}
</ul>
.stagger-list li {
  animation: fadeInUp 0.5s ease forwards;
  animation-delay: calc(var(--i) * 0.1s);
}

Scroll Animations

Intersection Observer

function useInView(options = {}) {
  const ref = useRef(null);
  const [isInView, setIsInView] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        setIsInView(true);
        if (options.once) observer.disconnect();
      }
    }, { threshold: 0.1, ...options });

    if (ref.current) observer.observe(ref.current);
    return () => observer.disconnect();
  }, []);

  return [ref, isInView];
}

// Usage
function AnimatedSection() {
  const [ref, isInView] = useInView({ once: true });

  return (
    <div
      ref={ref}
      className={cn(
        "transition-all duration-700",
        isInView ? "opacity-100 translate-y-0" : "opacity-0 translate-y-10"
      )}
    >
      Content
    </div>
  );
}

Scroll-triggered with Framer Motion

import { motion, useScroll, useTransform } from 'framer-motion';

function ParallaxSection() {
  const ref = useRef(null);
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ["start end", "end start"]
  });

  const y = useTransform(scrollYProgress, [0, 1], [100, -100]);
  const opacity = useTransform(scrollYProgress, [0, 0.5, 1], [0, 1, 0]);

  return (
    <motion.div ref={ref} style={{ y, opacity }}>
      Parallax content
    </motion.div>
  );
}

// Scroll-linked progress
function ScrollProgress() {
  const { scrollYProgress } = useScroll();

  return (
    <motion.div
      className="fixed top-0 left-0 right-0 h-1 bg-blue-600 origin-left"
      style={{ scaleX: scrollYProgress }}
    />
  );
}

CSS Scroll-driven Animations

/* Native scroll-driven animations (Chrome 115+) */
@keyframes reveal {
  from {
    opacity: 0;
    transform: translateY(50px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.scroll-reveal {
  animation: reveal linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

/* Scroll progress indicator */
.progress-bar {
  transform-origin: left;
  animation: grow linear;
  animation-timeline: scroll();
}

@keyframes grow {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

Accessibility

Reduced Motion

/* Global reduced motion */
@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;
  }
}

/* Per-element control */
.animated-element {
  animation: bounce 1s infinite;
}

@media (prefers-reduced-motion: reduce) {
  .animated-element {
    animation: none;
  }
}
// Tailwind motion-safe/motion-reduce
<div className="motion-safe:animate-bounce motion-reduce:animate-none">
  Respects preferences
</div>

// React hook
function usePrefersReducedMotion() {
  const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);

  useEffect(() => {
    const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
    setPrefersReducedMotion(mediaQuery.matches);

    const handler = (e) => setPrefersReducedMotion(e.matches);
    mediaQuery.addEventListener('change', handler);
    return () => mediaQuery.removeEventListener('change', handler);
  }, []);

  return prefersReducedMotion;
}

// Usage
function AnimatedComponent() {
  const prefersReducedMotion = usePrefersReducedMotion();

  return (
    <motion.div
      animate={{ x: 100 }}
      transition={{
        duration: prefersReducedMotion ? 0 : 0.3
      }}
    />
  );
}

Tailwind Animation Config

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      animation: {
        // Fade
        'fade-in': 'fadeIn 0.5s ease forwards',
        'fade-in-up': 'fadeInUp 0.5s ease forwards',
        'fade-in-down': 'fadeInDown 0.5s ease forwards',
        'fade-out': 'fadeOut 0.3s ease forwards',

        // Slide
        'slide-in-left': 'slideInLeft 0.3s ease-out',
        'slide-in-right': 'slideInRight 0.3s ease-out',
        'slide-in-up': 'slideInUp 0.3s ease-out',
        'slide-in-down': 'slideInDown 0.3s ease-out',

        // Scale
        'scale-in': 'scaleIn 0.2s ease-out',
        'pop': 'pop 0.3s ease-out',

        // Attention
        'shake': 'shake 0.5s ease-in-out',
        'wiggle': 'wiggle 1s ease-in-out infinite',
        'heartbeat': 'heartbeat 1.5s ease-in-out infinite',

        // Loading
        'shimmer': 'shimmer 2s infinite',
        'progress': 'progress 1s ease-in-out infinite',
      },
      keyframes: {
        fadeIn: {
          '0%': { opacity: '0' },
          '100%': { opacity: '1' },
        },
        fadeInUp: {
          '0%': { opacity: '0', transform: 'translateY(20px)' },
          '100%': { opacity: '1', transform: 'translateY(0)' },
        },
        fadeInDown: {
          '0%': { opacity: '0', transform: 'translateY(-20px)' },
          '100%': { opacity: '1', transform: 'translateY(0)' },
        },
        fadeOut: {
          '0%': { opacity: '1' },
          '100%': { opacity: '0' },
        },
        slideInLeft: {
          '0%': { transform: 'translateX(-100%)' },
          '100%': { transform: 'translateX(0)' },
        },
        slideInRight: {
          '0%': { transform: 'translateX(100%)' },
          '100%': { transform: 'translateX(0)' },
        },
        slideInUp: {
          '0%': { transform: 'translateY(100%)' },
          '100%': { transform: 'translateY(0)' },
        },
        slideInDown: {
          '0%': { transform: 'translateY(-100%)' },
          '100%': { transform: 'translateY(0)' },
        },
        scaleIn: {
          '0%': { transform: 'scale(0)' },
          '100%': { transform: 'scale(1)' },
        },
        pop: {
          '0%': { transform: 'scale(1)' },
          '50%': { transform: 'scale(1.1)' },
          '100%': { transform: 'scale(1)' },
        },
        shake: {
          '0%, 100%': { transform: 'translateX(0)' },
          '10%, 30%, 50%, 70%, 90%': { transform: 'translateX(-5px)' },
          '20%, 40%, 60%, 80%': { transform: 'translateX(5px)' },
        },
        wiggle: {
          '0%, 100%': { transform: 'rotate(-3deg)' },
          '50%': { transform: 'rotate(3deg)' },
        },
        heartbeat: {
          '0%, 100%': { transform: 'scale(1)' },
          '14%': { transform: 'scale(1.3)' },
          '28%': { transform: 'scale(1)' },
          '42%': { transform: 'scale(1.3)' },
          '70%': { transform: 'scale(1)' },
        },
        shimmer: {
          '100%': { transform: 'translateX(100%)' },
        },
        progress: {
          '0%': { transform: 'translateX(-100%)' },
          '100%': { transform: 'translateX(400%)' },
        },
      },
    },
  },
};

Performance Best Practices

GPU-Accelerated Properties

/* GOOD - GPU accelerated */
transform: translateX(100px);
transform: scale(1.1);
transform: rotate(45deg);
opacity: 0.5;

/* BAD - Triggers layout/paint */
left: 100px;
top: 50px;
width: 200px;
height: 100px;
margin: 20px;
padding: 10px;
border-width: 2px;
font-size: 16px;

will-change (Use Sparingly)

/* Only when needed for complex animations */
.complex-animation {
  will-change: transform, opacity;
}

/* Remove after animation */
.complex-animation.done {
  will-change: auto;
}

Contain for Isolation

.animated-section {
  contain: layout style paint;
}

Animation Performance Checklist

  • Only animate transform and opacity
  • Use will-change only when necessary
  • Keep animations under 300ms for UI feedback
  • Test on low-end devices
  • Use contain for isolated sections
  • Reduce animation during scroll
  • Pause off-screen animations

平台分布

Codex

37.73%
按下载量换算732

Claude

29.8%
按下载量换算578

Cursor

17.58%
按下载量换算341

Gemini CLI

9.4%
按下载量换算182

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

只读

该 Skill 主要提供规则、说明或参考内容,本身偏只读;真正读写文件、联网或执行命令仍取决于宿主 Agent 的任务。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills