Repository navigation
fix(native): resolve a deferred declaration on its own target, in cascade order - #474
Open
YevheniiKotyrlo wants to merge 5 commits into
Open
YevheniiKotyrlo wants to merge 5 commits into
YevheniiKotyrlo wants to merge 5 commits into
Conversation
applyDeclarations reassigned its target parameter per declaration, and a
deferred declaration's closure captured that shared binding. The closure
runs after every declaration in the rule has been walked, so it read
whatever nested target the last declaration ended on: a deferred color
beside a box-shadow checked its placeholder against the shadow entry,
missed it, and left the internal { color: true } in the style.
-webkit-line-clamp: var(--n) left { numberOfLines: true } on the prop the
same way.
Each declaration now walks into a binding of its own.
A deferred declaration resolves after every other declaration of the element, so a shorthand that resolved at runtime spread its keys over whatever a later declaration had set: border: var(--b) followed by border-color: blue painted the variable's colour, within a rule and across rules, and so did border-inline-width: var(--w) over a later border-inline-start-width. Each key belongs to the declaration that set it last (css-cascade-5 §6.4). Each element now records, per target and key, the cascade position of its last writer. A deferred shorthand learns its keys only when it resolves, so it claims them one by one and keeps the ones a later declaration holds.
This was referenced Oct 11, 2026
Contributor
Author
YevheniiKotyrlo
marked this pull request as ready for review
October 11, 2026 13:42
A shadow that omits its colour takes currentcolor, which resolves as a
deferred declaration inside the shadow. On main its placeholder reaches
the style: textShadowColor: { textShadowColor: true } for a colourless
text-shadow beside a color, and color: { color: true } on a colourless
box-shadow entry under an inherited colour.
YevheniiKotyrlo
marked this pull request as draft
October 16, 2026 14:04
A later `text-shadow` writes its offset through `textShadowOffset.width`
and `.height`, and only the leaf was claimed, so an earlier
`text-shadow: var()` that resolved late still replaced `textShadowOffset`
whole: `.earlier { --s: 2px 2px blue; text-shadow: var(--s) }` before
`.my-class { text-shadow: 1px 1px red }` drew the red shadow at the blue
offset. A declaration written through a path now claims each key it
passes through.
YevheniiKotyrlo
marked this pull request as ready for review
October 16, 2026 14:13
The two text-shadow rows are about which declaration wins, so the colour a variable carries is written in the form a runtime reader keeps whether it serialises a named colour or not.
This branch has not been deployed
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.


Problem
A declaration whose value depends on a
var()resolves after every other declaration of its element. Two defects onmainfollow from that:applyDeclarations's sharedtargetbinding, so it checked its placeholder against whatever nested target the last declaration ended on, missed it, and left the internal placeholder in the style.color: var(--c); box-shadow: 1px 1px bluerenderscolor: { color: true },text-shadowdoes the same, and-webkit-line-clamp: var(--n)beside a deferred colour rendersnumberOfLines: { numberOfLines: true }.border: var(--b); border-color: bluepaints the variable's colour, within a rule and across rules,border-inline-width: var(--w)does the same over a laterborder-inline-start-width, and an earlier rule'stext-shadow: var(--s)replaces a later rule'stext-shadow.Solution
textShadowOffset.width, also claims each key it passes through, because a shorthand writestextShadowOffsetwhole.Tests
src/__tests__/native/deferred-declarations.test.tsx: each placeholder shape, a shadow that omits its colour beside a colour, each shorthand before and after its longhand, in one rule and across rules, andtext-shadow: var()before and after a statictext-shadow.main, 10 of its 14 cases fail; the 4 that pass there are the ordersmainalready gets right, kept so they stay right.Verification
On Windows with Node 26:
yarn lintcleanyarn typecheckcleanyarn test: 1345 passed, 4 failed. The 4 are the babel cases that also fail onmainon this machine.yarn buildcleanyarn example expo export --platform webexportedDevice evidence, before and after: #474 (comment)
Merge order
#393 builds on this, so this lands first.
It also shares lines with #389, #391 and #411 (
native/styles/calculate-props.ts); whichever lands second rebases.It also shares lines with #488 (
native/styles/calculate-props.ts); whichever lands second rebases.Base
Branched off
main(a5002c5).