Skip to content

Improve animations and motion handling across the app - #54

Merged
iLevyTate merged 1 commit into
mainfrom
claude/tender-babbage-1ttfr7
Sep 29, 2026
Merged

iLevyTate merged 1 commit into
mainfrom
claude/tender-babbage-1ttfr7

Conversation

@iLevyTate

Copy link
Copy Markdown
Owner

This PR significantly enhances the app's animation system and motion handling, making transitions smoother and more responsive to user interactions and accessibility preferences.

Summary

Refactored the animation system to use Web Animations API for better control and consistency, improved toast notifications with proper slot animations, enhanced dialog transitions, and added comprehensive motion utilities that respect reduced-motion preferences.

Key Changes

Animation Infrastructure

  • Added animateSlot() function to smoothly grow/close element slots in stacks (used for toasts, entries, etc.) with proper handling of gaps, padding, borders, and margins
  • Added motionMs() utility to read CSS motion duration tokens from styles, ensuring JS timers stay in sync with CSS transitions
  • Added motionEase() utility to read the app's standard easing function for Web Animations calls
  • Created helper functions for common animation patterns: markArrival(), fadeViewIn(), spotlight(), growIn(), closeGap(), closeGapAfterRender()

Toast Notifications

  • Replaced inline style-based countdown bar with CSS animation (.toast-countdown) that properly animates from full to empty
  • Implemented smooth slot animations when toasts arrive and leave, so stacked toasts glide into position instead of jumping
  • Fixed double-tap protection logic with clearer comments
  • Added dismissLockScreen() for smooth fade-out of lock screen when unlocking

Dialog & View Transitions

  • Added switchThemeSmoothly() to use View Transitions API for theme changes (Chromium 111+, Safari 18+)
  • Improved modal swapping to keep overlay mounted and fade only the content, preventing backdrop blink
  • Added .is-fresh class to views after navigation to trigger entrance animations for Patterns charts
  • Fixed view navigation to jump to top in the same frame as fade-in, rather than smooth-scrolling through it

Entry Cards & Collapsing

  • Fixed collapsed entry cards to show only the header (was showing a strip of body + action bar)
  • Introduced .entry-body-clip wrapper for proper opacity fade during collapse
  • Added renderAnchored() to hold scroll position when pinning/unpinning coping cards

Lock Screen

  • Added lockShake state to shake PIN field once per failed attempt (not on "Forgot PIN?" click)
  • Implemented smooth fade-out animation when unlocking

Patterns Charts

  • Converted chart animations from transitions (which never ran) to keyframe animations that trigger on view entry
  • Added staggered animation delays for bar charts and ring/spark visualizations
  • Charts now draw in smoothly when the Patterns view opens, but don't replay on background re-renders

CSS Updates

  • Replaced hardcoded motion durations with CSS token references (var(--dur-short), var(--dur-medium), etc.)
  • Added @media (hover: hover) guards for hover states to prevent buttons/cards staying raised on touch devices
  • Added .is-moving, .is-leaving, .is-arriving, .is-spotlit animation classes
  • Improved button transitions to include box-shadow easing
  • Added comprehensive .gap-closer and slot animation styles

Accessibility

  • All motion respects prefers-reduced-motion preference (animations run at ~0ms)
  • Lock screen becomes inert while fading out to prevent interaction
  • Proper focus management in dialogs and after dismissals

Notable Implementation Details

  • Toast countdown bar uses CSS animation with inline animation-duration rather than transition, solving the issue where the end state was set before the element was styled
  • Slot animations use Web Animations API with fill: "forwards" for "out" direction to keep closed state until removal
  • Multiple concurrent slot animations on the same element are counted to prevent premature class removal
  • Motion duration tokens are read from computed styles at runtime, allowing single-point tuning
  • View fresh timer prevents background re-renders from replaying entrance animations

https://claude.ai/code/session_01Vg8Y3obwoznhSjSfg1zgSj

Fixes found while auditing motion, each covered by a new regression block
(33–41) that fails on the previous code:

- Collapsed entry cards showed a 35px strip of body and the whole
  Edit / Copy / Delete bar, inert and dead to taps. The grid-rows collapse
  held two padded children; it now holds one unpadded .entry-body-clip,
  and the content fades with the height.
- Switching from one dialog to another rebuilt the overlay, so its fade-in
  restarted from 0 and the page blinked through. The overlay now stays,
  the card morphs between heights, and focus moves into the new dialog.
- The Undo countdown bar never ran: its end state was set before it had
  been styled, so it sat at scaleX(0). It is a CSS animation now, inset
  inside the pill's rounded ends.
- Toasts glide when a neighbour arrives or leaves instead of jumping a
  whole slot, and are removed when their exit ends, not 20ms before it.
- Pinning the first coping card inserted a ~300px strip above the list and
  the starred card dropped 320px from under the finger. Re-renders that
  change content above the tap point now hold the tapped element still.
- View and step changes jump to the top under the fade-in instead of
  smooth-scrolling through the new content; re-tapping the current tab
  still scrolls smoothly. Saving an entry lands at the top of the journal.
- Deleting an entry closes its space with a placeholder; Undo, new capture
  rows and the pivot follow-up grow their slot open.
- Patterns charts draw in on arrival (their transitions could never run)
  and don't replay on a background render.
- The PIN field shakes once per failed try, not again on "Forgot PIN?",
  and unlocking fades the lock screen off.

Details: every hard-coded duration now uses the motion tokens, and JS
timers read the tokens instead of copying them. Hover lifts are gated to
devices that really hover, so cards and buttons don't stay raised after a
tap. The theme switch cross-fades where View Transitions exist. The service
worker update / precache banners and the install panel use the theme
tokens and the app's type instead of inline system-ui styles, and slide in
and out. Print output skips animations and transient chrome. Reduced
motion takes the instant path everywhere, delays included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vg8Y3obwoznhSjSfg1zgSj
@iLevyTate
iLevyTate merged commit 75488f3 into main Sep 29, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants