Animations Getting Started
This section introduces the TaylorUI animation approach:
- Build with Tailwind-native
@utilityanimation classes. - Use Framer Motion when interaction or orchestration needs JS.
- Reuse shared variants and presets from
@/animations.
1. CSS animation library (default)
Use CSS first for most UI states. Each animation is a Tailwind @utility class
that works like any other utility — combine with variants, compose with other
classes, and get autocomplete in your editor.
{
/* Trigger on data-state="open" (hover, click, accordion, etc.) */
}
<div className="data-[state=open]:animate-fade-up" data-state="closed">
Animated element
</div>;Available animation utilities:
| Utility | Description |
|---|---|
animate-fade-up | Fades in from below |
animate-fade-down | Fades in from above |
animate-fade-in | Simple fade in |
animate-fade-left | Fades in from the right |
animate-fade-right | Fades in from the left |
animate-zoom-in | Scales up from 95% |
animate-spin | Infinite rotation (tailwind native) |
animate-ping | Ping effect (tailwind native) |
animate-pulse | Pulse effect (tailwind native) |
animate-bounce | Bounce effect (tailwind native) |
Toggle data-state yourself to trigger CSS utilities on hover, click, or
other interactions:
<div
className="data-[state=hidden]:opacity-0 data-[state=visible]:animate-fade-up"
data-state={isVisible ? 'visible' : 'hidden'}
>
Controlled element
</div>For scroll-into-view reveals, use MotionReveal instead.
2. Framer Motion (when CSS is not enough)
Use Framer for gesture-driven interactions, coordinated stagger timelines, or route-level transitions that are easier to express in JS.
import { motion } from 'framer-motion';
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} />;3. Reusing our variants and presets
Import directly from @/animations to keep motion behavior consistent across
projects.
import { motion } from 'framer-motion';
import { fadeUp, pageTransition, staggerContainer } from '@/animations';
<motion.ul variants={staggerContainer} initial="hidden" animate="visible">
<motion.li variants={fadeUp}>Item one</motion.li>
<motion.li variants={fadeUp}>Item two</motion.li>
</motion.ul>;
<motion.div {...pageTransition}>Animated page content</motion.div>;4. MotionReveal
Universal motion-reveal wrapper for any child — grids, sections, cards. Wraps
content in motion.div, animates on viewport enter using shared Framer variants
(hidden / visible) from @/animations. Stagger via the delay prop (ms).
import { MotionReveal } from '@/animations';
<div className="grid gap-6 sm:grid-cols-2">
{articles.map((article, index) => (
<MotionReveal key={article.id} animation="fade-up" delay={index * 100}>
<Card {...article} />
</MotionReveal>
))}
</div>;| Prop | Type | Default | Description |
|---|---|---|---|
animation | string | 'fade-up' | fade-up, fade-in, fade-down, … |
delay | number | — | Stagger delay in milliseconds |
CardSkeleton mirrors the demo Card layout for loading states — same grid on
the Motion reveal page, with an On / Off toggle.
See that page for a full grid example with animation picker and skeleton preview.
5. CSS hover underline utilities
A single @utility class for animated underlines built with Tailwind's
@apply. The underline slides in from the left on hover and retreats
to the right on exit.
<a href="#" class="hover-underline">Hover me</a>Works with group hover too — just add group to the parent:
<div class="group">
<RiHeartLine />
<span class="hover-underline">Group hover underline</span>
</div>6. SlideUpOnGroupHover
A wrapper component that slides content up on parent hover, revealing a
duplicate underneath. Works inside any existing Button or ButtonLink.
import {
SlideUpOnGroupHover,
slideUpOnHoverGroupClassName,
} from '@/animations';
<Button className={slideUpOnHoverGroupClassName}>
<SlideUpOnGroupHover>Click me</SlideUpOnGroupHover>
</Button>;With an icon:
<Button
className={slideUpOnHoverGroupClassName}
variant="primary"
icon={<RiArrowRightCircleLine />}
>
<SlideUpOnGroupHover>With icon</SlideUpOnGroupHover>
</Button>7. ConfettiButton
A Framer Motion wrapper that spawns colored confetti particles on click.
Style it with the same classes you'd use for a Button.
import { ConfettiButton } from '@/animations';
<ConfettiButton className="focus-ring bg-primary-600 hover:bg-primary-700 rounded-lg px-4 py-2.5 text-white …">
Click for confetti!
</ConfettiButton>;8. ClickContentSwap
Swaps button content with a smooth enter/exit animation on click, then reverts after a configurable delay. Great for "Copy to clipboard" or "Saved!" feedback.
import { RiCheckLine } from '@remixicon/react';
import { ClickContentSwap } from '@/animations';
<ClickContentSwap
className="…"
swappedContent={
<>
<RiCheckLine />
Copied!
</>
}
>
Copy to clipboard
</ClickContentSwap>;| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Default button content |
swappedContent | ReactNode | — | Content shown after click |
duration | number | 1500 | ms before reverting |
className | string | — | Styles passed to the <button> |
onClick | () => void | — | Additional click handler |
9. Page Transitions:
TODO: add more information
Try out a page transition (swipe-right)
What to copy into another project
src/animations/css/src/animations/framer/src/animations/components/src/animations/index.ts
In your global stylesheet, include:
@import '../animations/css/animations.css';