Motion System
Motion brings interfaces to life. Our motion system provides a carefully crafted set of durations and easing curves that create smooth, purposeful animations. Use motion to guide attention, provide feedback, and create delightful experiences.
When to Use Motion
- • State changes (hover, active, disabled)
- • Page transitions and navigation
- • Drawing attention to important elements
- • Providing feedback for user actions
- • Revealing and hiding content
Motion Best Practices
- • Keep animations fast (150-500ms)
- • Use consistent easing curves
- • Respect prefers-reduced-motion
- • Animate transform and opacity (performant)
- • Avoid animating layout properties
Variable Weight Motion
For variable fonts (like Clash Display), use the VariableWeightText component to create a silky-smooth "breathing" effect that animates font weight. This animation uses font-variation-settings for true variable font interpolation.
Performance Optimizations:
- ✓ Uses true variable font (continuous weight axis from 200-700)
- ✓ GPU-accelerated with
will-change - ✓ Custom easing curve
[0.45, 0, 0.55, 1]for ultra-smooth motion - ✓ Font smoothing (antialiased) for crisp rendering
- ✓ Centered to prevent layout shifts during weight changes
import { VariableWeightText } from '@opencosmos/ui';
<VariableWeightText minWeight={200} maxWeight={700}>
Clash Display
</VariableWeightText>Note: The VariableWeightText component defaults to using Clash Display, but you can override with the fontFamily prop to use any variable font.
Duration Scale
Consistent timing creates rhythm. Choose durations based on the size and complexity of the animation.
instant0msfast150msnormal300msslow500msslower800msEasing Curves
Easing curves define the acceleration of animations. Different curves create different feelings of motion.
defaultcubic-bezier(0, 0, 0.2, 1)springcubic-bezier(0.16, 1, 0.3, 1)linearlinearInteractive Playground
Experiment with different combinations of duration and easing to find the perfect motion.
transition: transform 300ms cubic-bezier(0, 0, 0.2, 1)
Implementation Guide
CSS / Tailwind
For simple transitions and states, use CSS transitions with our motion tokens.
/* Using CSS custom properties */
.button {
transition: all var(--duration-normal) var(--ease-default);
}
/* Using Tailwind */
<button className="transition-all duration-300 ease-out hover:scale-105">
Hover me
</button>Framer Motion
motion.dev →Framer Motion is our recommended animation library for React. It provides declarative animations, gesture support, and excellent TypeScript integration.
When to use Framer Motion:
- • Component animations and transitions
- • Page transitions in React apps
- • Gesture-based interactions (drag, swipe)
- • Layout animations and shared element transitions
- • When you need React-aware animations
import { motion } from 'framer-motion';
// Basic animation
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.3, // 300ms = normal
ease: [0, 0, 0.2, 1] // ease-out
}}
>
Content
</motion.div>
// Using design system tokens
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
transition={{
duration: 0.15, // fast
ease: [0.16, 1, 0.3, 1] // spring
}}
>
Click me
</motion.button>GSAP (GreenSock Animation Platform)
gsap.com →GSAP is the industry-standard animation library for complex, timeline-based animations. Use it when you need precise control or advanced animation sequences.
When to use GSAP:
- • Complex animation timelines and sequences
- • SVG animations and morphing
- • Scroll-triggered animations (ScrollTrigger)
- • When you need maximum performance
- • Framework-agnostic animations
import { gsap } from 'gsap';
// Basic animation
gsap.to('.element', {
opacity: 1,
y: 0,
duration: 0.3, // 300ms = normal
ease: 'power2.out' // Similar to ease-out
});
// Timeline sequence
const tl = gsap.timeline();
tl.to('.hero', { opacity: 1, duration: 0.5 })
.to('.cta', { scale: 1, duration: 0.15 }, '-=0.2')
.to('.features', {
y: 0,
stagger: 0.1, // Stagger items
duration: 0.3
});Accessibility: Respecting User Preferences
Always respect the prefers-reduced-motion media query. Some users experience motion sickness or find animations distracting.
/* CSS approach */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
// React Hook approach (recommended)
import { useMotionPreference } from '@opencosmos/ui';
function MyComponent() {
const { shouldAnimate, scale } = useMotionPreference();
// Scale 5 (default) = 1x duration
// Duration: 0.3 is base duration for this animation
const duration = shouldAnimate && scale > 0
? 0.3 * (5 / scale)
: 0;
return (
<motion.div
initial={shouldAnimate ? { opacity: 0, y: 20 } : false}
animate={{ opacity: 1, y: 0 }}
transition={{ duration }}
>
Content
</motion.div>
);
}Framer Motion Integration
Pre-built animation variants and presets for Framer Motion. Import from @opencosmos/ui/utils
Fade Variants
Simple opacity transitions
import { fadeVariants } from '@opencosmos/ui/utils';
<motion.div
initial="hidden"
animate="visible"
variants={fadeVariantsSlide Variants
Slide in from: Left, Right, Top, Bottom
import { slideVariants } from '@opencosmos/ui/utils';
<motion.div
initial="hidden"
animate="visible"
variants={slideVariantsScale Variants
Default, Grow, Pop effects
import { scaleVariants } from '@opencosmos/ui/utils';
<motion.div
initial="hidden"
animate="visible"
variants={scaleVariantsModal Variants
Overlay + Content animations
import { modalVariants } from '@opencosmos/ui/utils';
<motion.div variants={modalVariants.overlay}>
<motion.div variantsRotate Variants
Rotation animations with fade
import { rotateVariants } from '@opencosmos/ui/utils';
<motion.div
initial="hidden"
animate="visible"
variants={rotateVariantsDrawer Variants
Slide-out panels from: Left, Right, Top, Bottom
import { drawerVariants } from '@opencosmos/ui/utils';
<motion.div
initial="hidden"
animate="visible"
variants={drawerVariantsCollapse Variants
Height-based expand/collapse for accordions
import { collapseVariants } from '@opencosmos/ui/utils';
<motion.div
initial="collapsed"
animate="expanded"
variantsComplete Presets
Ready-to-use animation configurations with variants + transitions included:
Available Presets
presets.fadepresets.slideUppresets.scalepresets.modalpresets.listimport { presets } from '@opencosmos/ui/utils';
// Simple fade animation
<motion.div {...presets.fade}>
Fades in
</motionCreate Custom Animations
Use the createAnimation helper to build custom variants:
import { createAnimation, transitions, easings } from '@opencosmos/ui/utils';
const customAnimation = createAnimation(