Skip to content

Motion

Bumi's motion language is purposeful and restrained. Animations serve function --- they communicate state changes, guide attention, and provide feedback. They never exist for decoration.

Live Animation Demos (hover & watch)
Hover me
Card Lift
200ms ease-out
Skeleton Shimmer
Online
Pulse Dot (2s)
Button Hover

Duration Scale

TokenDurationUsage
duration-micro100msHover color changes, opacity shifts
duration-fast150msButton press feedback, icon transitions
duration-normal200msPanel expand/collapse, fade transitions
duration-slow300msModal enter/exit, sheet slide, dropdown open
duration-enter500msPage transitions, staggered content load

Easing Curves

ContextEasingCSS Value
Enter / appearease-outcubic-bezier(0, 0, 0.2, 1)
Exit / dismissease-incubic-bezier(0.4, 0, 1, 1)
State changeease-in-outcubic-bezier(0.4, 0, 0.2, 1)
Spring (modal/sheet)customcubic-bezier(0.16, 1, 0.3, 1)

Common Animations

Hover: Card Lift

A subtle upward shift on card hover. Paired with shadow transition from Level 1 to Level 2.

css
.card {
  transition: transform 100ms ease-out, box-shadow 100ms ease-out;
}
.card:hover {
  transform: translateY(-1px);
  box-shadow: var(--shadow-2);
}

Hover: Button

Background color change only. No transform.

css
.btn-primary {
  transition: background-color 150ms ease-in-out;
}
.btn-primary:hover {
  background-color: var(--color-brand-hover);
}

Overlay fades in while content slides up from below.

css
/* Overlay */
.modal-overlay {
  animation: fade-in 200ms ease-out;
}
@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Content */
.modal-content {
  animation: slide-up 300ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes slide-up {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

Sheet (Slide from Right)

css
.sheet {
  animation: slide-right 300ms cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes slide-right {
  from { transform: translateX(100%); }
  to { transform: translateX(0); }
}

Page Load: Staggered Fade-Up

Each section on the page fades in with a staggered delay.

css
.page-section {
  animation: fade-up 500ms ease-out both;
}
.page-section:nth-child(1) { animation-delay: 0ms; }
.page-section:nth-child(2) { animation-delay: 50ms; }
.page-section:nth-child(3) { animation-delay: 100ms; }
.page-section:nth-child(4) { animation-delay: 150ms; }

@keyframes fade-up {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

Tab Switch

No animation. Content swaps instantly.

css
/* Tab content changes are immediate --- no transition */
.tab-content {
  /* No animation property */
}

Skeleton Shimmer

css
.skeleton {
  background: linear-gradient(
    90deg,
    var(--bg-inset) 0%,
    var(--bg-hover) 50%,
    var(--bg-inset) 100%
  );
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite linear;
}
@keyframes shimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

Live Indicator Pulse

css
.live-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-success);
  animation: pulse 2s infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

React Native (Mobile)

Use react-native-reanimated for mobile animations. Spring configs for common transitions:

tsx
import { withSpring, withTiming } from 'react-native-reanimated';

// Modal slide-up
const modalSpring = {
  damping: 20,
  stiffness: 300,
  mass: 0.8,
};

// Sheet slide
const sheetSpring = {
  damping: 25,
  stiffness: 250,
  mass: 1,
};

// Button press
const pressConfig = {
  duration: 150,
};

CSS Custom Properties

css
:root {
  --duration-micro: 100ms;
  --duration-fast: 150ms;
  --duration-normal: 200ms;
  --duration-slow: 300ms;
  --duration-enter: 500ms;

  --ease-out: cubic-bezier(0, 0, 0.2, 1);
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-spring: cubic-bezier(0.16, 1, 0.3, 1);
}

DO and DON'T

DO

  • Use ease-out for elements entering the screen
  • Use ease-in for elements leaving the screen
  • Keep hover animations at 100ms --- they should feel instant
  • Use staggered delays for lists of content loading in

DON'T

  • Never use bounce or elastic easing --- this is a business application, not a game
  • Never animate layout properties (width, height, top, left) --- use transform and opacity only
  • Don't add transition to tab content changes --- instant swap feels more responsive
  • Don't exceed 500ms for any single animation --- users will perceive it as sluggish

RetailOS - Sistem ERP Retail Modern untuk Indonesia