Skip to content

fix(lab): NavigationStack re-measures a screen when its title resizes - #10191

Merged
cixzhang merged 1 commit into
meta:mainfrom
vjeux:navigation-stack-title-remeasure
Nov 6, 2026
Merged

cixzhang merged 1 commit into
meta:mainfrom
vjeux:navigation-stack-title-remeasure

Conversation

@vjeux

@vjeux vjeux commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

User impact

When only a NavigationStack screen's large title changes height, such as a longer title, a screen that now overflows stays clipped and does not scroll. It offers no keyboard stop and contains no overscroll until another change re-measures it. A larger text size grows the rows too, so it already re-measures.

Problem and solution fit

  • Why is it stale? The large title sat in the scroller but outside useScrollableArea's content root. The hook measures when its viewport or its content root changes size, and a title-only change resized neither.
  • Why does it harm the task? The measured overflow decides whether the screen scrolls at all, its keyboard stop and its overscroll containment.
  • Why does that matter? No other scroll area behaves so.

The title is now the content root's first child, so its height is part of what the hook watches.

Expected behavior and authority

spec:AST-025:

  • FR5: live measurement observes viewport and content geometry.
  • FR12: keyboard access follows effective scrolling.
  • FR14: overscroll containment is active only on axes that overflow.

Each screen's scroller is a useScrollableArea viewport (#9697).

Smallest restoration

  • NavigationStack.tsx: the large <h1> moves inside the content root, first. Its scroll listener finds the scroller one level up. The content root is not positioned, so the title's offsetParent and the collapse math are unchanged. The h1 keeps tabIndex={-1}, which the keyboard handoff skips.
  • Intended side effect: under a large title, the screen's first element is no longer the content root's :first-child. So ScrollableArea fullBleed's pull-up by --container-padding-block-start no longer draws it over the title.
  • In the same files, comment only: the headers of navigationTransition.ts and the browser spec keep their fields. The transition's layer and progress notes move to the TransitionLayers and layersAt JSDoc.

Evidence

  • Reproduction before the change: in the Large title story, the viewport is just taller than the content. A title whose line grows to 400px then leaves the scroller's overscroll-behavior-y at auto.
  • Result after the change: the same growth switches it to contain.
  • Representative unchanged path: the large title still hands over to the bar's title. The whole browser spec passes in both engines.
  • Regression test: "a large title that grows re-measures its screen", red on the committed arm b33c4fde22 (main plus only the spec).

Scope

  • One defect is restored.
  • Separable contradictory or unsettled tagalongs were removed or split. The two header trims are neither: comment-only fixes in the same files.
  • No new public API, visual representation, default, or interaction model is hidden under the bug-fix label.
  • Public text and artifacts contain no internal Meta context.

Testing

  • Browser, NavigationStack.interaction.browser.spec.ts, on a fresh Storybook build of this head: Chromium 44/44; WebKit 40 passed and 4 skipped (Chromium-only rendering traces). The new case passes 5 of 5 in each engine.
  • Red-first: the arm b33c4fde22 is main plus only the new spec. It fails the new case in both engines (Chromium 8 of 8, WebKit 8 of 8), each at the final poll with auto. Before the change, the case lets three unrelated measurements end: a font load, the resize, and the stack's release of its motion hints. Each re-measured main by chance in an earlier draft of this case.
  • Units: NavigationStack.test.tsx and navigationTransition.test.ts, 37/37.
  • pnpm build, pnpm typecheck, the Storybook build, check:knowledge, check:repo, changeset coverage (Lab ships nothing to consumers) and review:pre (144 authored lines, 0 findings).
  • Accessibility contracts, on the same tree before the browser case's last change: 1,216 passed, 1 skipped, 1 failed. The failure: Spinner :203 fails on main since fix(Spinner): route standalone loading through the shared announcement channel #7910; fixed by test(Spinner): wait for announcements before tracing rotor paints #10174 (pending). It fails 22 of 22 runs on main's Spinner code, which this change does not touch.

@vercel

vercel Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Nov 6, 2026 1:22am UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 6, 2026
@astracat-bot

astracat-bot Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 6, 2026, 1:40 AM UTC

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · 740d13a

Storybook · Sandbox · built from this PR's head

Modified components

Component Size LoC
lab/NavigationStack 74.8 kB (+7 B) · 1.1 kB CSS (0) —
lab/NavigationScreen 74.8 kB (+6 B) · 1.1 kB CSS (0) —

Package size: measured; main has no measurement of the merge base to compare yet.

API changes: none in this pull request

Committed API report behind this build: cli 19 rows, core 23 rows; run pnpm api:report.

Not run on this PR: add ci:run-visual, ci:run-a11y or ci:run-rtl to run them.


Generated by PR Enrichment workflow | View full report

@astracat-bot astracat-bot 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.

Requesting changes for a wording fix in the description. The code change does what it says.

