Back to design system

VUI LMS Documentation

MOTION SYSTEM

# VUI LMS Motion Design System

**Version:** 1.0.0
**Target Framework:** React + Framer Motion
**Design Philosophy:** Fluid, Intentional, Premium, and Unobtrusive.

This document outlines the animation standards for VUI LMS. Our motion language combines the physical, tactile feel of Material You with the sleek, high-end polish of modern glassmorphism. Animations must feel natural, guiding the user's eye without ever blocking their workflow.

---

## 1. Core Motion Principles
- **Spatial Awareness:** Elements move logically from their origin. Dropdowns expand from their trigger; modals scale up from the center or slide up from the bottom.
- **Physics over Time:** Default to spring physics rather than rigid duration-based easing. Elements should have mass and momentum but minimal bounce.
- **Hierarchy of Motion:** Fast micro-interactions (< 200ms) for UI feedback. Slower, staggered animations (300-500ms) for data loads and page transitions.
- **Premium Restraint:** Avoid oversized bounces, rubber-banding, or overly dramatic spins. Keep it academic and trustworthy.

## 2. Timing Rules
- **Micro-interactions (Hover, Tap, Toggles):** 100ms - 150ms
- **Small expands (Accordions, Dropdowns):** 200ms - 250ms
- **Medium transitions (Modals, Toast notifications):** 300ms - 400ms
- **Large entrances (Page load, Staggered widgets):** 500ms - 800ms
- **Continuous/Looping (Live indicators):** 1500ms - 2000ms cycles

## 3. Recommended Easing & Springs
In Framer Motion, prefer `spring` over `tween` for structural animations.

```typescript
// Smooth, no bounce (Page transitions, Modals)
export const springSmooth = { type: "spring", stiffness: 300, damping: 30 };

// Slightly playful (Cards hover, Toggles, Tags)
export const springSnappy = { type: "spring", stiffness: 400, damping: 25 };

// Gentle reveal (Fade ups, Image loads)
export const easeOutFade = { type: "tween", ease: "easeOut", duration: 0.4 };
```

---

## 4. Page & System Animations

### Page Transitions
- **Fade & Slide:** `initial={{ opacity: 0, y: 10 }}` to `animate={{ opacity: 1, y: 0 }}`.
- Exit animations are typically simple fades `exit={{ opacity: 0 }}`.

### Dashboard Entrance Animations
- **Staggered Orchestration:** Wrap dashboard columns in a parent with `staggerChildren: 0.1`.
- Widgets slide up 20px and fade in. This directs the user's attention from top-left to bottom-right smoothly.

### Glass Panel Animations
- **Blur adjustments:** Animate backdrop-blur filters or background opacity slightly on hover (e.g., `bg-white/5` to `bg-white/10`).
- **Inner highlights:** Subtle shine sweeping diagonally across the glass panel on mount or hover.

### Sidebar Expand/Collapse Animation
- Use `layout` prop in Framer Motion to animate the width. Text elements inside should fade out rapidly before the width shrinks to prevent text wrapping glitches.

### Tab Switching Animations
- **Active Indicator:** Use Framer Motion's `layoutId="activeTab"` for an underline or rounded background pill that smoothly glides between tab items.
- **Content Crossfade:** Wrap content in `<AnimatePresence mode="wait">` fading out old content and sliding in new content.

### Modals & Dropdowns
- **Modal Entrance:** `initial={{ opacity: 0, scale: 0.95 }}` to `animate={{ opacity: 1, scale: 1 }}` using `springSmooth`.
- **Dropdowns:** Anchor to the trigger point (e.g., `origin-top-right`) and animate scaleY/opacity.

### Component-Level Motion
- **Course Card Hover:** `whileHover={{ y: -5, scale: 1.01 }}`. 
- **Notification Animations:** Slide in from right `x: 50` to `x: 0`, and bounce down for unread badges.
- **Live Class Indicator:** Standard CSS `animate-ping` for the outer radius, combined with a steady solid inner circle.
- **Progress Bars:** `initial={{ width: 0 }}` animating to `width: "X%"` over 1 second, with `easeOut`.
- **Progress Rings:** Animate the SVG `strokeDashoffset`.
- **Skeleton Loading:** `animate-pulse` or a CSS linear-gradient shimmer moving from `-100%` to `200%`.
- **Empty States:** Gentle float animation `animate={{ y: [-5, 5, -5] }}` over 4 seconds, or a slow draw-in of an SVG illustration.

---

## 5. Feature-Specific Choreography

