A transition held in a variable reached React Native unexpanded:
`transition: var(--t)` rendered `{ transition: [...] }`, and
`transition-property: var(--p)` named the CSS properties as written.
Every transition and animation property held in a variable now resolves
at runtime into the lists the compiler builds for a written value, its
keywords in any case, a list holding list variables read as one list, a
time a script sets as "300ms", and only Reanimated's easing objects
taken as a timing function, or writes nothing for a value its property
refuses. A list the declaration writes is refused for a comma group of
several tokens in a longhand or a transition group naming two
properties. `transition-behavior`, which lightningcss does not parse,
is read the same way when written. A value with no variable is read
where the cascade puts it, as `animation` already is, and a value the
compiler cannot read at all is dropped.
A rule was marked animated by its property only when its value was
static, and by the style function's name otherwise, so a longhand read
through a variable, or a timing function written as `cubic-bezier()` or
`steps()`, left its element unanimated. The mark now follows the
property whatever the value.
`transitionStyleKey` names the key a transition animates on both routes:
the key the property is written to, so `background-image` names
`experimental_backgroundImage`, and for a property no style key carries,
such as `visibility`, its own name, so it keeps its place in the list
and every later property keeps its own timing.
Problem
A transition held in a variable never animates as the written one does:
transition: var(--t)with--t: width 1s ease-in 200msrenders{ transition: ["width", 1000, "ease-in", 200] }, where the written value renders the fourtransition*longhands.transition-property: var(--p)names each CSS property as written, sobackground-imageandborder-inline-start-colormatch no style key.transition-duration: var(--d)leaves its element a plain View:addDescriptormarks a rule animated by the property only for a static value, and by the style function's name otherwise, andvarnames neither an animation nor a transition. The same holds for everyanimation-*longhand butanimation-name, and for a writtencubic-bezier()orsteps().EASE-INor on a list holding a list variable,var(--d), 2s.The written route names a transition by the property's camelCase rather than the key it is written to,
backgroundImageforexperimental_backgroundImage, and dropsvisibilityand unknown names from the list, so every later property takes its neighbour's timing, where CSS Transitions 1 §2.1 keeps each at its index.Solution
propertyRenamemoves toutilitiesbesidetoRNProperty, andtransitionStyleKeynames the key a transition animates on both routes, reading a name in any case and without a vendor prefix; a property with no style key, such asvisibility, keeps its own name and its place.addDescriptormarks a rule animated by the property it writes, whatever the value.var()or a value lightningcss leaves unparsed, into the lists the compiler builds for a written value: keywords in any case, list variables in a list as one list, a time a script sets as"300ms", rounded as a written one is throughround, which moves toutilities, and for a timing function only a keyword or the easingcubic-bezier()andsteps()resolve to. It writes nothing for a value the property refuses, or for an item of several tokens in a list the declaration writes. A value with no variable is read where the cascade puts it, asanimationalready is, and one the compiler cannot read at all is dropped.transition-behavior, which lightningcss does not parse, is read the same way.transitionresolver reads each comma group by CSS Transitions 2 §2.6, in any order, a negative time as the delay andallow-discreteas the behavior, and splices a variable's tokens into its group. It writes no transition for a written group naming two properties, or fornonebeside another property, which Reanimated throws on.Left for changes of their own:
width, opacityaswidth opacity), and a variable's list nests where another variable reads it, so a held transition's groups are told apart by property names alone:1s, 2sreads as one transition, a heldwidth var(--d)over--d: 1s ease-inas two, and a held1s 2s, 3sas three times.auto, ease-inreads asease-in.border: var(--b).width: 10px; transition: display 1s allow-discrete; transition-duration: 3skeeps1s.transition-behavior: normal, as lightningcss moves atransition-behaviorit cannot parse ahead of the shorthand, so a later shorthand does not reset an earlierallow-discrete.scaleintotransform, names a key no style has.5px,5and5msalike, so a held duration takes a length as milliseconds.Tests
src/__tests__/native/transition-variables.test.tsxrenders its route cases written, through a variable the compiler inlines, through one it keeps (inlineVariables: false), and through a kept one afterJSON.parse(JSON.stringify(sheet)), the form a device reads, and expects the element handed to Reanimated with the case's transition each way; the evidence comment names the cases that run fewer ways. It also covers a time a script sets throughVariableContextProvider, a later rule over a transition the runtime reads, atransition-behaviorafter atransitionin its rule, and a value the compiler cannot read.animations.test.tsxrenders eachanimation-*longhand butanimation-namethe four ways, and its invalid values the two kept ways.compiler.test.tsxholds the animated flag to every animation and transition property, whatever its value, and to nothing else.property-names.test.tsholds each renamed property's transition to the key it is written to, a name in another case to its key, andvisibilityand a custom property to their own names.Each expected value matches what Chromium, Firefox and WebKit compute with
getComputedStylefor the declaration written and through a variable.Verification
The workflow's jobs pass on Ubuntu, and the iOS dev build passes with Xcode 27.1; the workflow pins 26.6, which I did not run. Not run: a device recording of a transition held in a variable.
255 of this PR's 384 cases fail on
main, and none fail here: #497 (comment)Merge order
Stacked on #491, which gives
toRNPropertytheutilitieshome this extends, so #491 lands first. #413'svariable-fold-parity.test.tsxhas a case pinning the unanimatedtransition-durationthis fixes, so whichever of the two lands second updates it. It also shares files with #389, #391, #393, #411, #417, #420, #447, #448, #449, #459, #494, #495, #501 and #503; whichever lands later rebases.