Skip to content

Add AnimatePresence + MotionStyle/CSSValue interp #70

Merged
wheregmis merged 9 commits into
wheregmis:mainfrom
sarendipitee:main
Jun 19, 2026
Merged

Add AnimatePresence + MotionStyle/CSSValue interp #70
wheregmis merged 9 commits into
wheregmis:mainfrom
sarendipitee:main

Conversation

@sarendipitee

Copy link
Copy Markdown
Contributor

No description provided.

When a motion handle has been idle, the web loop can sleep for the idle poll interval. If a short exit tween starts during that sleep, the first update inherited the whole idle delta and could complete immediately, making initially visible elements disappear without a visible exit transition.

Reset the frame clock when transitioning from idle to running and keep web running frames on an 8ms cadence. Presence-driven helpers now wait until the exit animation has actually been observed running before calling safe_to_remove, while preserving immediate removal for zero-duration exits.
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fccfe1b1-0793-4bf6-af64-f9dad76146c6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Adds MotionStyle, a typed animatable CSS style struct, backed by new CssValue/CssColor/CssComplexValue interpolation primitives and motion_style! / presence_style! macros. Migrates Animatable from Copy to Clone throughout the engine. Introduces the AnimatePresence presence subsystem wired behind the dioxus feature. Adds two docs pages, a full spec, a Tailwind CSS rebuild, and a Playwright E2E test.

Changes

AnimatePresence + MotionStyle Feature

Layer / File(s) Summary
Copy→Clone migration across animation engine
src/animations/core.rs, src/motion.rs, src/pool.rs, src/sequence.rs, src/manager.rs, src/lib.rs
Animatable supertrait bound changes from Copy to Clone; explicit .clone() calls are added throughout Motion, RK4 spring integrator, AnimationSequence::current_target, and MotionHandle so animated types no longer need to be Copy.
AnimationConfig constructors and loop timing fix
src/animations/core.rs, src/lib.rs
Adds AnimationConfig::tween, tween_ms, and spring constructor helpers with unit tests. calculate_delay on web is simplified to a constant 8 ms; the use_motion async loop gains a first-active-frame branch that resets timing before proceeding.
CSS value interpolation primitives
src/animations/css.rs, src/animations/mod.rs
Introduces CssValue (numeric units, colors, keywords, complex interpolable strings), CssColor RGBA with clamped arithmetic, CssComplexValue tokenizer, IntoCssValue trait, parse_css_string, is_unitless_css_property, and all supporting color/number parsers. Comprehensive unit tests are included.
MotionStyle typed animation target and motion_style! macro
src/animations/style.rs, src/style_macros.rs
Defines MotionStyle with opacity and 3D transform fields plus a BTreeMap of typed CSS properties; implements Default, Add/Sub/Mul<f32>, Animatable, and Display. Adds motion_style! and motion_style_assign! macros for declarative construction and tested property-name normalization, arithmetic, and interpolation.
Presence subsystem wiring, macros, and dependencies
Cargo.toml, src/lib.rs, src/presence_macros.rs
Adds dioxus-core optional dependency and expanded web-sys feature flags to Cargo.toml. Wires presence and presence_macros modules into src/lib.rs behind the dioxus feature and expands prelude with all CSS and presence re-exports. Adds the presence_style! macro family with layout, tween, and spring helper macros that enforce supported fields at compile time.
AnimatePresence specification document
specs/animate-presence-spec.md
Full spec covering the public API contract (AnimatePresence, PresenceMode, hooks, PresenceHandle), lifecycle state machine, completion orchestration, child normalization rules, PopLayout design constraints, testing matrix, and documentation requirements.
Docs pages: MotionStyleGuide and PresenceGuide
docs/src/pages/motion_style_guide.rs, docs/src/pages/presence_guide.rs, docs/src/utils/router.rs, docs/src/pages/mod.rs, docs/src/pages/docs/index.rs
Adds MotionStyleGuide (interactive Randomize/Pulse showcase using use_motion + MotionStyle) and PresenceGuide (full AnimatePresence guide with basic, spring, stable-keys, mode, pop-layout, custom-data, and manual-removal examples). Registers /motion_style and /presence routes with SlideLeft transitions and adds sidebar navigation links.
Docs tooling: Tailwind CSS rebuild and E2E layout test
docs/assets/main.css, docs/tests/presence_layout_height.mjs, docs/package.json
Rebuilds CSS from Tailwind v4.1.11 to v4.3.1 with new utilities for the presence/style guide UI. Adds a Playwright E2E test that verifies layout-height animation regression behavior on the stable-keys presence section. Adds a test:presence-layout script and playwright dev dependency.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AnimatePresence
  participant PresenceContext
  participant ChildComponent
  participant use_presence_style

  User->>AnimatePresence: render with keyed children
  AnimatePresence->>PresenceContext: register keyed child (entering)
  PresenceContext-->>ChildComponent: provide PresenceHandle + is_present=true
  ChildComponent->>use_presence_style: use_presence_style(presence_style!{...})
  use_presence_style-->>ChildComponent: animate to "animate" state

  User->>AnimatePresence: remove keyed child from tree
  AnimatePresence->>PresenceContext: mark child as exiting (retain in DOM)
  PresenceContext-->>ChildComponent: is_present=false
  ChildComponent->>use_presence_style: animate to "exit" state
  use_presence_style->>PresenceContext: safe_to_remove() on completion
  PresenceContext->>AnimatePresence: unmount retained child
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • wheregmis/dioxus-motion#47: Both PRs modify Animatable trait bounds in src/animations/core.rs; this PR changes the supertrait from Copy to Clone directly building on that trait's structure.
  • wheregmis/dioxus-motion#63: Both PRs modify src/manager.rs (MotionHandle, write_motion) and src/motion.rs/src/lib.rs motion-loop logic; this PR extends those same code paths with Clone-based operations and a new set_current method.
  • wheregmis/dioxus-motion#46: Both PRs modify the calculate_delay helper and use_motion loop timing inside src/lib.rs, with this PR simplifying the delay to a constant 8 ms.

