# Motion System Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Add fluid spring-based animations and micro-interactions throughout ZeroClock — page transitions, list animations, button feedback, loading states, modal/dropdown polish, and timer-specific effects. **Architecture:** Vue `` and `` for enter/leave orchestration with CSS transition classes. `@vueuse/motion` for spring-physics on interactive elements (nav rail indicator, timer pulse). CSS keyframes for ambient/looping animations (shimmer, float). All animations respect `prefers-reduced-motion`. **Tech Stack:** Vue 3, @vueuse/motion, Tailwind CSS v4, CSS transitions/keyframes --- ### Task 1: Install @vueuse/motion and create spring presets **Files:** - Modify: `package.json` - Modify: `src/main.ts` - Create: `src/utils/motion.ts` **Step 1: Install the dependency** Run: `npm install @vueuse/motion` **Step 2: Register MotionPlugin in main.ts** Add to `src/main.ts`: ```ts import { MotionPlugin } from '@vueuse/motion' // After app.use(router), before app.mount: app.use(MotionPlugin) ``` Final `src/main.ts`: ```ts import { createApp } from 'vue' import { createPinia } from 'pinia' import { MotionPlugin } from '@vueuse/motion' import router from './router' import App from './App.vue' import './styles/main.css' const app = createApp(App) const pinia = createPinia() app.use(pinia) app.use(router) app.use(MotionPlugin) app.mount('#app') ``` **Step 3: Create spring preset module** Create `src/utils/motion.ts`: ```ts // Spring presets for @vueuse/motion export const springPresets = { snappy: { damping: 20, stiffness: 300 }, smooth: { damping: 15, stiffness: 200 }, popIn: { damping: 12, stiffness: 400 }, } ``` **Step 4: Verify build** Run: `npm run build` Expected: Build passes with no errors. **Step 5: Commit** ```bash git add package.json package-lock.json src/main.ts src/utils/motion.ts git commit -m "feat: install @vueuse/motion and create spring presets" ``` --- ### Task 2: Add CSS animation classes and keyframes **Files:** - Modify: `src/styles/main.css` Add all the reusable transition classes and new keyframes needed by subsequent tasks. Place them after the existing keyframes block (after line 232 `.animate-pulse-colon`). **Step 1: Add page transition CSS classes** ```css /* Page transitions */ .page-enter-active { transition: opacity 250ms cubic-bezier(0.22, 1, 0.36, 1), transform 250ms cubic-bezier(0.22, 1, 0.36, 1); } .page-leave-active { transition: opacity 150ms ease-out, transform 150ms ease-out; } .page-enter-from { opacity: 0; transform: translateY(8px); } .page-leave-to { opacity: 0; transform: translateY(-8px); } ``` **Step 2: Add list transition CSS classes** ```css /* List item transitions */ .list-enter-active { transition: opacity 250ms cubic-bezier(0.22, 1, 0.36, 1), transform 250ms cubic-bezier(0.22, 1, 0.36, 1); } .list-leave-active { transition: opacity 150ms ease-in, transform 150ms ease-in; } .list-move { transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1); } .list-enter-from { opacity: 0; transform: translateY(12px); } .list-leave-to { opacity: 0; transform: translateX(-20px); } ``` **Step 3: Add tag chip transition classes** ```css /* Tag chip transitions */ .chip-enter-active { transition: opacity 200ms cubic-bezier(0.34, 1.56, 0.64, 1), transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1); } .chip-leave-active { transition: opacity 100ms ease-in, transform 100ms ease-in; } .chip-enter-from { opacity: 0; transform: scale(0.8); } .chip-leave-to { opacity: 0; transform: scale(0.8); } ``` **Step 4: Add modal transition classes** ```css /* Modal backdrop transitions */ .modal-enter-active { transition: opacity 200ms ease-out; } .modal-leave-active { transition: opacity 150ms ease-in; } .modal-enter-from, .modal-leave-to { opacity: 0; } /* Modal panel transitions */ .modal-panel-enter-active { transition: opacity 250ms cubic-bezier(0.22, 1, 0.36, 1), transform 250ms cubic-bezier(0.22, 1, 0.36, 1); } .modal-panel-leave-active { transition: opacity 150ms ease-in, transform 150ms ease-in; } .modal-panel-enter-from { opacity: 0; transform: scale(0.95); } .modal-panel-leave-to { opacity: 0; transform: scale(0.97); } ``` **Step 5: Add dropdown transition classes** ```css /* Dropdown transitions */ .dropdown-enter-active { transition: opacity 150ms cubic-bezier(0.22, 1, 0.36, 1), transform 150ms cubic-bezier(0.22, 1, 0.36, 1); } .dropdown-leave-active { transition: opacity 100ms ease-in, transform 100ms ease-in; } .dropdown-enter-from { opacity: 0; transform: translateY(-4px) scale(0.95); } .dropdown-leave-to { opacity: 0; transform: translateY(-4px); } ``` **Step 6: Add content fade-in transition** ```css /* Content fade-in */ .fade-enter-active { transition: opacity 250ms cubic-bezier(0.22, 1, 0.36, 1), transform 250ms cubic-bezier(0.22, 1, 0.36, 1); } .fade-leave-active { transition: opacity 150ms ease-out; } .fade-enter-from { opacity: 0; transform: translateY(4px); } .fade-leave-to { opacity: 0; } ``` **Step 7: Add shimmer and float keyframes** ```css /* Skeleton shimmer */ @keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } .skeleton { background: linear-gradient(90deg, var(--color-bg-elevated) 25%, var(--color-bg-surface) 50%, var(--color-bg-elevated) 75%); background-size: 200% 100%; animation: shimmer 1.5s ease-in-out infinite; } /* Empty state floating icon */ @keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } } .animate-float { animation: float 3s ease-in-out infinite; } ``` **Step 8: Add button feedback utilities** ```css /* Button interactive feedback */ .btn-primary { transition: transform 150ms cubic-bezier(0.22, 1, 0.36, 1), box-shadow 150ms ease; } .btn-primary:hover { transform: scale(1.02); } .btn-primary:active { transform: scale(0.97); transition-duration: 50ms; } .btn-icon:active { transform: scale(0.85); transition: transform 100ms cubic-bezier(0.22, 1, 0.36, 1); } .btn-icon-delete:active { transform: scale(0.85) rotate(-10deg); transition: transform 100ms cubic-bezier(0.22, 1, 0.36, 1); } /* Card hover lift */ .card-hover { transition: transform 200ms cubic-bezier(0.22, 1, 0.36, 1), box-shadow 200ms ease; } .card-hover:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } .card-hover:active { transform: translateY(0); } ``` **Step 9: Add toggle switch overshoot** ```css /* Toggle switch overshoot easing */ .toggle-thumb { transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1); } ``` **Step 10: Add progress bar animation** ```css /* Progress bar animate-in */ .progress-bar { transition: width 600ms cubic-bezier(0.22, 1, 0.36, 1); } ``` **Step 11: Update toast keyframes with horizontal slide** Replace the existing `@keyframes toast-enter` (around line 196-205) with: ```css @keyframes toast-enter { from { opacity: 0; transform: translateY(-20px) translateX(10px); } to { opacity: 1; transform: translateY(0) translateX(0); } } ``` Replace `@keyframes toast-exit` with: ```css @keyframes toast-exit { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-10px); } } ``` **Step 12: Add reduced motion override** At the very end of the file: ```css /* Respect reduced motion preference */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } } ``` **Step 13: Verify build** Run: `npm run build` Expected: Build passes. **Step 14: Commit** ```bash git add src/styles/main.css git commit -m "feat: add animation CSS classes, keyframes, and reduced-motion support" ``` --- ### Task 3: Page transitions on router-view **Files:** - Modify: `src/App.vue:76-87` (template section) **Step 1: Wrap router-view in Transition** Replace the current `` at line 82 with: ```html ``` The `:key="$route.path"` ensures the transition fires on every route change. The CSS classes `page-enter-active`, `page-leave-active`, `page-enter-from`, `page-leave-to` were added in Task 2. **Step 2: Verify build** Run: `npm run build` Expected: Build passes. **Step 3: Commit** ```bash git add src/App.vue git commit -m "feat: add page transitions on route changes" ``` --- ### Task 4: NavRail animated active indicator **Files:** - Modify: `src/components/NavRail.vue` Currently NavRail renders a `
` inside each button, creating/destroying the indicator. Change this to a single absolutely-positioned indicator div that slides to the active item using CSS transitions. **Step 1: Rewrite NavRail template** Replace the entire template in `src/components/NavRail.vue` with: ```html ``` **Step 2: Add activeIndex computed** In the `