### Student Dashboard
- **Course cards:** Enter with a staggered fade-up.
- **Continue learning card:** Features a subtle, slow-moving glowing gradient on its border to draw attention.
- **Upcoming live class card:** A red recording dot pulses `animate-ping` infinitely.
- **Assignment cards:** Slide in sequentially based on urgency (red deadlines first).
- **Progress stats:** Use a `useMotionValue` and `animate()` utility to count numbers up from 0 to target on load.

### Course Page
- **Curriculum modules:** Expand with a smooth dynamic height animation (`height: "auto"`, `overflow: "hidden"`).
- **Completed lessons:** Once marked complete, a green checkmark "draws" itself in via `pathLength` animation.
- **Active lesson:** Uses an animated gradient background behind a `<div className="glass-panel">` to create a moving border effect.
- **Course progress bar:** Fills smoothly from left to right as the user navigates into the course view.

### Video Lesson Page
- **Player controls:** Bottom glass panel fades in on mouse move, fades out (`opacity: 0`) after 3s of inactivity.
- **Sidebar (Desktop):** Slides in from the right (`x: 100%` to `x: 0`).
- **Sidebar (Mobile):** Acts as a Bottom Sheet sliding up from the bottom (`y: 100%` to `y: 0`), using drag gestures (`drag="y"`).
- **Notes panel:** Slides out gracefully pushing video content to the side (using `layout` animations on the video wrapper).
- **Transcript:** Auto-scroll smoothly highlights the active timestamp with a soft blue text color transition.
- **Next lesson card:** Popping up softly in the corner during the last 10 seconds of the video (`y: 20`, `opacity: 0` -> `y: 0`, `opacity: 1`).

### Live Class
- **Waiting room:** A glass card that floats gently in the center of the screen `y: [0, -10, 0]`.
- **Join button:** Has a soft breathing glow box-shadow `animate={{ boxShadow: ["0px 0px 0px rgba(59,130,246,0)", "0px 0px 20px rgba(59,130,246,0.5)", "0px 0px 0px rgba(59,130,246,0)"] }}`.
- **Recording indicator:** Pulses red in the top left corner.
- **Speaking participant:** The glass card of the active speaker gets a 2px animated blue border.
- **Hand raise icon:** Bounces gently `y: [0, -5, 0]` loop when triggered.
- **Chat messages:** New messages slide up from the bottom `y: 10` and `opacity: 0`.

### Admin Dashboard
- **Analytics cards:** Count-up numbers on mount.
- **Charts:** Line charts path length draw in from left to right (`pathLength: 0` to `1`).
- **Tables:** Rows reveal with a very fast stagger (`staggerChildren: 0.05`).
- **Filters:** Expand as glass dropdown panels with `scaleY: 1` from `origin-top`.
- **Storage usage ring:** Circle stroke draws around smoothly, changing color from blue to orange depending on storage limits.

---

## 6. Implementation Guidance (Framer Motion)

### Recommended Base Variants
Use these for standard staggers (e.g., lists, dashboards).

```typescript
export const containerVariants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1,
      delayChildren: 0.2
    }
  }
};

export const itemVariants = {
  hidden: { opacity: 0, y: 20 },
  visible: { 
    opacity: 1, 
    y: 0,
    transition: { type: "spring", stiffness: 300, damping: 24 }
  }
};
```

### Hover & Tap States
Interactive buttons and cards should feel tactile.
```tsx
<motion.button
  whileHover={{ scale: 1.02, backgroundColor: "rgba(255,255,255,0.1)" }}
  whileTap={{ scale: 0.98 }}
>
  Join Class
</motion.button>
```

### Reduced Motion Accessibility
Respect user OS preferences to disable jarring animations. Fallback to basic opacity fades.

```tsx
import { motion, useReducedMotion } from 'motion/react';

export function AccessibleCard({ children }) {
  const shouldReduceMotion = useReducedMotion();

  return (
    <motion.div
      initial={{ opacity: 0, y: shouldReduceMotion ? 0 : 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.4 }}
    >
      {children}
    </motion.div>
  );
}
```

### Success States (Micro-interactions)
For task completions, grades uploaded, or module passed.
```tsx
<motion.path
  initial={{ pathLength: 0, opacity: 0 }}
  animate={{ pathLength: 1, opacity: 1 }}
  transition={{ duration: 0.5, ease: "easeOut" }}
  d="M5 13l4 4L19 7" // Checkmark SVG path
/>
```

---
*End of Motion Design System Document*