Poem

🐇 Hop hop, the bunny animates with flair,
MotionStyle blooms — transforms fill the air!
With presence_style! the children fade and go,
Spring-loaded exits put on quite a show.
From Copy to Clone, the engine runs free,
AnimatePresence — pure Dioxus poetry! 🎭

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided by the author; the description field is empty. Consider adding a description that explains the purpose, scope, and any breaking changes or migration notes for users.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: adding AnimatePresence functionality and MotionStyle/CSSValue interpolation support to the dioxus-motion library.
Docstring Coverage ✅ Passed Docstring coverage is 88.26% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/package.json (1)

10-11: ⚠️ Potential issue | 🟠 Major

Align Tailwind dependency versions with the committed generated CSS.

docs/package.json pins @tailwindcss/cli and tailwindcss to ^4.1.11, but docs/assets/main.css was generated by tailwindcss v4.3.1 (visible in the file header). This mismatch breaks rebuild reproducibility—regenerating the CSS will produce different output.

Proposed fix
   "dependencies": {
-    "`@tailwindcss/cli`": "^4.1.11",
-    "tailwindcss": "^4.1.11"
+    "`@tailwindcss/cli`": "^4.3.1",
+    "tailwindcss": "^4.3.1"
   }
🤖 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 `@docs/package.json` around lines 10 - 11, The tailwindcss dependencies in
docs/package.json are pinned to version ^4.1.11, but the generated CSS file
docs/assets/main.css was created using tailwindcss v4.3.1 as indicated in its
header comment. Update both the `@tailwindcss/cli` and tailwindcss version entries
in docs/package.json from ^4.1.11 to ^4.3.1 to align with the version that
generated the committed CSS file, ensuring rebuild reproducibility and
preventing future CSS regeneration from producing different output.
🧹 Nitpick comments (1)
src/animations/style.rs (1)

210-213: ⚡ Quick win

