Motion
Diwa motion tokens encode duration and easing so animations feel consistent and purposeful across the system. When objects move within a limited area, shorter durations are used; larger distances require more time. A global reduced-motion reset ensures animations are disabled for users who prefer it.
Duration
The duration of an animation directly affects perceived lag. Motion should be swift, subtle and purposeful — choose the shortest duration that still feels natural.
--diwa-motion-duration-short--diwa-motion-duration-moderate--diwa-motion-duration-long--diwa-motion-duration-very-long| Token | Value | Use |
|---|---|---|
| --diwa-motion-duration-short | 0.15s | Micro: icon swap, focus ring appearance |
| --diwa-motion-duration-moderate | 0.25s | Standard: fade, slide, scale |
| --diwa-motion-duration-long | 0.4s | Complex: accordion expand, drawer open |
| --diwa-motion-duration-very-long | 0.7s | Page-level: route transition, wizard |
Easing
Easing curves are the key for turning components from static to interactive. Use easing-out when an element enters the screen (decelerates into position) and easing-in when it exits (accelerates away). Use easing-base for anything that stays on screen.
| Token | Curve | Use |
|---|---|---|
| --diwa-motion-easing-base | cubic-bezier(0.4, 0, 0.2, 1) | Standard enter/exit — ease-in-out |
| --diwa-motion-easing-in | cubic-bezier(0.4, 0, 1, 1) | Element leaves screen — accelerate |
| --diwa-motion-easing-out | cubic-bezier(0, 0, 0.2, 1) | Element enters screen — decelerate |
Examples
Live demonstrations of each motion pattern using the Diwa tokens. They are suppressed when prefers-reduced-motion: reduce is active.
Moving
translateX · duration-short · easing-base
Enter / Exit
translateY + opacity · duration-moderate · easing-in / easing-out
Show / Hide
opacity · duration-long · easing-base
Expand
height · duration-short → duration-moderate · easing-in → easing-base
Styles
Import JS tokens for use in CSS-in-JS or Framer Motion. Use CSS custom properties directly in stylesheets. Include the reduced-motion reset in both global CSS and inside each Shadow DOM component.
// JS — import motion tokens
import {
motionDurationShort,
motionDurationModerate,
motionDurationLong,
motionDurationVeryLong,
motionEasingBase,
motionEasingIn,
motionEasingOut,
} from '@diwacopilot/components/styles';
// Framer Motion
<motion.div
animate={{ opacity: 1, y: 0 }}
initial={{ opacity: 0, y: 40 }}
transition={{
duration: parseFloat(motionDurationModerate),
ease: motionEasingOut,
}}
/>
/* ─── CSS ─────────────────────────────────────────────────────────────── */
/* Moving — element stays on screen */
.chip {
transition: transform var(--diwa-motion-duration-short)
var(--diwa-motion-easing-base);
}
/* Enter — element arrives on screen (decelerates) */
.drawer {
transition: transform var(--diwa-motion-duration-long)
var(--diwa-motion-easing-out);
}
/* Exit — element leaves screen (accelerates) */
.toast[aria-hidden='true'] {
transition: opacity var(--diwa-motion-duration-moderate)
var(--diwa-motion-easing-in);
}
/* Reduced motion — global reset */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
/* Reduced motion — Shadow DOM components (boundary breaks global reset) */
@media (prefers-reduced-motion: reduce) {
:host {
transition: none !important;
animation: none !important;
}
}@use '@diwacopilot/components/styles' as *;
.chip {
transition: transform $diwa-motion-duration-short $diwa-motion-easing-base;
}
.drawer {
transition: transform $diwa-motion-duration-long $diwa-motion-easing-out;
}
.toast[aria-hidden='true'] {
transition: opacity $diwa-motion-duration-moderate $diwa-motion-easing-in;
}
@media (prefers-reduced-motion: reduce) {
:host {
transition: none !important;
animation: none !important;
}
}