On 0.0.173, a position: absolute child of a native screen's root sometimes pins to the screen and sometimes scrolls away with the ScrollView's content. The two screens I can compare have identical structure, so I cannot make it depend on anything in the markup.
The shape, on both:
<View class="flex-1 …"> <!-- root -->
<ScrollView class="flex-1" @refresh :refreshing>…</ScrollView>
<View class="absolute bottom-4 right-4 h-14 w-14 rounded-full">…</View>
</View>Today — the absolute child holds its position: measured at y≈730 pt before scrolling and y≈730 pt after scrolling a long page. Correct.
Progress — it is not on screen at the top of the page at all, and appears near the bottom only once you scroll to the end of the content, half behind the floating tab bar. It is travelling with the content, i.e. the root's height has resolved to the content height rather than the viewport.
Both screens: root flex-1, ScrollView flex-1 with the same @refresh / :refreshing props and nothing else, the absolute child as the second and last root child. I diffed them line by line; the ScrollView's attributes are character-for-character the same.
What I ruled out, each with a build and a look on an iPhone 17 Pro (iOS 26.5):
- Root class source. Today writes the classes literally, Progress and Calendar use a bound
:class. Calendar also uses the bound form and pins correctly, so it is not that. - An explicit height on the root.
h-fulladded to the root's bound class (verified present in the compiled bundle) changes nothing. - A trailing spacer. Today and Calendar end their scroll content with
<View class="h-24" />; Progress does not. Adding one changes nothing. - Content wrapping. Today's scroll content sits inside a
<Fragment>, Progress's does not. Wrapping Progress's content in aFragmentchanges nothing. - The component not rendering. Its
testIDappears the expected number of times in the built bundle, and it does render — just in the wrong place.
So the two screens that pin are the one with a Fragment and the one with stickyHeaderIndices, and the three that do not have plain content. That is a correlation I could not turn into a cause, and it may be coincidence.
Why I think it is the root's height. rootStack is pinned to the safe area on all four edges in viewDidLoad, so the root should have a definite height on every screen, and flex-1 should then clamp the ScrollView. CraftNativeScrollView.measure returns height ?? contentFitting(…).height — so wherever the height arrives as nil the scroll view reports its content height, and a parent sizing to it grows past the viewport. On the screens that misbehave, that looks like what happens.
Happy to put a reduced screen in a branch if that helps — the app reproduces it reliably on three screens and not on two, with the same markup, so it should reduce.
I have shipped the element on the two screens that place it correctly and left it off the other three.