Normalize keys in property() for consistency with add_css_property().

Right now property() can bypass normalization and create parallel keys for the same CSS property.

Suggested refactor
     pub fn property(mut self, property: impl Into<String>, value: CssValue) -> Self {
-        self.properties.insert(property.into(), value);
+        let property = normalize_style_property(&property.into());
+        self.properties.insert(property, value);
         self
     }
🤖 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 `@src/animations/style.rs` around lines 210 - 213, The property() method
currently inserts CSS property keys without normalization, which can create
duplicate entries for the same property compared to add_css_property(). Apply
the same key normalization logic that add_css_property() uses when inserting the
property into self.properties, ensuring that keys are normalized before being
used as map keys to maintain consistency across both methods.
🤖 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 `@docs/assets/main.css`:
- Around line 551-563: The stylelint configuration is currently checking
docs/assets/main.css which is a generated Tailwind output file, causing
declaration-empty-line-before violations to be reported on auto-generated
utility blocks like .-translate-x-4, .-translate-x-\[100px\], and
.translate-x-0. Since this file is regenerated on each build, exclude
docs/assets/main.css from stylelint checks by modifying the stylelint
configuration (typically .stylelintrc or stylelint config in package.json) to
either ignore this generated file path or to only lint source Tailwind input
files rather than the compiled output.

In `@docs/src/pages/presence_guide.rs`:
- Around line 756-758: The detail text for the `initial: false` property at line
756 incorrectly states it applies to all "newly mounted children," but the
actual implementation only suppresses the initial animation on first
render—children inserted later still run their enter animation. Update the
detail string to accurately reflect that initial: false only affects the initial
render behavior and clarifies that subsequently mounted children will still
animate on entry.

In `@src/animations/css.rs`:
- Around line 559-579: The parse_rgb_color function incorrectly handles
percentage values in RGB and alpha channels. When parsing color values, the code
strips the percent sign with trim_end_matches but does not scale the values. For
RGB percentages, values should be scaled from 0-100% to 0-255 range, and for
alpha percentages, values should be scaled from 0-100% to 0-1 range. Modify the
parsing logic to detect when a part contains a percent sign, then apply the
appropriate scaling factor based on whether it's an RGB channel (first 3 parts,
multiply by 2.55) or an alpha channel (4th part, divide by 100).
- Around line 219-236: The CssComplexValue::parse method uses byte indexing with
the `index` variable to iterate through the value string, but in the else branch
it advances by only 1 byte with index += 1 after pushing a character. For
non-ASCII characters which are multiple bytes in UTF-8, this causes the next
value[index..] slice to potentially index in the middle of a multi-byte
character, causing a panic. Replace the byte-indexing approach with
character-aware iteration using char_indices() or a similar method that returns
both the character and its correct byte position, so that the index is always
positioned at valid UTF-8 boundaries.

In `@src/animations/style.rs`:
- Around line 480-492: The interpolation logic only processes properties from
the target style, leaving properties that exist only in the start style
(self.properties) in an unstable state. After the existing loop that handles
target properties, add additional logic to iterate over properties in
self.properties that are not present in target.properties, and ensure these
self-only properties interpolate correctly by inserting them back into
style.properties with the appropriate interpolated value (they should
interpolate to themselves to remain stable throughout the animation).

In `@src/lib.rs`:
- Around line 136-143: The prelude module contains a duplicate re-export of
MotionStyle from crate::animations::style. The MotionStyle symbol is currently
re-exported twice, which causes a compilation error E0252. Remove the second pub
use statement that re-exports MotionStyle to eliminate the duplicate declaration
in the module namespace.

---

Outside diff comments:
In `@docs/package.json`:
- Around line 10-11: The tailwindcss dependencies in docs/package.json are
pinned to version ^4.1.11, but the generated CSS file docs/assets/main.css was
created using tailwindcss v4.3.1 as indicated in its header comment. Update both
the `@tailwindcss/cli` and tailwindcss version entries in docs/package.json from
^4.1.11 to ^4.3.1 to align with the version that generated the committed CSS
file, ensuring rebuild reproducibility and preventing future CSS regeneration
from producing different output.

