Motion

popcn/ui includes a comprehensive motion system designed for GenZ aesthetics. Bouncy, playful animations that feel responsive and delightful without being distracting.

Animation Tokens

Customize timing and easing through CSS custom properties.

Duration Tokens
--ap-dur-1100ms
--ap-dur-2200ms
--ap-dur-3300ms
--ap-dur-4500ms
--ap-dur-5800ms
Easing Tokens
--ap-ease-popBouncy
--ap-ease-softSoft
--ap-ease-snapSharp
--ap-ease-springElastic
:root {
  /* Duration tokens */
  --ap-dur-1: 100ms;   /* Micro: button press */
  --ap-dur-2: 200ms;   /* Quick: pop, snap */
  --ap-dur-3: 300ms;   /* Standard: most transitions */
  --ap-dur-4: 500ms;   /* Emphasis: entrance animations */
  --ap-dur-5: 800ms;   /* Dramatic: confetti, sequences */

  /* Easing tokens */
  --ap-ease-pop: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ap-ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
  --ap-ease-snap: cubic-bezier(0.2, 0, 0, 1);
  --ap-ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);

  /* Motion control */
  --ap-motion: 1;        /* 1=enabled, 0=disabled */
  --ap-motion-scale: 1;  /* Intensity multiplier */
}

定常モーションとクリック時モーション

motionIdle(定常)は float / shine / wave のように常時再生、motionClick(クリック時)は pop / snap / bounce / wiggle / jelly のようにクリックで発火します。 両方指定して組み合わせられます。

<Button motionIdle="shine" motionClick="pop">shine + pop</Button>
<Button motionIdle="wave" motionClick="bounce">wave + bounce</Button>

Motion Presets

motion で従来どおり一括指定も可能です。 定常とクリック時を分けたい場合は motionIdle / motionClick を使ってください。

<Button motion="pop">Pop</Button>
<Button motion="bounce">Bounce</Button>
<Button motion="wiggle">Wiggle</Button>
<Button motion="jelly">Jelly</Button>
<Button motion="float">Float</Button>
<Button motion="shine">Shine</Button>
<Button motion="wave">Wave</Button>
<Button motion="snap">Snap</Button>
<Button motion="none">None</Button>

Motion Details

Pop

Default motion preset

Quick scale bounce on click. Satisfying feedback that feels responsive.

<Button motion="pop" />
Bounce

Vertical bounce with scale

Playful vertical bounce combined with subtle scaling. Perfect for likes, notifications, and celebratory actions.

<Button motion="bounce" />
Wiggle

Horizontal shake animation

Attention-grabbing horizontal shake. Great for error states or drawing user attention.

<Button motion="wiggle" />
Jelly

Elastic squash and stretch

Playful jelly-like deformation. Adds a fun, bouncy character to interactions.

<Button motion="jelly" />
Float

Continuous gentle hover

Subtle floating animation that draws attention. Best for CTAs and featured actions.

<Button motion="float" />
Shine

Gradient sweep effect

::before の光の帯が左端から右端へ約2.5秒でスイープするループアニメーションです。常時動くため、クリック時の pop や bounce より控えめで、プライマリボタンにさりげないきらめきを足したいときに使います。

<Button motion="shine" />
Wave

Gradient wave effect

グラデーションの背景位置を波のように動かすループアニメーションです。約4秒かけてグラデーションが上下左右にゆらめき、有機的な動きを生み出します。常時動くため、控えめなアクセントとして使えます。

<Button motion="wave" />
Snap

Instant press feedback

Immediate scale reduction on press. Creates tactile button-press feel.

<Button motion="snap" />

Utility Classes

Pre-built utility classes for common animation patterns.

Hover Effects

Float.ap-hover-float

Hover to see the float effect

Scale.ap-hover-scale

Hover to see the scale effect

Glow.ap-hover-glow

Hover to see the glow effect

Tilt.ap-hover-tilt

Hover to see the tilt effect

Active Effects

Squish.ap-active-squish

Click and hold to see the squish effect

Press.ap-active-press

Click and hold to see the press effect

Enter/Exit Animations

Pop In.ap-enter-pop

Scale from 0.9 with fade

Slide In.ap-enter-slide

Slide up from below

Bounce In.ap-enter-bounce

Bouncy entrance

Jelly In.ap-enter-jelly

Elastic jelly entrance

/* Transition presets */
.ap-trans-pop    /* Bouncy transition */
.ap-trans-snap   /* Sharp transition */
.ap-trans-soft   /* Soft transition */
.ap-trans-spring /* Elastic transition */

/* Hover effects */
.ap-hover-float  /* Float up on hover */
.ap-hover-glow   /* Glow on hover */
.ap-hover-scale  /* Scale up on hover */
.ap-hover-tilt   /* Tilt on hover */

