Expert knowledge for pure CSS animations - lightweight, performant animations using CSS keyframes, transitions, and modern CSS features without JavaScript dependencies.
From react-animation-studionpx claudepluginhub markus41/claude --plugin react-animation-studioThis skill uses the workspace's default tool permissions.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Details PluginEval's skill quality evaluation: 3 layers (static, LLM judge), 10 dimensions, rubrics, formulas, anti-patterns, badges. Use to interpret scores, improve triggering, calibrate thresholds.
Expert knowledge for pure CSS animations - lightweight, performant animations using CSS keyframes, transitions, and modern CSS features without JavaScript dependencies.
Activate this skill when:
**/*.css with @keyframes definitions**/*.scss or **/*.sass with animations**/*.tsx with Tailwind animation classes**/styles/*.css with animation utilitiestailwind.config.js with custom animations.element {
transition: transform 0.3s ease-out, opacity 0.3s ease-out;
}
.element:hover {
transform: translateY(-4px);
opacity: 0.9;
}
/* Shorthand */
.element {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.element {
animation: fadeIn 0.5s ease-out forwards;
}
.element {
animation-name: slideIn;
animation-duration: 0.5s;
animation-timing-function: ease-out;
animation-delay: 0.2s;
animation-iteration-count: 1; /* or infinite */
animation-direction: normal; /* or reverse, alternate, alternate-reverse */
animation-fill-mode: forwards; /* none, forwards, backwards, both */
animation-play-state: running; /* or paused */
/* Shorthand */
animation: slideIn 0.5s ease-out 0.2s 1 normal forwards;
}
/* Built-in */
transition-timing-function: linear;
transition-timing-function: ease;
transition-timing-function: ease-in;
transition-timing-function: ease-out;
transition-timing-function: ease-in-out;
/* Cubic bezier */
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); /* Material ease */
transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1); /* Default ease */
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); /* Back ease */
/* Steps (for sprite animations) */
transition-timing-function: steps(4, end);
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translate3d(0, 20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translate3d(0, -20px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
}
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
@keyframes dotPulse {
0%, 80%, 100% {
transform: scale(0);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}
<div class="animate-spin">Loading...</div>
<div class="animate-ping">Notification</div>
<div class="animate-pulse">Skeleton</div>
<div class="animate-bounce">Scroll down</div>
// tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
'fade-in': 'fadeIn 0.5s ease-out forwards',
'fade-in-up': 'fadeInUp 0.5s ease-out forwards',
'slide-in-right': 'slideInRight 0.3s ease-out forwards',
'scale-in': 'scaleIn 0.2s ease-out forwards',
'shimmer': 'shimmer 2s infinite',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
fadeInUp: {
'0%': { opacity: '0', transform: 'translateY(20px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
slideInRight: {
'0%': { opacity: '0', transform: 'translateX(20px)' },
'100%': { opacity: '1', transform: 'translateX(0)' },
},
scaleIn: {
'0%': { opacity: '0', transform: 'scale(0.95)' },
'100%': { opacity: '1', transform: 'scale(1)' },
},
shimmer: {
'0%': { backgroundPosition: '-200% 0' },
'100%': { backgroundPosition: '200% 0' },
},
},
},
},
};
.stagger-item {
animation: fadeInUp 0.5s ease-out forwards;
animation-delay: calc(var(--index) * 0.1s);
opacity: 0;
}
{items.map((item, index) => (
<div
key={item.id}
className="stagger-item"
style={{ '--index': index } as React.CSSProperties}
>
{item.content}
</div>
))}
// components/Spinner.tsx
export function Spinner({ size = 'md' }: { size?: 'sm' | 'md' | 'lg' }) {
const sizes = {
sm: 'w-4 h-4 border-2',
md: 'w-8 h-8 border-3',
lg: 'w-12 h-12 border-4',
};
return (
<div
className={`${sizes[size]} rounded-full border-blue-500 border-t-transparent animate-spin`}
/>
);
}
// components/Skeleton.tsx
export function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={`bg-gray-200 rounded animate-pulse ${className}`}
{...props}
/>
);
}
// Usage
<div className="space-y-2">
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-4 w-1/2" />
<Skeleton className="h-4 w-5/6" />
</div>
.shimmer {
background: linear-gradient(
90deg,
#f0f0f0 0%,
#e0e0e0 50%,
#f0f0f0 100%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
/* Good - GPU accelerated */
transform: translateX(100px);
transform: scale(1.1);
transform: rotate(45deg);
opacity: 0.5;
/* Avoid - trigger layout/paint */
width: 100px;
height: 100px;
top: 10px;
left: 10px;
margin: 10px;
padding: 10px;
.animated-element {
will-change: transform, opacity;
}
/* Remove after animation */
.animated-element.done {
will-change: auto;
}
/* Bad - triggers layout */
.slide {
left: 0;
transition: left 0.3s;
}
.slide.active {
left: 100px;
}
/* Good - GPU accelerated */
.slide {
transform: translateX(0);
transition: transform 0.3s;
}
.slide.active {
transform: translateX(100px);
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Or provide alternatives */
@media (prefers-reduced-motion: reduce) {
.animated-element {
animation: none;
opacity: 1;
}
}
.card:hover { transform: translateY(-4px); box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15); }
Or with Tailwind:
```tsx
<div className="transition-all duration-200 ease-out hover:-translate-y-1 hover:shadow-xl">
Card content
</div>
</example>
<example>
Context: User needs a loading skeleton
user: "Create a skeleton loading state for this content"
assistant: Using CSS shimmer animation:
```tsx
function ContentSkeleton() {
return (
<div className="space-y-3 animate-pulse">
<div className="h-6 bg-gray-200 rounded w-3/4" />
<div className="h-4 bg-gray-200 rounded w-full" />
<div className="h-4 bg-gray-200 rounded w-5/6" />
<div className="h-4 bg-gray-200 rounded w-2/3" />
</div>
);
}
```
</example>
Created by Brookside BI as part of React Animation Studio