Dynamic Island component#26
Merged
Merged
Conversation
iOS-style island pill that morphs between live activity views. Shell resizes via layout animation with a slightly underdamped spring for Apple-like overshoot; views crossfade with SPRING_SWAP blur slots. Controlled API mirrors morphing-modal: view id prop plus a compact slot shown when idle. role=status with aria-live announces view changes; reduced motion collapses everything to opacity crossfades. Preview demos call, timer (rolling countdown via NumberTicker) and music states.
Three compounding artifacts on the shrink path: - slots were plain children of the layout-animating shell, so they got stretched per-frame during the resize; layout="position" opts them into proper scale correction - the exiting view is popped out of layout at its old size and the shell shrinks over it, so the slow blurred exit read as clipped flicker; exits are now 120ms, opacity + slight scale only - shell spring damped a touch (28 -> 30, mass 0.65) so the overshoot does not wobble the clip on the way down
Layout-projection approach kept flickering on collapse: the shell animated via transform scale, so slots needed per-frame scale correction and the popped exiting view clashed with the shrinking transform. New approach eliminates transforms entirely. A ResizeObserver tracks the natural size of the active content and the shell springs its real width, height and borderRadius toward it (per motion docs: animating border alongside layout defeats transform perf anyway, so animate the box classically). Slots render at natural size inside a w-max sizer and are never distorted. Padding moves from shell to slots so the measurement includes it.
- compact pill gets real iPhone proportions (126 x 37) so every state blooms out of and collapses back into the same capsule - shell springs split by direction: expansion is deliberately underdamped for a visible bloom overshoot, collapse snaps back tighter and faster - content originates from the pill core: views enter at scale 0.6 with blur, slightly delayed so the shell leads; exits get sucked back to center in 100ms before the shrinking shell can clip them - expanded radius eased to 24 for a rounded-rect look against the full-pill compact state
Content scaled from its own center while the shell grew symmetrically, so expansion never read as coming out of the pill. Now everything is anchored top center like the real island: the shell pins content to its top edge, slots unfurl downward (origin top, slight y drift, scale 0.8) and exits get sucked back up into the pill. Preview reserves a fixed top-aligned zone so the shell grows downward and the controls stay put.
The corner flash on collapse came from springing borderRadius between a fake 999 and 24: spring overshoot across that range glitches the rendered corner mid-resize. Radius now tweens 200ms between honest values (18.5, exactly half the 37px pill, and 24 expanded), decoupled from the size spring so the corner shape never wobbles. Snappier overall: shell springs stiffened (expand 550/25, collapse 620/32, same bounce ratios), content spring 560/28, exits trimmed to 80ms and opacity/blur tweens tightened.
Sticky feel had two sources. The shell waited for an async ResizeObserver callback before springing (a 2-3 frame stall after every press); the sizer is now measured synchronously in a layout effect on each view swap, so shell and content launch in the same frame, with ResizeObserver kept for async drift like ticker digits and font loads. Content delays dropped (views 0, compact 20ms) so slots travel with the shell instead of trailing it, and damping eased a touch on all three springs for more follow-through.
This reverts commit a37c9f5.
Revert the press-stall experiment, then fix what heavy actually was: the collapse spring was deliberately stiffer and more damped than the expand (620/32 vs 550/25), which read as magnetic resistance on the squeeze. Collapse now carries the same bounce character as the bloom (520/24, mass 0.45) so the pill squeezes slightly past its size and springs back instead of crunching shut.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
New motion component: iOS-style Dynamic Island. Pill morphs between live activity views (call, timer, music in the preview) with Apple-style physics.
How it works
API
Controlled, mirrors morphing-modal:
viewid pluscompactslot,DynamicIslandViewchildren. Registry bundle verified: ships with lib/ease.ts and lib/utils.ts only.Also adds a gitignored /docs/ folder for local working notes.
Verification