Overview
Produces high-quality, performant CSS animations and transitions using modern best practices. The skill generates keyframe definitions, timing functions, accessibility-respecting motion (prefers-reduced-motion), GPU-accelerated properties, stagger patterns, scroll-driven animations, and ready-to-use recipes for common UI motions such as fades, slides, bounces, loading states, and hover effects.
When to Use This Skill
- Adding subtle motion to buttons, cards, modals, loaders, or page transitions.
- The user explicitly requests "CSS animation", "keyframe", "transition", "micro-interaction", or "motion".
- Avoiding JavaScript animation libraries (Framer Motion, GSAP, anime.js) for performance or bundle size reasons.
- Implementing loading states, success/error feedback, or attention-guiding animations.
- Creating scroll-triggered or entrance animations.
Prerequisites
- A web project using CSS (plain CSS, Tailwind, SCSS, etc.).
- Target browsers that support modern CSS (Chrome 90+, Firefox 90+, Safari 15+ recommended for advanced features like
@property or scroll timelines).
- For scroll-driven animations, the page must have scrollable content.
- No JavaScript required unless the skill explicitly adds a tiny progressive enhancement script.
Steps
Analyze the motion requirement:
- Identify the trigger (hover, focus, load, click, scroll, state change).
- Determine the animation type: entrance, exit, attention, feedback, loading, or continuous.
- Choose properties that are GPU-composited:
transform (translate, scale, rotate), opacity, filter. Avoid animating width, height, left, top, margin, padding when possible.
Respect user motion preferences (mandatory):
- Always wrap animations in
@media (prefers-reduced-motion: no-preference) { ... }.
- Provide a reduced or static fallback for users who prefer reduced motion.
- Never animate on load for users with reduced motion unless it is essential.
Select timing and easing:
- Use
cubic-bezier() for custom easings (e.g., cubic-bezier(0.4, 0, 0.2, 1) for material design).
- Common durations: 150ms (micro), 250-300ms (standard), 400-600ms (emphasis), 800ms+ (page-level).
- Use
animation-fill-mode: both or forwards/backwards as needed.
- Prefer
transition for simple state changes; use @keyframes + animation for complex or repeating sequences.
Write the keyframes:
- Name them descriptively (e.g.,
fadeInUp, pulse, modalEnter).
- Use percentage-based or
from/to.
- Keep keyframe lists short (3-5 stops maximum for most UI work).
Implement stagger for lists:
- Use
animation-delay with incremental values (e.g., calc(var(--i) * 80ms)).
- Set
--i via inline style or CSS counters on list items.
Add scroll-driven animations (modern):
- Use
animation-timeline: view() or scroll() when supported.
- Provide a JavaScript fallback using IntersectionObserver for older browsers.
Provide multiple recipe variants:
- Always deliver the animation in a self-contained code block.
- Show how to apply it to real HTML elements.
- Include Tailwind arbitrary value versions when relevant.
Include performance and accessibility notes:
- List which properties are safe.
- Warn about
will-change usage (use sparingly).
- Document how the animation respects reduced motion.
Output complete, copy-pasteable code:
- Include the CSS (and minimal HTML demo if helpful).
- Add comments explaining key decisions.
Examples
Example 1: Fade + Slide Up Entrance Animation (with stagger)
/* Fade In Up Animation */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (prefers-reduced-motion: no-preference) {
.fade-in-up {
animation: fadeInUp 400ms cubic-bezier(0.4, 0, 0.2, 1) both;
}
/* Staggered list items */
.stagger-list > * {
--stagger-delay: 80ms;
animation-delay: calc(var(--i, 0) * var(--stagger-delay));
}
}
/* Usage */
<div class="stagger-list">
<div class="fade-in-up" style="--i: 0">Item 1</div>
<div class="fade-in-up" style="--i: 1">Item 2</div>
<div class="fade-in-up" style="--i: 2">Item 3</div>
</div>
Example 2: Subtle Button Micro-interaction (Scale + Shadow)
@media (prefers-reduced-motion: no-preference) {
.btn-primary {
transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
.btn-primary:hover {
transform: translateY(-1px) scale(1.02);
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}
.btn-primary:active {
transform: translateY(0) scale(0.985);
transition-duration: 75ms;
}
}
Example 3: Loading Spinner (Infinite, GPU only)
@keyframes spin {
to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: no-preference) {
.spinner {
width: 24px;
height: 24px;
border: 3px solid #e5e7eb;
border-top-color: #3b82f6;
border-radius: 9999px;
animation: spin 800ms linear infinite;
}
}
Example 4: Scroll-Driven Fade (with fallback)
/* Modern scroll-driven */
@media (prefers-reduced-motion: no-preference) and (prefers-reduced-motion: no-preference) {
@supports (animation-timeline: view()) {
.scroll-fade {
animation: fadeIn linear both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
}
}
}
/* JS fallback for older browsers */
.scroll-fade {
opacity: 0;
transform: translateY(20px);
transition: opacity 400ms ease, transform 400ms ease;
}
.scroll-fade.is-visible {
opacity: 1;
transform: none;
}
(Include a small IntersectionObserver script in the output when using the fallback.)
Example 5: Attention Pulse (for notifications)
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.08); }
}
@media (prefers-reduced-motion: no-preference) {
.attention-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
}
Edge Cases & Error Handling
- User has
prefers-reduced-motion: reduce: Always provide a no-animation path. For loading spinners that are essential, use a static indicator + ARIA live region instead of infinite animation.
- Animation causes layout thrashing: Only animate
transform and opacity. If the user requests width/height animation, provide a transform: scale alternative and explain the performance difference.
- Too many staggered items: Cap stagger at ~10-12 items or use a CSS variable that the agent can adjust. Suggest IntersectionObserver + class toggle for very long lists.
- Browser support: For advanced features (scroll timelines,
@property), always provide a progressive enhancement comment and a basic fallback.
- Accessibility: Never use animation to convey critical information without a static alternative. For attention animations, also use color or icon changes.
- Performance on low-end devices: Recommend testing with DevTools throttling. Suggest
will-change: transform only on the animating element and remove it after the animation ends.
- Conflict with Tailwind: Show how to use arbitrary values (
animate-[spin_800ms_linear_infinite]) and how to extend Tailwind config with custom keyframes.
Verification
- Paste the generated CSS + HTML into a test file and open in Chrome, Firefox, and Safari.
- Reduced motion test: Set
prefers-reduced-motion: reduce in DevTools (Rendering panel) — confirm animations are disabled or greatly simplified.
- Performance check: Use Chrome DevTools Performance tab while triggering the animation. Confirm no layout thrashing (purple bars minimal) and GPU compositing (green bars for the layer).
- Accessibility: Use keyboard to trigger hover/focus states. Verify focus is never lost due to animation. Run axe or Lighthouse — motion-related issues should be absent.
- Stagger verification: Inspect computed
animation-delay values on list items; they should increment correctly.
- Success: Animations are smooth (60fps), respect user preferences, use only safe properties, and enhance (not distract from) the UI.
References
1---2name: css-animation-crafter3description: Creates CSS keyframe animations, transitions, and micro-interactions. Use when adding motion to web UIs without JavaScript animation libraries.4license: Apache-2.05---67## Overview89Produces high-quality, performant CSS animations and transitions using modern best practices. The skill generates keyframe definitions, timing functions, accessibility-respecting motion (prefers-reduced-motion), GPU-accelerated properties, stagger patterns, scroll-driven animations, and ready-to-use recipes for common UI motions such as fades, slides, bounces, loading states, and hover effects.1011## When to Use This Skill1213- Adding subtle motion to buttons, cards, modals, loaders, or page transitions.14- The user explicitly requests "CSS animation", "keyframe", "transition", "micro-interaction", or "motion".15- Avoiding JavaScript animation libraries (Framer Motion, GSAP, anime.js) for performance or bundle size reasons.16- Implementing loading states, success/error feedback, or attention-guiding animations.17- Creating scroll-triggered or entrance animations.1819## Prerequisites2021- A web project using CSS (plain CSS, Tailwind, SCSS, etc.).22- Target browsers that support modern CSS (Chrome 90+, Firefox 90+, Safari 15+ recommended for advanced features like `@property` or scroll timelines).23- For scroll-driven animations, the page must have scrollable content.24- No JavaScript required unless the skill explicitly adds a tiny progressive enhancement script.2526## Steps27281. **Analyze the motion requirement**:29 - Identify the trigger (hover, focus, load, click, scroll, state change).30 - Determine the animation type: entrance, exit, attention, feedback, loading, or continuous.31 - Choose properties that are GPU-composited: `transform` (translate, scale, rotate), `opacity`, `filter`. Avoid animating `width`, `height`, `left`, `top`, `margin`, `padding` when possible.32332. **Respect user motion preferences (mandatory)**:34 - Always wrap animations in `@media (prefers-reduced-motion: no-preference) { ... }`.35 - Provide a reduced or static fallback for users who prefer reduced motion.36 - Never animate on load for users with reduced motion unless it is essential.37383. **Select timing and easing**:39 - Use `cubic-bezier()` for custom easings (e.g., `cubic-bezier(0.4, 0, 0.2, 1)` for material design).40 - Common durations: 150ms (micro), 250-300ms (standard), 400-600ms (emphasis), 800ms+ (page-level).41 - Use `animation-fill-mode: both` or `forwards`/`backwards` as needed.42 - Prefer `transition` for simple state changes; use `@keyframes` + `animation` for complex or repeating sequences.43444. **Write the keyframes**:45 - Name them descriptively (e.g., `fadeInUp`, `pulse`, `modalEnter`).46 - Use percentage-based or `from`/`to`.47 - Keep keyframe lists short (3-5 stops maximum for most UI work).48495. **Implement stagger for lists**:50 - Use `animation-delay` with incremental values (e.g., `calc(var(--i) * 80ms)`).51 - Set `--i` via inline style or CSS counters on list items.52536. **Add scroll-driven animations** (modern):54 - Use `animation-timeline: view()` or `scroll()` when supported.55 - Provide a JavaScript fallback using IntersectionObserver for older browsers.56577. **Provide multiple recipe variants**:58 - Always deliver the animation in a self-contained code block.59 - Show how to apply it to real HTML elements.60 - Include Tailwind arbitrary value versions when relevant.61628. **Include performance and accessibility notes**:63 - List which properties are safe.64 - Warn about `will-change` usage (use sparingly).65 - Document how the animation respects reduced motion.66679. **Output complete, copy-pasteable code**:68 - Include the CSS (and minimal HTML demo if helpful).69 - Add comments explaining key decisions.7071## Examples7273**Example 1: Fade + Slide Up Entrance Animation (with stagger)**7475```css76/* Fade In Up Animation */77@keyframes fadeInUp {78 from {79 opacity: 0;80 transform: translateY(20px);81 }82 to {83 opacity: 1;84 transform: translateY(0);85 }86}8788@media (prefers-reduced-motion: no-preference) {89 .fade-in-up {90 animation: fadeInUp 400ms cubic-bezier(0.4, 0, 0.2, 1) both;91 }92 93 /* Staggered list items */94 .stagger-list > * {95 --stagger-delay: 80ms;96 animation-delay: calc(var(--i, 0) * var(--stagger-delay));97 }98}99100/* Usage */101<div class="stagger-list">102 <div class="fade-in-up" style="--i: 0">Item 1</div>103 <div class="fade-in-up" style="--i: 1">Item 2</div>104 <div class="fade-in-up" style="--i: 2">Item 3</div>105</div>106```107108**Example 2: Subtle Button Micro-interaction (Scale + Shadow)**109110```css111@media (prefers-reduced-motion: no-preference) {112 .btn-primary {113 transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1),114 box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1);115 }116 117 .btn-primary:hover {118 transform: translateY(-1px) scale(1.02);119 box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);120 }121 122 .btn-primary:active {123 transform: translateY(0) scale(0.985);124 transition-duration: 75ms;125 }126}127```128129**Example 3: Loading Spinner (Infinite, GPU only)**130131```css132@keyframes spin {133 to { transform: rotate(360deg); }134}135136@media (prefers-reduced-motion: no-preference) {137 .spinner {138 width: 24px;139 height: 24px;140 border: 3px solid #e5e7eb;141 border-top-color: #3b82f6;142 border-radius: 9999px;143 animation: spin 800ms linear infinite;144 }145}146```147148**Example 4: Scroll-Driven Fade (with fallback)**149150```css151/* Modern scroll-driven */152@media (prefers-reduced-motion: no-preference) and (prefers-reduced-motion: no-preference) {153 @supports (animation-timeline: view()) {154 .scroll-fade {155 animation: fadeIn linear both;156 animation-timeline: view();157 animation-range: entry 0% entry 50%;158 }159 }160}161162/* JS fallback for older browsers */163.scroll-fade {164 opacity: 0;165 transform: translateY(20px);166 transition: opacity 400ms ease, transform 400ms ease;167}168169.scroll-fade.is-visible {170 opacity: 1;171 transform: none;172}173```174175(Include a small IntersectionObserver script in the output when using the fallback.)176177**Example 5: Attention Pulse (for notifications)**178179```css180@keyframes pulse {181 0%, 100% { transform: scale(1); }182 50% { transform: scale(1.08); }183}184185@media (prefers-reduced-motion: no-preference) {186 .attention-pulse {187 animation: pulse 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;188 }189}190```191192## Edge Cases & Error Handling193194- **User has `prefers-reduced-motion: reduce`**: Always provide a no-animation path. For loading spinners that are essential, use a static indicator + ARIA live region instead of infinite animation.195- **Animation causes layout thrashing**: Only animate `transform` and `opacity`. If the user requests width/height animation, provide a `transform: scale` alternative and explain the performance difference.196- **Too many staggered items**: Cap stagger at ~10-12 items or use a CSS variable that the agent can adjust. Suggest IntersectionObserver + class toggle for very long lists.197- **Browser support**: For advanced features (scroll timelines, `@property`), always provide a progressive enhancement comment and a basic fallback.198- **Accessibility**: Never use animation to convey critical information without a static alternative. For attention animations, also use color or icon changes.199- **Performance on low-end devices**: Recommend testing with DevTools throttling. Suggest `will-change: transform` only on the animating element and remove it after the animation ends.200- **Conflict with Tailwind**: Show how to use arbitrary values (`animate-[spin_800ms_linear_infinite]`) and how to extend Tailwind config with custom keyframes.201202## Verification2032041. Paste the generated CSS + HTML into a test file and open in Chrome, Firefox, and Safari.2052. **Reduced motion test**: Set `prefers-reduced-motion: reduce` in DevTools (Rendering panel) — confirm animations are disabled or greatly simplified.2063. **Performance check**: Use Chrome DevTools Performance tab while triggering the animation. Confirm no layout thrashing (purple bars minimal) and GPU compositing (green bars for the layer).2074. **Accessibility**: Use keyboard to trigger hover/focus states. Verify focus is never lost due to animation. Run axe or Lighthouse — motion-related issues should be absent.2085. **Stagger verification**: Inspect computed `animation-delay` values on list items; they should increment correctly.2096. Success: Animations are smooth (60fps), respect user preferences, use only safe properties, and enhance (not distract from) the UI.210211## References212213- [MDN CSS Animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations)214- [MDN CSS Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions)215- [prefers-reduced-motion on MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion)216- [Scroll-driven Animations Spec](https://drafts.csswg.org/css-animations-2/#scroll-driven-animations)217- [CSS Triggers](https://csstriggers.com/) — which properties cause layout/paint218- [Cubic-bezier.com](https://cubic-bezier.com) for custom easing visualization