Moving the large title into the scroll area's content root makes a title that grows or shrinks re-measure its screen. In the Large title story, the bar handoff and keyboard entry are unchanged.

  • Blocking: the User impact section and the commit message name "larger text" as a cause and describe a scroller that chains at its end, but on main a larger browser text size already re-measures the Large title screen (the rows grow with it) and a stale screen stays overflow-y: clip and does not scroll at all, so please describe a title-only change instead, for example: "When only a screen's large title changes height, such as a longer title, a screen that now overflows stays clipped and does not scroll, offers no keyboard stop and contains no overscroll until another change re-measures it."

CI has not run on this head yet because the pull request is a draft.

[Automated review]

The large title sat outside the scroll area's content root, so a change
of its height alone, such as a longer title, re-measured nothing. A
screen that then overflowed stayed clipped: it did not scroll, offered
no keyboard stop and contained no overscroll until another change
re-measured it. The title is now first in the content root, whose size
the scroll area watches.

The headers of navigationTransition.ts and the browser spec keep their
fields only. The transition's layer notes move to the JSDoc of the type
and function they describe.

@astracat-bot astracat-bot 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.

Approved pending CI: CI has not run on this head yet.

Moving the large title into the scroll area's content root makes a title-only height change re-measure the screen, and the new browser case fails on main and passes here. The reworded description and commit message now match what main does.

Next step: a maintainer adds the ci:run label so CI runs.

[Automated review]

@vjeux
vjeux marked this pull request as ready for review November 6, 2026 01:37
vjeux added a commit to vjeux/astryx that referenced this pull request Nov 6, 2026
Both are open: meta#10191 moves NavigationStack's large title into the scroll area's content, and meta#10172 lets useFingerScroll tell its caller when a finger's scroll ends. They share no file.
@cixzhang cixzhang added the ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) label Nov 6, 2026
@cixzhang
cixzhang marked this pull request as draft November 6, 2026 04:42
@cixzhang
cixzhang marked this pull request as ready for review November 6, 2026 04:42
@cixzhang
cixzhang merged commit 75c3c48 into meta:main Nov 6, 2026
150 of 155 checks passed
@vjeux
vjeux deleted the navigation-stack-title-remeasure branch November 6, 2026 05:39
cixzhang added a commit to vjeux/astryx that referenced this pull request Nov 6, 2026
cixzhang added a commit that referenced this pull request Nov 6, 2026
#8396)

* feat(core): useFingerScroll tells its caller when a finger's scroll ends

`useFingerScroll(scroller, {onEnd})` calls `onEnd` once each time a
finger's scroll on the scroller ends — its scrollend after the lift, the
lift after a scrollend under a resting finger, the quiet fallback, or the
pointer turning fine — after the reader already says false. Unmounting
never calls it. The internal touch entry re-exports the hook for Lab's
ChatVirtualizer, which will drop its own touch listeners for it.

* fix(lab): NavigationStack re-measures a screen when its title resizes

The large title sat outside the scroll area's content root, so a change
of its height alone, such as a longer title, re-measured nothing. A
screen that then overflowed stayed clipped: it did not scroll, offered
no keyboard stop and contained no overscroll until another change
re-measured it. The title is now first in the content root, whose size
the scroll area watches.

The headers of navigationTransition.ts and the browser spec keep their
fields only. The transition's layer notes move to the JSDoc of the type
and function they describe.

* fix(core): useFingerScroll keeps the latest onEnd through the isomorphic layout effect

Core hooks write commit-time refs with useIsomorphicLayoutEffect, which
stays quiet under server rendering.

* docs(core): the finger-scroll re-export names no reader that main does not have

The changeset, the internal touch entry, the gestures README and the
tests say Lab components that watch a finger's scroll, instead of naming
ChatVirtualizer, which is still in an open pull request. Text only.

* Merge #10172 into #10191 as this change's base

Both are open: #10191 moves NavigationStack's large title into the scroll area's content, and #10172 lets useFingerScroll tell its caller when a finger's scroll ends. They share no file.

* feat(lab): under a finger, NavigationStack's bar takes the iOS 26 look

Under a finger, NavigationStack's bars paint no background and no
hairline, as iOS 26's do. Each screen carries a scroll edge effect: a
veil of the body colour that fades in once its content has moved 10px
under the bar, on UIKit's measured springs. A push, a pop and a back
swipe carry each screen's veil with its card, and the bar a card covers
is cut at that card's leading edge. A mouse keeps opaque bars.

The veil and the UIKit bar constants live in Lab. The veil reads Core's
finger-scroll hook, with its end call, and Core's internal touch entry
also names the gesture host and the coarse-pointer check: export-only
additions, with no change to Core's behavior.

* Merge main into the NavigationStack clear bars branch (#10172 and #10191 landed)

* test(lab): NavigationStack's cut specs read the push at held times

Under load, WebKit can report one moving layer's animated translate a
frame late. The old mid-flight read then put the cut 2 to 3 px off the
card's edge while the layers moved as one. The two cut specs now freeze
the scene while the push runs, move every animation of the push to the
same scene times, and read the rendered boxes there.

* Merge branch 'main' into uikit/c16-stack-bar

Co-authored-by: Cindy Zhang <cindyxz@meta.com>

This branch was successfully deployed

1 active deployment
Preview — 740d13a2 Deployed Nov 6, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants