Define 19 micro-animations + 5 navigation transitions across all screens with expressive/bouncy style and shared SwooshMotion specs.
7.8 KiB
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:
animateFloatAsStatewithBouncyspring 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.pointerInputdetecting press →animateFloatAsStatescale 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:
AnimatedVisibilityper item withslideInVertically+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:
AnimatedVisibilitywithfadeIn + 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:
rememberInfiniteTransitionfor pulse;animateColorAsState+ scale bounce on status change - Color transition:
Quicktween
7. Snackbar error (SLIDE/Snappy)
Slides in from bottom with slight overshoot. Fades out on timeout.
- Compose API:
AnimatedVisibilitywithslideInVertically(initialOffsetY = { it })+Snappyspring - Exit:
fadeOutwithQuicktween
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:
AnimatedVisibilitywithscaleInusingBouncyspring - Close button:
animateFloatAsStateon rotation +fadeIn
9. Link preview card (SLIDE/Gentle)
After link loads, card slides up from below + fades in. While loading: shimmer placeholder.
- Compose API:
AnimatedVisibilitywithslideInVertically + fadeInusingGentlespring - Shimmer: infinite
rememberInfiniteTransitionon a translucent gradient offset
10. Schedule chip (SPRING/Bouncy)
After picking date, chip pops in with bouncy spring. Clock icon subtly rotates.
- Compose API:
AnimatedVisibilitywithscaleInusingBouncy - Icon:
animateFloatAsStaterotation 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;rememberInfiniteTransitionfor pulse;AnimatedContentfor icon swap withscaleIn
12. Character counter color (FADE/Quick)
Smooth color crossfade when exceeding 280 characters — neutral → red.
- Compose API:
animateColorAsStatewithQuicktween - Trigger:
text.length > 280
13. Action buttons staggered entrance (SCALE/Gentle)
Draft, Schedule, Publish buttons — cascading scale-in with 50ms delay each.
- Compose API:
AnimatedVisibilityper button withscaleIn+StaggerDelay * index
Detail Screen — 4 animations
14. Content reveal (FADE/Gentle)
Elements appear sequentially: status badge → text → image → metadata. 80ms delay each.
- Compose API:
AnimatedVisibilityper section withfadeIn + slideInVertically(initialOffsetY = { 20 }) - Delay:
LaunchedEffectwithdelay(80 * index)
15. Status badge entrance (SPRING/Bouncy)
Badge scales from 0 with bounce — first visible element, draws attention.
- Compose API:
animateFloatAsStatescale withBouncyspring
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)+fadeInbackdrop - Spring spec:
Snappy
17. Metadata section (SLIDE/Gentle)
Bottom metadata slides up — last in the reveal sequence.
- Compose API:
AnimatedVisibilitywithslideInVertically+Gentlespring
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:
AnimatedVisibilitywithscaleInusingBouncyspring;LaunchedEffect→delay(2000)→ hide - Exit:
fadeOutwithQuicktween
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:
rememberInfiniteTransitionon 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.ktui/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