Diwa Design System

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
0.15s
--diwa-motion-duration-moderate
0.25s
--diwa-motion-duration-long
0.4s
--diwa-motion-duration-very-long
0.7s
TokenValueUse
--diwa-motion-duration-short0.15sMicro: icon swap, focus ring appearance
--diwa-motion-duration-moderate0.25sStandard: fade, slide, scale
--diwa-motion-duration-long0.4sComplex: accordion expand, drawer open
--diwa-motion-duration-very-long0.7sPage-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.

TokenCurveUse
--diwa-motion-easing-basecubic-bezier(0.4, 0, 0.2, 1)Standard enter/exit — ease-in-out
--diwa-motion-easing-incubic-bezier(0.4, 0, 1, 1)Element leaves screen — accelerate
--diwa-motion-easing-outcubic-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

Moving

translateX · duration-short · easing-base

Enter / Exit

Enter / Exit

translateY + opacity · duration-moderate · easing-in / easing-out

Show / Hide

Show / Hide

opacity · duration-long · easing-base

Expand

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;
  }
}