---

Nitpick comments:
In `@src/animations/style.rs`:
- Around line 210-213: The property() method currently inserts CSS property keys
without normalization, which can create duplicate entries for the same property
compared to add_css_property(). Apply the same key normalization logic that
add_css_property() uses when inserting the property into self.properties,
ensuring that keys are normalized before being used as map keys to maintain
consistency across both methods.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88d44ac7-e28c-4d7a-ae99-8fb979adf2e9

📥 Commits

Reviewing files that changed from the base of the PR and between 6ed7e31 and 43a0ba9.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • docs/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (22)
  • Cargo.toml
  • docs/assets/main.css
  • docs/package.json
  • docs/src/pages/docs/index.rs
  • docs/src/pages/mod.rs
  • docs/src/pages/motion_style_guide.rs
  • docs/src/pages/presence_guide.rs
  • docs/src/utils/router.rs
  • docs/tests/presence_layout_height.mjs
  • specs/animate-presence-spec.md
  • src/animations/core.rs
  • src/animations/css.rs
  • src/animations/mod.rs
  • src/animations/style.rs
  • src/lib.rs
  • src/manager.rs
  • src/motion.rs
  • src/pool.rs
  • src/presence.rs
  • src/presence_macros.rs
  • src/sequence.rs
  • src/style_macros.rs

Comment thread docs/assets/main.css
Comment on lines +551 to +563
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.-translate-x-4 {
--tw-translate-x: calc(var(--spacing) * -4);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.-translate-x-\[100px\] {
--tw-translate-x: calc(100px * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.translate-x-0 {
--tw-translate-x: 0;
translate: var(--tw-translate-x) var(--tw-translate-y);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stylelint is failing on generated Tailwind output.

Static analysis reports declaration-empty-line-before violations on these generated utility blocks. Since this file is generated, manual formatting edits will be overwritten on the next build. Prefer excluding docs/assets/main.css from stylelint (or lint only source Tailwind inputs) to keep CI stable.

Also applies to: 677-677, 719-719, 733-733, 1320-1320, 1467-1471

🧰 Tools
🪛 Stylelint (17.13.0)

[error] 551-551: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 555-555: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 559-559: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)


[error] 563-563: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 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 `@docs/assets/main.css` around lines 551 - 563, The stylelint configuration is
currently checking docs/assets/main.css which is a generated Tailwind output
file, causing declaration-empty-line-before violations to be reported on
auto-generated utility blocks like .-translate-x-4, .-translate-x-\[100px\], and
.translate-x-0. Since this file is regenerated on each build, exclude
docs/assets/main.css from stylelint checks by modifying the stylelint
configuration (typically .stylelintrc or stylelint config in package.json) to
either ignore this generated file path or to only lint source Tailwind input
files rather than the compiled output.

Source: Linters/SAST tools

Comment thread docs/src/pages/presence_guide.rs
Comment thread src/animations/css.rs
Comment thread src/animations/css.rs
Comment thread src/animations/style.rs
Comment thread src/lib.rs Outdated

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional Comment: We can definately omit this specs file, if the goal of this file is achieved through the implementation

@wheregmis

wheregmis commented Jun 19, 2026

Copy link
Copy Markdown
Owner

@sarendipitee I dont mind bringing this changes as it is, but if you think some critical nits reported by coderabit is something that we can address, i would love them to be addressed or closed based on your use case. Feel free to mention me on that. PS: Please fix the failed CIs

@sarendipitee

Copy link
Copy Markdown
Contributor Author

nits have been de-picked :)

CodeRabbit seems to be happy now

@wheregmis

Copy link
Copy Markdown
Owner

nits have been de-picked :)

CodeRabbit seems to be happy now

Waiting for CI to go green, Will merge afterwards, Thank you for your contribution. Really appreciate it

@wheregmis wheregmis merged commit c7f7e69 into wheregmis:main Jun 19, 2026
15 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