
Frontend Ui Animator
- 4 installs
- 53 repo stars
- Updated December 9, 2025
- julianromli/droid-factory-template
Add smooth animations and motion to UI components.
About
Implements micro-interactions and transitions for polished UX. Uses CSS and JavaScript animation techniques.
- Motion and transitions
- Micro-interactions
Frontend Ui Animator by the numbers
- 4 all-time installs (skills.sh)
- Ranked #1,524 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/julianromli/droid-factory-template --skill frontend-ui-animatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4 |
|---|---|
| repo stars | ★ 53 |
| Last updated | December 9, 2025 |
| Repository | julianromli/droid-factory-template ↗ |
What it does
Add smooth animations and motion to UI components.
Files
Frontend UI Animator
Implement purposeful, performant animations that enhance UX without overwhelming users. Focus on key moments: hero intros, hover feedback, content reveals, and navigation transitions.
Core Philosophy
"You don't need animations everywhere" - Prioritize:
| Priority | Area | Purpose |
|---|---|---|
| 1 | Hero Intro | First impression, brand personality |
| 2 | Hover Interactions | Feedback, discoverability |
| 3 | Content Reveal | Guide attention, reduce cognitive load |
| 4 | Background Effects | Atmosphere, depth |
| 5 | Navigation Transitions | Spatial awareness, continuity |
Workflow
Execute phases sequentially. Complete each before proceeding.
Phase 1: Analyze
1. Scan project structure - Identify all pages in app/ and components in components/ 2. Check existing setup - Review tailwind.config.ts for existing animations/keyframes 3. Identify animation candidates - List components by priority category 4. Document constraints - Note installed animation libraries (framer-motion, etc.)
Output: Animation audit table. See references/component-checklist.md.
Phase 2: Plan
1. Map animations to components - Assign specific animation patterns 2. Determine triggers - Load, scroll (intersection), hover, click 3. Estimate effort - Low (CSS only), Medium (hooks needed), High (library required) 4. Propose phased rollout - Quick wins first
Output: Implementation plan with component → animation mapping.
Phase 3: Implement
1. Extend Tailwind config - Add keyframes and animation utilities 2. Add reduced-motion support - Accessibility first 3. Create reusable hooks - useScrollReveal, useMousePosition if needed 4. Apply animations per component - Follow patterns in references/animation-patterns.md
Performance rules:
// ✅ DO: Use transforms and opacity only
transform: translateY(20px);
opacity: 0.5;
filter: blur(4px);
// ❌ DON'T: Animate layout properties
margin-top: 20px;
height: 100px;
width: 200px;Phase 4: Verify
1. Test in browser - Visual QA all animations 2. Test reduced-motion - Verify prefers-reduced-motion works 3. Check CLS - No layout shifts from animations 4. Performance audit - No jank on scroll animations
Quick Reference
Animation Triggers
| Trigger | Implementation |
|---|---|
| Page load | CSS animation with animation-delay for stagger |
| Scroll into view | IntersectionObserver or react-intersection-observer |
| Hover | Tailwind hover: utilities or CSS :hover |
| Click/Tap | State-driven with useState |
Common Patterns
Staggered children:
{items.map((item, i) => (
<div
key={item.id}
style={{ animationDelay: `${i * 100}ms` }}
className="animate-fade-slide-in"
/>
))}Scroll reveal hook:
const useScrollReveal = (threshold = 0.1) => {
const ref = useRef<HTMLDivElement>(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => entry.isIntersecting && setIsVisible(true),
{ threshold }
);
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, [threshold]);
return { ref, isVisible };
};Usage:
const { ref, isVisible } = useScrollReveal();
<div ref={ref} className={isVisible ? 'animate-fade-in' : 'opacity-0'} />Resources
- Animation patterns: See
references/animation-patterns.md - Audit template: See
references/component-checklist.md - Tailwind presets: See
references/tailwind-presets.md
Technical Stack
- CSS animations: Default for simple effects
- Tailwind utilities: For hover states and basic animations
- Framer Motion: For complex orchestration, gestures, layout animations
- GSAP: For timeline-based sequences (if already installed)
Accessibility (Required)
Always include in global CSS:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}Animation Patterns Reference
Ready-to-use animation patterns for Next.js + Tailwind + React.
---
1. Intro / Page Load Animations
Fade + Slide + Blur In
@keyframes fadeSlideIn {
from {
opacity: 0.01;
transform: translateY(20px);
filter: blur(4px);
}
to {
opacity: 1;
transform: translateY(0);
filter: blur(0);
}
}
.animate-fade-slide-in {
animation: fadeSlideIn 0.6s ease-out both;
}Clip-Path Reveal (Horizontal)
@keyframes clipRevealX {
from { clip-path: inset(0 100% 0 0); }
to { clip-path: inset(0 0 0 0); }
}
.animate-clip-reveal {
animation: clipRevealX 0.8s ease-out both;
}Clip-Path Reveal (Vertical)
@keyframes clipRevealY {
from { clip-path: inset(100% 0 0 0); }
to { clip-path: inset(0 0 0 0); }
}Scale In
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}---
2. Button Animations
Hover Lift + Shadow
className="transition-all duration-200 hover:-translate-y-0.5 hover:scale-[1.02] hover:shadow-lg"Border Beam Effect
.btn-beam {
position: relative;
overflow: hidden;
}
.btn-beam::before {
content: '';
position: absolute;
inset: 0;
border: 1px solid transparent;
border-radius: inherit;
background: linear-gradient(90deg, transparent, var(--accent, #d4af37), transparent) border-box;
mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: 0;
transition: opacity 0.3s;
}
.btn-beam:hover::before {
opacity: 1;
animation: beamRotate 2s linear infinite;
}
@keyframes beamRotate {
to { transform: rotate(360deg); }
}Pulse on Hover
className="hover:animate-pulse"Ripple Effect (Click)
const ButtonRipple = ({ children, ...props }) => {
const [ripples, setRipples] = useState([]);
const handleClick = (e) => {
const rect = e.currentTarget.getBoundingClientRect();
const ripple = {
x: e.clientX - rect.left,
y: e.clientY - rect.top,
id: Date.now(),
};
setRipples((prev) => [...prev, ripple]);
setTimeout(() => {
setRipples((prev) => prev.filter((r) => r.id !== ripple.id));
}, 600);
};
return (
<button onClick={handleClick} className="relative overflow-hidden" {...props}>
{children}
{ripples.map((ripple) => (
<span
key={ripple.id}
className="absolute bg-white/30 rounded-full animate-ripple"
style={{ left: ripple.x, top: ripple.y }}
/>
))}
</button>
);
};@keyframes ripple {
from {
width: 0;
height: 0;
opacity: 0.5;
transform: translate(-50%, -50%);
}
to {
width: 200px;
height: 200px;
opacity: 0;
transform: translate(-50%, -50%);
}
}---
3. Text Animations
Letter-by-Letter Reveal
const AnimatedText = ({ text, className = '' }) => (
<span className={cn("inline-flex overflow-hidden", className)}>
{text.split('').map((char, i) => (
<span
key={i}
className="animate-slide-up"
style={{ animationDelay: `${i * 50}ms` }}
>
{char === ' ' ? '\u00A0' : char}
</span>
))}
</span>
);@keyframes slideUp {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.animate-slide-up {
animation: slideUp 0.5s ease-out both;
}Word-by-Word Reveal
const AnimatedHeading = ({ text }) => (
<h1>
{text.split(' ').map((word, i) => (
<span
key={i}
className="inline-block animate-fade-slide-in"
style={{ animationDelay: `${i * 150}ms` }}
>
{word}
</span>
))}
</h1>
);Typewriter Effect
const Typewriter = ({ text, speed = 50 }) => {
const [displayed, setDisplayed] = useState('');
useEffect(() => {
let i = 0;
const timer = setInterval(() => {
if (i < text.length) {
setDisplayed(text.slice(0, i + 1));
i++;
} else {
clearInterval(timer);
}
}, speed);
return () => clearInterval(timer);
}, [text, speed]);
return <span>{displayed}<span className="animate-blink">|</span></span>;
};---
4. Card Animations
Hover Scale + Shadow
className="transition-all duration-300 hover:scale-[1.02] hover:shadow-xl"Image Zoom on Hover
<div className="overflow-hidden rounded-lg">
<img
className="transition-transform duration-500 hover:scale-110"
src={src}
alt={alt}
/>
</div>Flashlight / Spotlight Effect
const FlashlightCard = ({ children }) => {
const [pos, setPos] = useState({ x: 0, y: 0 });
const [isHovered, setIsHovered] = useState(false);
const handleMouseMove = (e) => {
const rect = e.currentTarget.getBoundingClientRect();
setPos({
x: e.clientX - rect.left,
y: e.clientY - rect.top,
});
};
return (
<div
onMouseMove={handleMouseMove}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className="relative overflow-hidden rounded-xl bg-card"
style={{
background: isHovered
? `radial-gradient(300px circle at ${pos.x}px ${pos.y}px, rgba(255,255,255,0.06), transparent)`
: undefined,
}}
>
{children}
</div>
);
};Border Glow on Hover
.card-glow {
--mouse-x: 50%;
--mouse-y: 50%;
position: relative;
}
.card-glow::before {
content: '';
position: absolute;
inset: -1px;
border-radius: inherit;
background: radial-gradient(
200px circle at var(--mouse-x) var(--mouse-y),
rgba(212, 175, 55, 0.4),
transparent
);
opacity: 0;
transition: opacity 0.3s;
z-index: -1;
}
.card-glow:hover::before {
opacity: 1;
}---
5. Marquee / Infinite Loop
CSS-Only Marquee
const Marquee = ({ children, speed = 30 }) => (
<div className="overflow-hidden [mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)]">
<div
className="flex gap-8 animate-marquee"
style={{ animationDuration: `${speed}s` }}
>
{children}
{children} {/* Duplicate for seamless loop */}
</div>
</div>
);@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
.animate-marquee {
animation: marquee linear infinite;
}Pause on Hover
className="animate-marquee hover:[animation-play-state:paused]"---
6. Scroll-Triggered Animations
useScrollReveal Hook
import { useEffect, useRef, useState } from 'react';
export const useScrollReveal = (options = {}) => {
const { threshold = 0.1, triggerOnce = true } = options;
const ref = useRef<HTMLDivElement>(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const element = ref.current;
if (!element) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
if (triggerOnce) observer.unobserve(element);
} else if (!triggerOnce) {
setIsVisible(false);
}
},
{ threshold }
);
observer.observe(element);
return () => observer.disconnect();
}, [threshold, triggerOnce]);
return { ref, isVisible };
};ScrollReveal Component
const ScrollReveal = ({
children,
className = '',
animation = 'animate-fade-slide-in',
delay = 0
}) => {
const { ref, isVisible } = useScrollReveal();
return (
<div
ref={ref}
className={cn(
'transition-opacity',
isVisible ? animation : 'opacity-0',
className
)}
style={{ animationDelay: `${delay}ms` }}
>
{children}
</div>
);
};Staggered Children on Scroll
const StaggeredReveal = ({ children }) => {
const { ref, isVisible } = useScrollReveal();
return (
<div ref={ref}>
{Children.map(children, (child, i) => (
<div
className={isVisible ? 'animate-fade-slide-in' : 'opacity-0'}
style={{ animationDelay: `${i * 100}ms` }}
>
{child}
</div>
))}
</div>
);
};---
7. Background Animations
Subtle Float
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.animate-float {
animation: float 3s ease-in-out infinite;
}Ken Burns (Image)
@keyframes kenburns {
0% { transform: scale(1); }
100% { transform: scale(1.1); }
}
.animate-kenburns {
animation: kenburns 20s ease-out forwards;
}Gradient Shift
@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
.animate-gradient {
background-size: 200% 200%;
animation: gradientShift 15s ease infinite;
}---
8. Navigation / Page Transitions
Navbar Scroll Effect
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => setScrolled(window.scrollY > 50);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
<nav className={cn(
"fixed top-0 transition-all duration-300",
scrolled ? "bg-background/80 backdrop-blur-md shadow-lg" : "bg-transparent"
)}>Mobile Menu Slide
<div className={cn(
"fixed inset-y-0 right-0 w-64 bg-background transform transition-transform duration-300",
isOpen ? "translate-x-0" : "translate-x-full"
)}>---
Framer Motion Patterns
Stagger Container
import { motion } from 'framer-motion';
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: { staggerChildren: 0.1 }
}
};
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 }
};
<motion.div variants={container} initial="hidden" animate="show">
{items.map((i) => (
<motion.div key={i} variants={item}>{i}</motion.div>
))}
</motion.div>Scroll-Triggered
import { motion, useInView } from 'framer-motion';
const ref = useRef(null);
const isInView = useInView(ref, { once: true });
<motion.div
ref={ref}
initial={{ opacity: 0, y: 50 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5 }}
/>Layout Animation
<motion.div layout layoutId="unique-id">
{/* Content that changes size/position */}
</motion.div>Component Animation Checklist
Use this template to audit and plan animations for a project.
---
Project Analysis Template
1. Project Structure Scan
# Pages to analyze
app/
├── page.tsx # Homepage
├── about/page.tsx # About page
├── services/page.tsx # Services page
└── contact/page.tsx # Contact page
# Components to analyze
components/
├── ui/ # Base UI components
├── sections/ # Page sections
└── layout/ # Layout components2. Technical Setup Check
| Item | Status | Notes |
|---|---|---|
| Tailwind config has keyframes | ☐ | Check theme.extend.keyframes |
| Tailwind config has animations | ☐ | Check theme.extend.animation |
| Framer Motion installed | ☐ | Check package.json |
| GSAP installed | ☐ | Check package.json |
| Reduced motion CSS exists | ☐ | Check globals.css |
---
Animation Audit Table
Priority 1: Hero / First Impression
| Component | Current State | Proposed Animation | Trigger | Effort |
|---|---|---|---|---|
| Hero heading | Static | Fade + slide up, word-by-word | Load | Low |
| Hero subtext | Static | Fade in with delay | Load | Low |
| Hero CTA button | Static | Fade in + hover lift | Load/Hover | Low |
| Hero image | Static | Scale in or ken burns | Load | Medium |
| Hero background | Static | Gradient shift or parallax | Load/Scroll | Medium |
Priority 2: Hover Interactions
| Component | Current State | Proposed Animation | Trigger | Effort |
|---|---|---|---|---|
| Navigation links | Basic hover | Underline slide or color | Hover | Low |
| Buttons (all) | Basic hover | Lift + shadow | Hover | Low |
| Cards | Basic hover | Scale + shadow | Hover | Low |
| Images in cards | Static | Zoom on parent hover | Hover | Low |
| Social icons | Basic hover | Scale + color | Hover | Low |
Priority 3: Content Reveal (Scroll)
| Component | Current State | Proposed Animation | Trigger | Effort |
|---|---|---|---|---|
| Section headings | Static | Fade + slide up | Scroll | Medium |
| Feature cards | Static | Staggered fade in | Scroll | Medium |
| Testimonials | Static | Slide in from sides | Scroll | Medium |
| Stats/Numbers | Static | Count up animation | Scroll | High |
| Images | Static | Fade + scale | Scroll | Medium |
Priority 4: Background / Atmosphere
| Component | Current State | Proposed Animation | Trigger | Effort |
|---|---|---|---|---|
| Page background | Solid color | Gradient mesh | Always | Low |
| Section dividers | Static | Wave or curve | Always | Low |
| Decorative elements | Static | Float | Always | Low |
| Noise/grain overlay | None | Subtle texture | Always | Low |
Priority 5: Navigation / Transitions
| Component | Current State | Proposed Animation | Trigger | Effort |
|---|---|---|---|---|
| Navbar | Static | Scroll-based blur/shadow | Scroll | Medium |
| Mobile menu | Instant show/hide | Slide in from right | Click | Medium |
| Page transitions | None | Fade or clip reveal | Route change | High |
| Scroll to section | Instant jump | Smooth scroll | Click | Low |
---
Implementation Phases
Phase 1: Quick Wins (1-2 hours)
Focus on CSS-only animations that require no new dependencies:
- [ ] Add base keyframes to Tailwind config
- [ ] Add reduced-motion support to globals.css
- [ ] Hero section fade-in animations
- [ ] Button hover effects (all buttons)
- [ ] Card hover effects (scale + shadow)
- [ ] Navbar scroll effect
Phase 2: Scroll Reveals (2-3 hours)
Add IntersectionObserver-based animations:
- [ ] Create
useScrollRevealhook - [ ] Section headings reveal
- [ ] Feature/service cards staggered reveal
- [ ] Testimonials reveal
- [ ] Footer content reveal
Phase 3: Enhanced Effects (3-4 hours)
More complex animations:
- [ ] Hero text letter/word animation
- [ ] Image zoom on card hover
- [ ] Marquee for logos/partners
- [ ] Flashlight effect on cards (if applicable)
- [ ] Mobile menu slide animation
Phase 4: Polish (Optional, 2+ hours)
Advanced effects requiring more effort:
- [ ] Page transitions (Framer Motion)
- [ ] Number count-up animations
- [ ] Parallax backgrounds
- [ ] Border beam effects on CTAs
- [ ] Custom cursor effects
---
Animation Token Reference
Use consistent timing and easing across the project:
| Token | Value | Use Case |
|---|---|---|
duration-fast | 150ms | Micro-interactions, hovers |
duration-normal | 300ms | Standard transitions |
duration-slow | 500ms | Reveals, entrances |
duration-slower | 800ms | Hero animations |
ease-out | cubic-bezier(0, 0, 0.2, 1) | Entrances |
ease-in-out | cubic-bezier(0.4, 0, 0.2, 1) | Continuous |
ease-spring | cubic-bezier(0.34, 1.56, 0.64, 1) | Bouncy effects |
---
Quality Checklist
Before marking animation implementation complete:
- [ ] All animations respect
prefers-reduced-motion - [ ] No layout shifts (CLS) caused by animations
- [ ] Animations don't block user interaction
- [ ] Stagger delays are reasonable (not too slow)
- [ ] Hover states have appropriate transition duration
- [ ] Mobile performance is acceptable (test on real device)
- [ ] Animations enhance, not distract from content
Tailwind Animation Presets
Copy-paste ready configurations for tailwind.config.ts.
---
Complete Animation Config
Add to theme.extend in your Tailwind config:
// tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
// ... other config
theme: {
extend: {
// ... other extensions
keyframes: {
// === FADE ANIMATIONS ===
'fade-in': {
from: { opacity: '0' },
to: { opacity: '1' },
},
'fade-out': {
from: { opacity: '1' },
to: { opacity: '0' },
},
'fade-slide-in': {
from: {
opacity: '0',
transform: 'translateY(20px)',
filter: 'blur(4px)',
},
to: {
opacity: '1',
transform: 'translateY(0)',
filter: 'blur(0)',
},
},
'fade-slide-in-right': {
from: {
opacity: '0',
transform: 'translateX(20px)',
},
to: {
opacity: '1',
transform: 'translateX(0)',
},
},
'fade-slide-in-left': {
from: {
opacity: '0',
transform: 'translateX(-20px)',
},
to: {
opacity: '1',
transform: 'translateX(0)',
},
},
// === SCALE ANIMATIONS ===
'scale-in': {
from: {
opacity: '0',
transform: 'scale(0.9)',
},
to: {
opacity: '1',
transform: 'scale(1)',
},
},
'scale-out': {
from: {
opacity: '1',
transform: 'scale(1)',
},
to: {
opacity: '0',
transform: 'scale(0.9)',
},
},
// === SLIDE ANIMATIONS ===
'slide-up': {
from: { transform: 'translateY(100%)' },
to: { transform: 'translateY(0)' },
},
'slide-down': {
from: { transform: 'translateY(-100%)' },
to: { transform: 'translateY(0)' },
},
'slide-left': {
from: { transform: 'translateX(100%)' },
to: { transform: 'translateX(0)' },
},
'slide-right': {
from: { transform: 'translateX(-100%)' },
to: { transform: 'translateX(0)' },
},
// === CLIP-PATH REVEALS ===
'clip-reveal-right': {
from: { clipPath: 'inset(0 100% 0 0)' },
to: { clipPath: 'inset(0 0 0 0)' },
},
'clip-reveal-left': {
from: { clipPath: 'inset(0 0 0 100%)' },
to: { clipPath: 'inset(0 0 0 0)' },
},
'clip-reveal-up': {
from: { clipPath: 'inset(100% 0 0 0)' },
to: { clipPath: 'inset(0 0 0 0)' },
},
'clip-reveal-down': {
from: { clipPath: 'inset(0 0 100% 0)' },
to: { clipPath: 'inset(0 0 0 0)' },
},
// === CONTINUOUS ANIMATIONS ===
'marquee': {
from: { transform: 'translateX(0)' },
to: { transform: 'translateX(-50%)' },
},
'marquee-reverse': {
from: { transform: 'translateX(-50%)' },
to: { transform: 'translateX(0)' },
},
'float': {
'0%, 100%': { transform: 'translateY(0)' },
'50%': { transform: 'translateY(-10px)' },
},
'pulse-soft': {
'0%, 100%': { opacity: '1' },
'50%': { opacity: '0.7' },
},
'spin-slow': {
from: { transform: 'rotate(0deg)' },
to: { transform: 'rotate(360deg)' },
},
'blink': {
'0%, 100%': { opacity: '1' },
'50%': { opacity: '0' },
},
// === BACKGROUND ANIMATIONS ===
'gradient-shift': {
'0%, 100%': { backgroundPosition: '0% 50%' },
'50%': { backgroundPosition: '100% 50%' },
},
'kenburns': {
from: { transform: 'scale(1)' },
to: { transform: 'scale(1.1)' },
},
// === SPECIAL EFFECTS ===
'ripple': {
from: {
width: '0',
height: '0',
opacity: '0.5',
},
to: {
width: '200px',
height: '200px',
opacity: '0',
},
},
'beam-rotate': {
to: { transform: 'rotate(360deg)' },
},
'shimmer': {
from: { backgroundPosition: '-200% 0' },
to: { backgroundPosition: '200% 0' },
},
},
animation: {
// === FADE ===
'fade-in': 'fade-in 0.5s ease-out both',
'fade-out': 'fade-out 0.3s ease-out both',
'fade-slide-in': 'fade-slide-in 0.6s ease-out both',
'fade-slide-in-right': 'fade-slide-in-right 0.5s ease-out both',
'fade-slide-in-left': 'fade-slide-in-left 0.5s ease-out both',
// === SCALE ===
'scale-in': 'scale-in 0.3s ease-out both',
'scale-out': 'scale-out 0.2s ease-in both',
// === SLIDE ===
'slide-up': 'slide-up 0.5s ease-out both',
'slide-down': 'slide-down 0.5s ease-out both',
'slide-left': 'slide-left 0.5s ease-out both',
'slide-right': 'slide-right 0.5s ease-out both',
// === CLIP REVEAL ===
'clip-reveal-right': 'clip-reveal-right 0.8s ease-out both',
'clip-reveal-left': 'clip-reveal-left 0.8s ease-out both',
'clip-reveal-up': 'clip-reveal-up 0.8s ease-out both',
'clip-reveal-down': 'clip-reveal-down 0.8s ease-out both',
// === CONTINUOUS ===
'marquee': 'marquee 30s linear infinite',
'marquee-fast': 'marquee 15s linear infinite',
'marquee-slow': 'marquee 45s linear infinite',
'marquee-reverse': 'marquee-reverse 30s linear infinite',
'float': 'float 3s ease-in-out infinite',
'pulse-soft': 'pulse-soft 2s ease-in-out infinite',
'spin-slow': 'spin-slow 8s linear infinite',
'blink': 'blink 1s step-end infinite',
// === BACKGROUND ===
'gradient-shift': 'gradient-shift 15s ease infinite',
'kenburns': 'kenburns 20s ease-out forwards',
// === SPECIAL ===
'ripple': 'ripple 0.6s ease-out forwards',
'beam-rotate': 'beam-rotate 2s linear infinite',
'shimmer': 'shimmer 2s linear infinite',
},
},
},
};
export default config;---
Global CSS Additions
Add to globals.css:
/* === 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;
}
}
/* === UTILITY CLASSES === */
/* Stagger delay utilities */
.stagger-1 { animation-delay: 100ms; }
.stagger-2 { animation-delay: 200ms; }
.stagger-3 { animation-delay: 300ms; }
.stagger-4 { animation-delay: 400ms; }
.stagger-5 { animation-delay: 500ms; }
.stagger-6 { animation-delay: 600ms; }
.stagger-7 { animation-delay: 700ms; }
.stagger-8 { animation-delay: 800ms; }
/* Pause animation on hover (for marquees) */
.hover-pause:hover {
animation-play-state: paused;
}
/* Marquee mask for fade edges */
.marquee-mask {
mask-image: linear-gradient(
to right,
transparent 0%,
black 10%,
black 90%,
transparent 100%
);
}
/* Button beam effect base */
.btn-beam {
position: relative;
overflow: hidden;
}
.btn-beam::before {
content: '';
position: absolute;
inset: 0;
border: 1px solid transparent;
border-radius: inherit;
background: linear-gradient(90deg, transparent, hsl(var(--primary)), transparent) border-box;
mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
mask-composite: exclude;
opacity: 0;
transition: opacity 0.3s;
}
.btn-beam:hover::before {
opacity: 1;
animation: beam-rotate 2s linear infinite;
}
/* Shimmer skeleton effect */
.shimmer {
background: linear-gradient(
90deg,
hsl(var(--muted)) 0%,
hsl(var(--muted-foreground) / 0.1) 50%,
hsl(var(--muted)) 100%
);
background-size: 200% 100%;
animation: shimmer 2s linear infinite;
}---
Minimal Config (Quick Start)
If you need just the essentials:
keyframes: {
'fade-slide-in': {
from: { opacity: '0', transform: 'translateY(20px)' },
to: { opacity: '1', transform: 'translateY(0)' },
},
'scale-in': {
from: { opacity: '0', transform: 'scale(0.95)' },
to: { opacity: '1', transform: 'scale(1)' },
},
'marquee': {
from: { transform: 'translateX(0)' },
to: { transform: 'translateX(-50%)' },
},
},
animation: {
'fade-slide-in': 'fade-slide-in 0.5s ease-out both',
'scale-in': 'scale-in 0.3s ease-out both',
'marquee': 'marquee 30s linear infinite',
},---
Usage Examples
Hero Section
<section className="relative">
<h1 className="animate-fade-slide-in">Welcome</h1>
<p className="animate-fade-slide-in stagger-1">Subtitle text</p>
<button className="animate-fade-slide-in stagger-2 hover:-translate-y-0.5 hover:shadow-lg transition-all">
Get Started
</button>
</section>Card Grid with Stagger
<div className="grid grid-cols-3 gap-6">
{cards.map((card, i) => (
<div
key={card.id}
className="animate-fade-slide-in hover:scale-[1.02] hover:shadow-xl transition-all"
style={{ animationDelay: `${i * 100}ms` }}
>
{card.content}
</div>
))}
</div>Logo Marquee
<div className="overflow-hidden marquee-mask">
<div className="flex gap-8 animate-marquee hover-pause">
{[...logos, ...logos].map((logo, i) => (
<img key={i} src={logo} className="h-8 w-auto" />
))}
</div>
</div>Button with Effects
<button className="btn-beam px-6 py-3 rounded-full bg-primary text-primary-foreground hover:-translate-y-0.5 hover:shadow-lg transition-all">
Contact Us
</button>