# Animation Gallery

A collection of motion and animation experiments adapted from the standalone demos.

```tsx
import { css, on, type Handle, type RemixNode } from 'remix/ui'
import { DefaultAnimate } from './default-animate.tsx'
import { EnterAnimation } from './enter.tsx'
import { ExitAnimation } from './exit.tsx'
import { Press } from './press.tsx'
import { HTMLContent } from './html-content.tsx'
import { Keyframes } from './keyframes.tsx'
import { InterruptibleKeyframes } from './interruptible-keyframes.tsx'
import { RollingSquare } from './rolling-square.tsx'
import { Rotate } from './rotate.tsx'
import { TransitionOptions } from './transition-options.tsx'
import { Cube } from './cube.tsx'
import { SharedLayout } from './shared-layout.tsx'
import { AspectRatio } from './aspect-ratio.tsx'
import { BouncySwitch } from './bouncy-switch.tsx'
import { ColorInterpolation } from './color-interpolation.tsx'
import { FlipToggle } from './flip-toggle.tsx'
import { Reordering } from './reordering.tsx'
import { MultiStateBadge } from './multi-state-badge.tsx'
import { HoldToConfirm } from './hold-to-confirm.tsx'
import { MaterialRipple } from './material-ripple.tsx'

function Tile(
  handle: Handle<{ title: string; children: RemixNode; notes?: string }>,
) {
  let remountKey = 0

  return () => {
    let { title, children, notes } = handle.props

    return (
      <div
        mix={[
          css({
            backgroundColor: 'white',
            padding: '40px',
            borderRadius: 12,
            boxShadow: '0 0 10px 0 rgba(0, 0, 0, 0.1)',
            display: 'flex',
            alignItems: 'center',
            flexDirection: 'column',
            gap: 12,
            position: 'relative',
          }),
        ]}
      >
        <button
          mix={[
            css({
              position: 'absolute',
              bottom: 8,
              right: 8,
              width: 18,
              height: 18,
              padding: 0,
              border: 'none',
              background: 'transparent',
              cursor: 'pointer',
              opacity: 0.4,
              '&:hover': {
                opacity: 1,
              },
            }),
            on('click', () => {
              remountKey++
              handle.update()
            }),
          ]}
          title="Replay animation"
        >
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            strokeWidth="2"
          >
            <path d="M1 4v6h6M23 20v-6h-6" />
            <path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15" />
          </svg>
        </button>
        <h3 mix={[css({ margin: 0 })]}>{title}</h3>
        <div
          key={remountKey}
          mix={[
            css({
              flex: 1,
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              minHeight: 280,
            }),
          ]}
        >
          {children}
        </div>
        {notes && (
          <p
            mix={[
              css({
                margin: 0,
                fontSize: 12,
                color: '#666',
                textAlign: 'center',
                maxWidth: '200px',
              }),
            ]}
          >
            {notes}
          </p>
        )}
      </div>
    )
  }
}

export default function AnimationGallery() {
  return () => (
    <>
      <h1 mix={[css({ marginBottom: 0, '& + p': { marginTop: 0 } })]}>
        Animations
      </h1>
      <p>
        Most animations are adapted from{' '}
        <a href="https://www.motion.dev">Motion</a>. Thank you for your work{' '}
        <a href="https://motion.dev/@matt">Matt Perry</a>!
      </p>
      <div
        mix={[
          css({
            display: 'grid',
            gridTemplateColumns: 'repeat(auto-fill, minmax(320px, 1fr))',
            gap: 24,
            marginTop: 40,
          }),
        ]}
      >
        <Tile
          title="Default Animate"
          notes="animateEntrance + animateLayout defaults"
        >
          <DefaultAnimate />
        </Tile>
        <Tile
          title="Rolling Square"
          notes="CSS transition with spring() timing function"
        >
          <RollingSquare />
        </Tile>
        <Tile
          title="Enter Animation"
          notes="animateEntrance() with spring physics"
        >
          <EnterAnimation />
        </Tile>
        <Tile title="Exit Animation" notes="animateEntrance() + animateExit()">
          <ExitAnimation />
        </Tile>
        <Tile
          title="Press Interaction"
          notes="CSS transition + pointer/keyboard events"
        >
          <Press />
        </Tile>
        <Tile
          title="HTML Content"
          notes="rAF loop with spring iterator for text"
        >
          <HTMLContent />
        </Tile>
        <Tile title="Keyframes" notes="CSS @keyframes with infinite loop">
          <Keyframes />
        </Tile>
        <Tile
          title="Interruptible Keyframes"
          notes="Web Animations API with commitStyles()"
        >
          <InterruptibleKeyframes />
        </Tile>
        <Tile title="Rotate" notes="CSS @keyframes (one-shot)">
          <Rotate />
        </Tile>
        <Tile
          title="Transition Options"
          notes="animateEntrance() with cubic-bezier + delay"
        >
          <TransitionOptions />
        </Tile>
        <Tile title="3D Cube" notes="rAF loop with direct style manipulation">
          <Cube />
        </Tile>
        <Tile
          title="Shared Layout"
          notes="CSS Grid overlap for simultaneous enter/exit"
        >
          <SharedLayout />
        </Tile>
        <Tile title="Aspect Ratio">
          <AspectRatio />
        </Tile>
        <Tile
          title="Bouncy Switch"
          notes="Spring up, bounce down with CSS linear()"
        >
          <BouncySwitch />
        </Tile>
        <Tile
          title="FLIP Toggle"
          notes="animateLayout() with interruptible WAAPI"
        >
          <FlipToggle />
        </Tile>
        <Tile
          title="Reordering"
          notes="animateLayout() with auto-shuffling list"
        >
          <Reordering />
        </Tile>
        <Tile title="Color Interpolation" notes="sRGB vs OKLCH color space">
          <ColorInterpolation />
        </Tile>
        <Tile
          title="Multi-State Badge"
          notes="Animated icon/label swap with WAAPI shake"
        >
          <MultiStateBadge />
        </Tile>
        <Tile
          title="Hold to Confirm"
          notes="Custom interaction with progress tracking"
        >
          <HoldToConfirm />
        </Tile>
        <Tile
          title="Material Ripple"
          notes="Pointer-tracked ripples with enter/exit animations"
        >
          <MaterialRipple />
        </Tile>
      </div>
    </>
  )
}

```
