Animation identification

How MotionPR recognises the same element and the same animation in two versions of your app.

On this page
  1. Element identity
  2. Matching
  3. Low confidence means uncertain
  4. Give important elements an id

A comparison is only as good as its pairing. Before MotionPR can say "this transition got slower", it has to decide that two records, one per capture, are the same animation.

Element identity

Every animated element gets an identity key, chosen in this order:

PriorityMethodKey exampleConfidence
1motionpr-idmotionpr-id:menuhigh
2test-idtest-id:nav-panelhigh
3dom-idid:pricing (unique, not generated)medium
4ariaaria:button[Open menu]medium
5dom-pathpath:test-id:work>div:nth-of-type(2)low
  • identity.attribute (default data-motionpr-id) and identity.testIdAttribute (default data-testid) are configurable.
  • An id counts only when it is unique in the page and does not look generated: React's :r1:, «r1», prefixes such as radix-, headlessui-, mui-, ember, ng-, long hex runs and digit-heavy ids are skipped.
  • An accessible label counts for aria-label, and for the text of leaf buttons, links, tabs and menu items when it is unique among elements of the same kind.
  • A DOM path is anchored at the nearest ancestor with a stronger identity, so path:test-id:work>div:nth-of-type(2) survives changes elsewhere in the page.

The record id combines the element key, the animation type and its name: motionpr-id:menu::css-transition::transform. Duplicates get #2, #3.

Matching

Records are matched per checkpoint in three deterministic passes:

PassRuleConfidence
exactIdentical record idthe lower of the two sides
elementSame element and type, different name, and exactly one such record on each side (a renamed @keyframes)medium, or low
fuzzySame type, name and tag, the same accessible label or selector, and at least 50% property overlapalways low

Anything left over is added (only in the candidate) or removed (only in the baseline). An element pass records a note such as Same element, renamed from "process@discover" to "process@design"; the demo app's Anime.js timeline produces exactly that.

Every difference in the demo app's real comparison (changed, added and removed records), filterable by severity, engine and text:

Interactive diff viewer: needs JavaScript.

Low confidence means uncertain

With comparison.uncertainOnLowConfidence (default true), every change on a pair where either side was identified by DOM position only is reported as uncertain, and an uncertain difference never fails a run unless failOnUncertain is set. A wrong pairing therefore cannot produce a false regression; it produces a question. The capture also carries a LOW_CONFIDENCE_IDENTITY note with the number of such animations.

The demo app has one on purpose: a decorative glow without an id inside the work grid. Its duration change is reported as uncertain with the detail (element matched by DOM position only).

Give important elements an id

HTML
<nav data-motionpr-id="menu">…</nav>
<dialog data-motionpr-id="booking-dialog">…</dialog>
<li data-motionpr-id="card-1">…</li>

Numbered ids such as card-1, card-2 are also what stagger detection groups by: siblings whose keys differ only by a trailing number form one stagger family. See Motion comparison.

GSAP and Anime.js records are additionally named by the adapter (hero-intro@lines), so name timelines you care about with track(timeline, "name").