/* Active effects */
.ap-active-squish /* Squish on press */
.ap-active-press  /* Press down on click */

/* Enter animations */
.ap-enter-pop    /* Pop in */
.ap-enter-slide  /* Slide up */
.ap-enter-bounce /* Bounce in */
.ap-enter-jelly  /* Jelly in */

/* Exit animations */
.ap-exit-fade    /* Fade out */
.ap-exit-slide   /* Slide down */

/* Special effects */
.ap-wiggle       /* Shake animation */
.ap-pulse-ring   /* Ripple effect */
.ap-confetti     /* Celebration */

Card Motion Variants

Cards support motion variants for interactive hover effects.

Float

motion="float"

Scale

motion="scale"

Tilt

motion="tilt"

Glow

motion="glow"

<Card motion="float">Float on hover</Card>
<Card motion="scale">Scale on hover</Card>
<Card motion="tilt">Tilt on hover</Card>
<Card motion="glow">Glow on hover</Card>

Tailwind Configuration

Extend your Tailwind config to include the animation utilities.

// tailwind.config.ts
export default {
  theme: {
    extend: {
      keyframes: {
        "ap-bounce": {
          "0%, 100%": { transform: "translateY(0) scale(1)" },
          "30%": { transform: "translateY(-8px) scale(1.02)" },
          "50%": { transform: "translateY(-4px) scale(1)" },
          "70%": { transform: "translateY(-2px) scale(1.01)" },
        },
        "ap-wiggle": {
          "0%, 100%": { transform: "translateX(0)" },
          "10%, 30%": { transform: "translateX(-4px)" },
          "20%, 40%": { transform: "translateX(4px)" },
          // ... shake pattern
        },
        "ap-jelly": {
          "0%": { transform: "scale(1, 1)" },
          "25%": { transform: "scale(0.95, 1.05)" },
          "50%": { transform: "scale(1.05, 0.95)" },
          "75%": { transform: "scale(0.98, 1.02)" },
          "100%": { transform: "scale(1, 1)" },
        },
        // ... more keyframes
      },
      animation: {
        "ap-bounce": "ap-bounce var(--ap-dur-3) var(--ap-ease-pop)",
        "ap-wiggle": "ap-wiggle var(--ap-dur-4) var(--ap-ease-snap)",
        "ap-jelly": "ap-jelly var(--ap-dur-3) var(--ap-ease-spring)",
        // ... more animations
      },
      transitionDuration: {
        "ap-1": "var(--ap-dur-1)",
        "ap-2": "var(--ap-dur-2)",
        "ap-3": "var(--ap-dur-3)",
        "ap-4": "var(--ap-dur-4)",
        "ap-5": "var(--ap-dur-5)",
      },
      transitionTimingFunction: {
        "ap-pop": "var(--ap-ease-pop)",
        "ap-soft": "var(--ap-ease-soft)",
        "ap-snap": "var(--ap-ease-snap)",
        "ap-spring": "var(--ap-ease-spring)",
      },
    },
  },
}

Accessibility

popcn/ui respects user motion preferences and provides multiple ways to control animations.

1. System Preference

Automatically respects prefers-reduced-motion. When enabled, --ap-motion is set to 0.

2. CSS Variable Control

Set --ap-motion: 0 to disable animations programmatically. Use --ap-motion-scale to adjust intensity (0.5 = subtle, 1 = normal).

3. Utility Class

Add .ap-motion-reduce to any element to disable animations for that subtree. Alternatively, use .ap-no-motion on the root.

4. Functionality Preserved

All interactive elements remain fully functional without animations. Visual feedback through color changes and focus states is maintained.

/* System preference auto-detection */
@media (prefers-reduced-motion: reduce) {
  :root {
    --ap-motion: 0;
    --ap-motion-scale: 0;
  }
}

/* Manual disable class */
.ap-no-motion {
  --ap-motion: 0;
}

/* Disable for specific section */
.ap-motion-reduce,
.ap-motion-reduce * {
  --ap-motion: 0 !important;
  animation: none !important;
  transition-duration: 0.01ms !important;
}

/* Adjust intensity without fully disabling */
.subtle-motion {
  --ap-motion-scale: 0.5;
}

Best Practices

Use meaningful motion: Animation should provide feedback, not just decoration. Pop for clicks, wiggle for errors, bounce for success.

Keep it subtle: GenZ aesthetics favor quick, bouncy animations. Use --ap-dur-2 (200ms) for most interactions.

Combine wisely: Hover effects + click animations work great together. Avoid combining multiple continuous animations.

Test with reduced motion: Always verify your UI works with animations disabled. Use the toggle at the top of this page to test.