Repository navigation
feat: motion polish across ui, playground, code - #3243
Conversation
Implements the 10 gated opportunities from the animation audit: press feedback on all shared buttons, popover transform-origin fix, symmetric toast exits, DevPass activation + Reset Pass purchase visa stamps, Lounge points odometer roll, copy-button crossfades, animated Chats collapse, CREDITS ADDED stamp-in, and census page stagger. All movement carries prefers-reduced-motion fallbacks. Claude-Session: https://claude.ai/code/session_01DobvVEwfVg89UUMzUBsxRF
WalkthroughThis PR adds reduced-motion-aware animations and state transitions across dashboard, playground, census, and shared UI components. It updates activation and pass-success feedback, copy-button icons, chat-history collapse behavior, entrance animations, button press styling, popover origins, and toast dismissal transitions. ChangesUI motion and feedback
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/code/src/app/dashboard/components/ResetPassCard.tsx (1)
208-251: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate "stamp overlay" motion pattern across two files. Both blocks build the same double-border, uppercase stamp card with an identical
reduceMotion-branchedinitial/animateshape and{ type: "spring", duration: 0.4 }transition — differing only in message content, colors, and whetherAnimatePresence/exitis used. This duplication means fixes (e.g. adding exit animation, or accessibility tweaks) have to be applied twice and can drift, as already evident inDashboardShell.tsx's block lacking the exit handling thatResetPassCard.tsxhas.
apps/code/src/app/dashboard/components/ResetPassCard.tsx#L208-L251: extract this block's card/motion logic into a sharedStampOverlaycomponent (props:kind/color, title, subtitle,reduceMotion, optionalexit).apps/code/src/app/dashboard/DashboardShell.tsx#L519-L553: replace the inline motion.div stamp with the same sharedStampOverlaycomponent, passing the "DevPass activated" copy.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/code/src/app/dashboard/components/ResetPassCard.tsx` around lines 208 - 251, Extract the duplicated stamp-card motion and styling from ResetPassCard.tsx lines 208-251 into a shared StampOverlay component accepting kind/color, title, subtitle, reduceMotion, and optional exit props; preserve the existing redeemed/purchase appearance and animation behavior. Replace the inline stamp motion in DashboardShell.tsx lines 519-553 with StampOverlay, passing the “DevPass activated” copy; both sites require changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/code/src/app/dashboard/components/ResetPassCard.tsx`:
- Around line 164-165: Replace the direct setStampOverlay/setTimeout pairs in
both redeem and purchase success handlers with a shared showStampOverlay helper
that clears the existing pending timeout before scheduling a new 2200ms
dismissal; update purchaseMutation.onSuccess to call showStampOverlay({ kind:
"purchased", amount: data.amount }) and preserve the corresponding redeemed
overlay payload.
---
Nitpick comments:
In `@apps/code/src/app/dashboard/components/ResetPassCard.tsx`:
- Around line 208-251: Extract the duplicated stamp-card motion and styling from
ResetPassCard.tsx lines 208-251 into a shared StampOverlay component accepting
kind/color, title, subtitle, reduceMotion, and optional exit props; preserve the
existing redeemed/purchase appearance and animation behavior. Replace the inline
stamp motion in DashboardShell.tsx lines 519-553 with StampOverlay, passing the
“DevPass activated” copy; both sites require changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 7336fa40-7775-4859-b863-f55cd64f5a10
📒 Files selected for processing (16)
apps/code/src/app/dashboard/DashboardShell.tsxapps/code/src/app/dashboard/components/QuickStart.tsxapps/code/src/app/dashboard/components/ResetPassCard.tsxapps/code/src/app/data/[year]/page.tsxapps/code/src/components/CodingModelsShowcase.tsxapps/code/src/components/ui/button.tsxapps/playground/src/components/ai-elements/code-block.tsxapps/playground/src/components/credits/top-up-credits-dialog.tsxapps/playground/src/components/lounge/sidebar-points.tsxapps/playground/src/components/playground/chat-sidebar.tsxapps/playground/src/components/ui/button.tsxapps/playground/src/components/ui/popover.tsxapps/ui/src/lib/components/button.tsxapps/ui/src/lib/components/toast.tsxapps/ui/src/lib/utils/markdown-code-block.tsxpackages/shared/src/components/ui/button.tsx
| setStampOverlay({ kind: "redeemed" }); | ||
| setTimeout(() => setStampOverlay(null), 2200); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Overlapping redeem/purchase actions can clear the overlay early.
Each onSuccess schedules an independent, untracked setTimeout(..., 2200). If a user redeems and then purchases (or vice versa) within 2200ms, the second overlay ("purchased") is shown, but the first timer still fires at its original 2200ms mark and clears stampOverlay prematurely — before the second overlay's own 2200ms has elapsed. A later, now-stale second timeout then fires as a no-op.
🐛 Proposed fix: track and clear the pending timeout
+ const stampTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
+
+ const showStampOverlay = (
+ overlay: NonNullable<typeof stampOverlay>,
+ ) => {
+ if (stampTimeoutRef.current) {
+ clearTimeout(stampTimeoutRef.current);
+ }
+ setStampOverlay(overlay);
+ stampTimeoutRef.current = setTimeout(() => setStampOverlay(null), 2200);
+ };
+
+ useEffect(() => {
+ return () => {
+ if (stampTimeoutRef.current) {
+ clearTimeout(stampTimeoutRef.current);
+ }
+ };
+ }, []);
+
const redeemMutation = api.useMutation(
"post",
"/dev-plans/reset-pass/redeem",
{
onSuccess: async () => {
- setStampOverlay({ kind: "redeemed" });
- setTimeout(() => setStampOverlay(null), 2200);
+ showStampOverlay({ kind: "redeemed" });
await invalidateStatus();
},Apply the analogous change to purchaseMutation.onSuccess, replacing its setStampOverlay/setTimeout pair with showStampOverlay({ kind: "purchased", amount: data.amount }).
Also applies to: 179-180, 208-233
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/code/src/app/dashboard/components/ResetPassCard.tsx` around lines 164 -
165, Replace the direct setStampOverlay/setTimeout pairs in both redeem and
purchase success handlers with a shared showStampOverlay helper that clears the
existing pending timeout before scheduling a new 2200ms dismissal; update
purchaseMutation.onSuccess to call showStampOverlay({ kind: "purchased", amount:
data.amount }) and preserve the corresponding redeemed overlay payload.
Summary
Implements the 10 highest-leverage opportunities from a full animation audit of
apps/ui,apps/playground, andapps/code, gated on Emil Kowalski's frequency/purpose/speed/function framework. Every candidate that failed the gate (keyboard-driven surfaces, data being read, nav pills, modal origins) was deliberately left alone.What changed
Buttons (ui, playground, code, shared)active:scale-[0.98]press feedback — nothing in any app responded to press beforePopoverContentorigin-(--radix-popover-content-transform-origin)— the 720px model picker now grows from its trigger like every sibling primitivenot-data-[swipe=end]variantDashboardShell){ type: "spring", duration: 0.4 }), success screen held 1.6s (abort-safe) before the dashboard swapResetPassCard)[0.16,1,0.3,1]), streak flame crossfade — the core gamification loop is finally visible/data/[year]motion-safe:staggered entrance (70ms steps) for hero, stats, and the first 10 registry rows — deep rows render instantlyAll new movement has
prefers-reduced-motionfallbacks (fade-only, never zero feedback). Onlytransform/opacityare animated except the Chats collapse, which is an interruptible accordion-style transition confined to the sidebar subtree.Review
Ran an adversarial review against the repo's review-animations bar; its findings (diagonal toast swipe exit, untransitioned press scales on raw buttons, reduced-motion gaps, split stamp physics, stamp+toast double-fire, collapse replay on hydration) are all fixed in this diff. Two conscious accepts: the pre-existing
transition-allon the shared Button cva is left as-is (stock shadcn; scoping it repo-wide is a separate change), and the points pill snaps ~2ch wider at digit-count boundaries (rare; the fixes are all worse).Test plan
turbo run build --filter=ui --filter=playground --filter=code✓ (15/15)pnpm test:unit✓ (3053 passed)pnpm format/ lint-staged ✓https://claude.ai/code/session_01DobvVEwfVg89UUMzUBsxRF
Summary by CodeRabbit
New Features
Improvements