Animation Gallery
A collection of motion and animation experiments adapted from the standalone demos.
Animations
Most animations are adapted from Motion. Thank you for your work Matt Perry!
Default Animate
animateEntrance + animateLayout defaults
Rolling Square
CSS transition with spring() timing function
Enter Animation
animateEntrance() with spring physics
Exit Animation
animateEntrance() + animateExit()
Press Interaction
CSS transition + pointer/keyboard events
HTML Content
0
rAF loop with spring iterator for text
Keyframes
CSS @keyframes with infinite loop
Interruptible Keyframes
Web Animations API with commitStyles()
Rotate
CSS @keyframes (one-shot)
Transition Options
animateEntrance() with cubic-bezier + delay
3D Cube
rAF loop with direct style manipulation
Shared Layout
CSS Grid overlap for simultaneous enter/exit
Aspect Ratio
Bouncy Switch
Spring up, bounce down with CSS linear()
FLIP Toggle
animateLayout() with interruptible WAAPI
Reordering
animateLayout() with auto-shuffling list
Color Interpolation
sRGB vs OKLCH color space
Multi-State Badge
Animated icon/label swap with WAAPI shake
Hold to Confirm
Custom interaction with progress tracking
Material Ripple
Pointer-tracked ripples with enter/exit animations
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>
</>
)
}