From 31b04e549c3f46adc83efa876dec9c22a7c38a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Orzech?= Date: Thu, 19 Mar 2026 10:28:17 +0100 Subject: [PATCH] docs: add micro-animations design spec Define 19 micro-animations + 5 navigation transitions across all screens with expressive/bouncy style and shared SwooshMotion specs. --- .../2026-03-19-micro-animations-design.md | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 docs/superpowers/specs/2026-03-19-micro-animations-design.md diff --git a/docs/superpowers/specs/2026-03-19-micro-animations-design.md b/docs/superpowers/specs/2026-03-19-micro-animations-design.md new file mode 100644 index 0000000..55512c3 --- /dev/null +++ b/docs/superpowers/specs/2026-03-19-micro-animations-design.md @@ -0,0 +1,178 @@ +# Micro-Animations Design — Swoosh + +**Date:** 2026-03-19 +**Status:** Approved +**Style:** Expressive & playful — bouncy springs, overshoot, lively feedback + +## Overview + +Add 19 micro-animations + 5 navigation transitions across all screens to make the app feel alive. Create a shared `SwooshMotion` object with predefined animation specs ensuring consistent character throughout the app. + +## Shared Animation Specs — SwooshMotion + +Central object in a new file `ui/animation/SwooshMotion.kt` providing reusable animation specifications: + +| Name | Type | Parameters | Use case | +|------|------|-----------|----------| +| `Bouncy` | Spring | dampingRatio=0.55, stiffness=400 | FAB, buttons, chips | +| `Snappy` | Spring | dampingRatio=0.7, stiffness=800 | Expand/collapse, dialogs | +| `Gentle` | Spring | dampingRatio=0.8, stiffness=300 | Cards, content reveal | +| `Quick` | Tween | 200ms, FastOutSlowInEasing | Fade, color transitions | +| `StaggerDelay` | Offset | 50ms per item | List item entrances | + +## Feed Screen — 7 animations + +### 1. FAB entrance (SPRING/Bouncy) +When the feed screen opens, the FAB scales from 0 to 1 with `Bouncy` spring. Visible overshoot — the FAB "pops" onto screen. +- Compose API: `animateFloatAsState` with `Bouncy` spring on scale modifier +- Trigger: `LaunchedEffect(Unit)` sets target to 1f + +### 2. FAB press (SPRING/Bouncy) +On tap, FAB shrinks to 85% and springs back to 100%. +- Compose API: `Modifier.pointerInput` detecting press → `animateFloatAsState` scale 0.85f → 1f +- Spring spec: `Bouncy` + +### 3. Post cards staggered entrance (SLIDE/Gentle) +Cards slide in from bottom with cascading delay — 50ms per item. "Waterfall" effect. +- Compose API: `AnimatedVisibility` per item with `slideInVertically` + `fadeIn` +- Delay: `StaggerDelay * index` (capped at visible items, ~8 max) +- Only on initial load, not on scroll-append + +### 4. "Show more" expand (SPRING/Snappy) +Card height animates with spring when text expands/collapses. +- Compose API: `Modifier.animateContentSize(animationSpec = Snappy)` +- Text content enters with `fadeIn` + +### 5. Empty state (FADE/Quick + scale) +"No posts yet" icon and text fade in with subtle scale from 0.9 to 1.0. +- Compose API: `AnimatedVisibility` with `fadeIn + scaleIn(initialScale = 0.9f)` + +### 6. Queue status chip (BOUNCE/Bouncy) +During upload: chip pulses (infinite alpha animation 0.6→1.0). On status change (success/fail): bounce scale + color crossfade. +- Compose API: `rememberInfiniteTransition` for pulse; `animateColorAsState` + scale bounce on status change +- Color transition: `Quick` tween + +### 7. Snackbar error (SLIDE/Snappy) +Slides in from bottom with slight overshoot. Fades out on timeout. +- Compose API: `AnimatedVisibility` with `slideInVertically(initialOffsetY = { it })` + `Snappy` spring +- Exit: `fadeOut` with `Quick` tween + +## Composer Screen — 6 animations + +### 8. Image preview (SCALE/Bouncy) +After picking an image, the preview scales from 0 with bouncy spring. Close button ("X") rotates in (0°→360° with fade). +- Compose API: `AnimatedVisibility` with `scaleIn` using `Bouncy` spring +- Close button: `animateFloatAsState` on rotation + `fadeIn` + +### 9. Link preview card (SLIDE/Gentle) +After link loads, card slides up from below + fades in. While loading: shimmer placeholder. +- Compose API: `AnimatedVisibility` with `slideInVertically + fadeIn` using `Gentle` spring +- Shimmer: infinite `rememberInfiniteTransition` on a translucent gradient offset + +### 10. Schedule chip (SPRING/Bouncy) +After picking date, chip pops in with bouncy spring. Clock icon subtly rotates. +- Compose API: `AnimatedVisibility` with `scaleIn` using `Bouncy` +- Icon: `animateFloatAsState` rotation 0→360 + +### 11. Publish button (BOUNCE/Bouncy) +Subtle bounce on activation. During publishing: loading pulse (alpha animation). On success: checkmark icon scales in. +- Compose API: Scale bounce on click via `animateFloatAsState`; `rememberInfiniteTransition` for pulse; `AnimatedContent` for icon swap with `scaleIn` + +### 12. Character counter color (FADE/Quick) +Smooth color crossfade when exceeding 280 characters — neutral → red. +- Compose API: `animateColorAsState` with `Quick` tween +- Trigger: `text.length > 280` + +### 13. Action buttons staggered entrance (SCALE/Gentle) +Draft, Schedule, Publish buttons — cascading scale-in with 50ms delay each. +- Compose API: `AnimatedVisibility` per button with `scaleIn` + `StaggerDelay * index` + +## Detail Screen — 4 animations + +### 14. Content reveal (FADE/Gentle) +Elements appear sequentially: status badge → text → image → metadata. 80ms delay each. +- Compose API: `AnimatedVisibility` per section with `fadeIn + slideInVertically(initialOffsetY = { 20 })` +- Delay: `LaunchedEffect` with `delay(80 * index)` + +### 15. Status badge entrance (SPRING/Bouncy) +Badge scales from 0 with bounce — first visible element, draws attention. +- Compose API: `animateFloatAsState` scale with `Bouncy` spring + +### 16. Delete confirmation dialog (SCALE/Snappy) +Dialog scales from center (0.8→1.0) with spring + backdrop fades in. +- Compose API: Custom dialog wrapper with `scaleIn(initialScale = 0.8f)` + `fadeIn` backdrop +- Spring spec: `Snappy` + +### 17. Metadata section (SLIDE/Gentle) +Bottom metadata slides up — last in the reveal sequence. +- Compose API: `AnimatedVisibility` with `slideInVertically` + `Gentle` spring + +## Settings Screen — 2 animations + +### 18. "Saved!" feedback (SPRING/Bouncy) +Green "Saved!" text pops in with bounce (scale 0→1). After 2 seconds, fades out. +- Compose API: `AnimatedVisibility` with `scaleIn` using `Bouncy` spring; `LaunchedEffect` → `delay(2000)` → hide +- Exit: `fadeOut` with `Quick` tween + +### 19. Disconnect dialog (FADE+SCALE/Snappy) +Same pattern as delete dialog — scale from center + backdrop fade. "Disconnect" button has subtle red pulse. +- Compose API: Same custom dialog wrapper as #16 +- Red pulse: `rememberInfiniteTransition` on alpha of error color + +## Navigation Transitions — 5 + +### Feed → Composer +Slide up from bottom + fade. Conceptually: FAB transforms into full screen. +- `enterTransition = slideInVertically(initialOffsetY = { it }) + fadeIn()` +- `exitTransition = slideOutVertically(targetOffsetY = { it }) + fadeOut()` + +### Feed → Detail +Slide in from right + fade. Post card expands into full view. +- `enterTransition = slideInHorizontally(initialOffsetX = { it }) + fadeIn()` +- `exitTransition = slideOutHorizontally(targetOffsetX = { it }) + fadeOut()` + +### Feed → Settings +Standard slide from right. +- `enterTransition = slideInHorizontally(initialOffsetX = { it })` +- `exitTransition = slideOutHorizontally(targetOffsetX = { it })` + +### Back (all screens) +Reverse of the entry animation for each screen. + +### Setup → Feed +Crossfade — smooth transition from animated setup background to feed. +- `enterTransition = fadeIn(tween(500))` +- `exitTransition = fadeOut(tween(500))` + +## File Structure + +``` +ui/ +├── animation/ +│ └── SwooshMotion.kt # Shared animation specs object +├── components/ +│ └── AnimatedDialog.kt # Reusable animated dialog wrapper (used by #16, #19) +├── feed/ +│ └── FeedScreen.kt # Modified: #1-#7 +├── composer/ +│ └── ComposerScreen.kt # Modified: #8-#13 +├── detail/ +│ └── DetailScreen.kt # Modified: #14-#17 +├── settings/ +│ └── SettingsScreen.kt # Modified: #18-#19 +└── navigation/ + └── NavGraph.kt # Modified: navigation transitions +``` + +## New files: 2 +- `ui/animation/SwooshMotion.kt` +- `ui/components/AnimatedDialog.kt` + +## Modified files: 5 +- `FeedScreen.kt`, `ComposerScreen.kt`, `DetailScreen.kt`, `SettingsScreen.kt`, `NavGraph.kt` + +## Testing Strategy + +- Existing unit tests should pass unchanged (animations don't affect business logic) +- Manual verification: each animation visually correct on emulator +- Compose Preview for individual animated components where feasible