
Svelte Ui Animator
- 18 installs
- 4 repo stars
- Updated December 6, 2025
- ajianaz/skills-collection
svelte-ui-animator is a Claude skill that implements purposeful, performant UI animations for Svelte and SvelteKit projects using Svelte transitions and actions.
About
This skill implements purposeful UI animations for Svelte and SvelteKit projects using Svelte's built-in transitions and actions. A developer uses it to add page-load intros, hover feedback, scroll reveals, and navigation transitions through a four-phase analyze, plan, implement, and verify workflow. It enforces transform/opacity-only performance rules and prefers-reduced-motion accessibility support.
- Implements purposeful Svelte transitions, actions, and animation patterns
- Runs a 4-phase workflow: analyze, plan, implement, verify
- Enforces performance and prefers-reduced-motion accessibility rules
Svelte Ui Animator by the numbers
- 18 all-time installs (skills.sh)
- Ranked #1,576 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
svelte-ui-animator capabilities & compatibility
- Capabilities
- svelte design · stunning animations · svelte ui integration
- Use cases
- ui design · frontend
What svelte-ui-animator says it does
Implement purposeful, performant animations using Svelte's built-in transitions and actions.
npx skills add https://github.com/ajianaz/skills-collection --skill svelte-ui-animatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 18 |
|---|---|
| repo stars | ★ 4 |
| Last updated | December 6, 2025 |
| Repository | ajianaz/skills-collection ↗ |
What it does
Add purposeful, performant animations to Svelte/SvelteKit UIs using transitions, actions, and scroll reveals.
Who is it for?
Developers adding motion and micro-interactions to Svelte/SvelteKit UIs the right way.
Skip if: Non-Svelte frameworks or projects that need no animation.
When should I use this skill?
When a user asks to add animations, animate a page/component, add hover or scroll effects, or page transitions in a Svelte project.
What you get
- Animation audit table
- Component-to-animation implementation plan
- Reusable Svelte actions like scrollReveal
By the numbers
- 5-priority animation priority table
- 4-phase workflow (analyze, plan, implement, verify)
Files
Svelte UI Animator
Implement purposeful, performant animations using Svelte's built-in transitions and actions. Focus on key moments: hero intros, hover feedback, content reveals, and navigation transitions with Svelte's reactive nature.
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 src/routes/ and components in src/lib/components/ 2. Check existing setup - Review tailwind.config.js and app.html for existing animations/keyframes 3. Identify animation candidates - List components by priority category 4. Document constraints - Note installed animation libraries (svelte/transition, auto-animate, 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 actions - scrollReveal, mousePosition, staggerAnimate if needed 4. Apply animations per component - Follow patterns in references/animation-patterns.md
Performance rules:
<!-- ✅ DO: Use transforms and opacity only -->
<div style="transform: translateY(20px); opacity: 0.5; filter: blur(4px);" />
<!-- ❌ DON'T: Animate layout properties -->
<div style="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 | Svelte actions with IntersectionObserver or on:viewportenter |
| Hover | Tailwind hover: utilities or Svelte mouseenter/mouseleave |
| Click/Tap | State-driven with Svelte reactive statements ($:) |
Common Patterns
Staggered children with Svelte transitions:
<script>
import { flip, fly } from 'svelte/transition';
</script>
{#each items as item, i (item.id)}
<div
in:fly={{ y: 20, delay: i * 50 }}
out:fly={{ y: -20 }}
>
{item.content}
</div>
{/each}Advanced scroll reveal action:
<!-- actions/scrollReveal.js -->
export function scrollReveal(node, options = {}) {
const {
threshold = 0.1,
animation = 'fade-slide-up',
delay = 0
} = options;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setTimeout(() => {
node.classList.add('animate-' + animation);
}, delay);
}
},
{ threshold }
);
observer.observe(node);
return {
destroy() => observer.disconnect()
};
}Usage with reactive state:
<script>
import { scrollReveal } from '$lib/actions/scrollReveal.js';
import { onMount } from 'svelte';
let isVisible = false;
let element;
onMount(() => {
const observer = new IntersectionObserver(
([entry]) => isVisible = entry.isIntersecting
);
observer.observe(element);
return () => observer.disconnect();
});
</script>
<div
bind:this={element}
use:scrollReveal={{ animation: 'fade-in', delay: 100 }}
class:visible={isVisible}
>
Content here
</div>Resources
- Animation patterns: See
references/animation-patterns.md - Audit template: See
references/component-checklist.md - Tailwind presets: See
references/tailwind-presets.md
Technical Stack
- Svelte transitions: Primary choice -
fade,fly,slide,scale,blur,crossfade - CSS animations: For complex keyframe animations not covered by Svelte transitions
- Tailwind utilities: For hover states and basic animations
- Auto-animate: For automatic layout animations (if installed)
- Svelte actions: For custom scroll-triggered and interactive animations
- Motion: For advanced gesture-based animations (if installed)
- 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>Related skills
FAQ
What animation approach does it use?
Svelte's built-in transitions (fade, fly, slide, scale, blur, crossfade), Svelte actions, CSS animations, and Tailwind utilities.
Does it handle accessibility?
Yes, it requires prefers-reduced-motion support and warns against animating layout properties to avoid layout shift.