Skip to content

feat(TextInput, TextArea, NumberInput): fields stay above the keyboard - #9777

Open
vjeux wants to merge 2 commits into
meta:mainfrom
vjeux:c09/keyboard-f1
Open

vjeux wants to merge 2 commits into
meta:mainfrom
vjeux:c09/keyboard-f1

Conversation

@vjeux

@vjeux vjeux commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Stack: #9777 → #10462

Behavior module: the keyboard module

Builds on #8559, #9776 and #8176. This PR's change: vjeux/astryx@44862e5...d3e9cce

User need

On a phone, a low field should rise above the opening keyboard, with no pan, and come back when it closes.

Problem and solution fit

Each field decides where it is:

  • in scrolling content, its scroller or the document scrolls it 10 px above the keyboard, and in Chrome a pan takes any rest;
  • in a bar docked at the bottom, even the app's, the bar rides the keyboard;
  • in an app's fixed container, a scroller in it, else the page or the pan;
  • in a fully expanded Tall BottomSheet, the sheet.

On iOS the reveal rises with the keyboard and goes back after it, unless the person scrolled; reduced motion makes both instant. Where Firefox for Android turns the reveal back into a document scroll after the keyboard's hide report, the scroll back follows it. A report moves nothing while a finger is down after a touch began since placement. A mouse attaches nothing.

Current specification

spec:AST-099 FR9, DEC-5, DEC-6; family:input-fields FR13. The records commit adds FR9's viewport-offset, open, close and touch sentences, Gap 13 and five rows. It meets Gap 2 and updates Gaps 1, 10 and 11, one row and FR13.

Public delta and caller burden

No prop, option or export. [feat] changeset.

Design and alternatives

  • The pan is the owners' scroll-range interim.
  • A docked bar rides the keyboard, putting back Chrome's centring reveal (DEC-6, Gap 11).
  • On open, Safari's and UIKit's reveals glide. Safari pauses page frames, so the rise is the browser's smooth scroll; Chrome moves the field at once (Gap 13).
  • On close, Safari and UIKit keep the page raised; the maintainers chose a smooth scroll back.

Evidence

iOS Simulator, sandbox page /sandbox/pages/mobile-keyboard-fields/, 3+ takes per build:

  • Open: on a0f647d the page lands in one frame. On d619304, whose rise this head keeps, it rises through 6 to 9 frames, settling within 0.12 s after the keyboard settles (frames).
  • Close: main leaves the page up; this head returns it, keeping a person's scroll.

Fields end 10 px above the keyboard (frames). With no keyboard, nothing moves (captures). Android: Chrome and Firefox pass at this head; Firefox cannot pan a page with no range (Gap 11). A physical iPhone gates release.

Scope

  • One capability is added.
  • Unrelated fixes, cleanup, and policy changes were removed or split.
  • Consumer docs, focused tests, and migration evidence ship with the feature.
  • Public text and artifacts contain no internal Meta context.

Testing

  • Node 24.22.1 from .nvmrc, after pnpm install --frozen-lockfile, on main 44862e5 with this change.
  • Against main 44862e5, a keyboard report waits only while a finger is down after a touch has begun since the last reveal. So the press that focused the field holds nothing, as when Gboard reports 48 px, then its full height, under a long press. The input stream counts touch starts (getTouchStartCount) and fingers on the screen (isTouchOnScreen) for that. A finger down at the keyboard's last hide counts no more until a touch starts (isFingerOnScreen). A touch whose target leaves the page ends where the window never hears it. FR9 states the hold. The rise on iOS reads Core's iOS WebKit check (isIosWebKit). Its query is the same -webkit-touch-callout test as before, in condition form. The previous check passes the same tests under the same stub, and no Android browser supports that property.
  • Android emulator, an independent set at this head, in Chrome with Gboard and Firefox for Android: normal speed, slow, and animations removed all pass. In 6 of 6 runs, FR9's reveal ends 9.9 px clear in Firefox and 9.6 px in Chrome. Back then returns the page to its place before the reveal. A drag while Firefox's toolbar hides makes no Core scroll call until the finger lifts (3 of 3). Gboard stays up, and Back keeps the drag's scroll. A long press that focuses a field is not reproducible there, since a long press does not open Gboard. The unit tests cover the keyboard's first report during that press.
  • Earlier heads: an independent emulator set at e6280ea passed Firefox's close. Plain Back returned the page to its place before the reveal (4 of 4, normal speed and slow). A finger drag's scroll was kept (4 of 4). In that drag the reveal re-applied itself with small scrolls as Firefox's toolbar hid; this head reveals nothing until the finger is up.
  • Red-first, each arm a commit on this head in the fork:
  • vitest run over TextInput, TextArea, NumberInput, the keyboard module, the motion module, BottomSheet, Chat, Layer, Popover, Outline, DateInput, ProgressBar, the touch and private-attribute helpers, the gestures, the hooks, utils and the sandbox's route tests: 325 files, 7,613 tests pass. The UI tests for the changed set (--project ui --changed): 506 files, 15,717 tests pass.
  • CI=true pnpm lint: 0 errors. pnpm build, then pnpm typecheck: 0 errors. Knowledge records, changesets and their coverage pass. pnpm api:report adds nothing for the keyboard module, the input stream or the three fields. The touch-support, lazy-sheet and layer-toast bundle guards pass (19). In Chromium, the three fields' accessibility specs (49) and BottomSheet's keyboard-inset interaction spec with the interaction harness's own spec (158, 10 skipped, 2 of 2) pass; CI runs the harness spec in WebKit. pnpm review:pre: no findings.
  • iOS Simulator (iOS 26.2, iPhone 17 Pro, Safari), the sandbox page, on builds of main (6718d7e) and of d619304, alternating builds over 3 rounds; real taps on Quantity, Notes and Title, then the keyboard's Done; Quantity again with a slow one-finger drag of the page while the keyboard shows.
    • Open, read from the screen recording at 60 fps: the page's position from an unfocused field's label and box, and the keyboard's top from the screen's right margin. Takes whose recording drops no more than 70 ms around the keyboard's first frame count. On d619304 the page rises through 6 to 9 frames, starting within 50 ms of the keyboard's first frame and settling 50 to 117 ms after the keyboard settles; no frame carries more than 27 % of the move (Quantity 3 takes, Notes 1). On main, Safari's own reveal moves through 6 to 10 frames, starting 0 to 100 ms before the keyboard's first frame and settling by the time the keyboard does (8 takes). On a0f647d, an earlier round shows Quantity's 198 px landing in one frame and Notes' 346 of 354 px in one (frames). The page's own frames pause for 0.2 to 1.1 s after the focus on every build, so a rise set from script each frame could not run then.
    • Close, from the page's own per-frame numbers: on d619304, Quantity, Title and Notes are revealed (document scroll 198, 149 and 355); at the hide report, 498 to 561 ms after Done, the page smooth-scrolls back, settled 814 to 895 ms after Done, never rising. It ends at 0, 0 and 7 px: 7 is Safari's own scroll at the focus, before the reveal (9 of 9). After the drag, the page stays where the drag left it (285, 281 and 285; 3 of 3).
    • Main: Safari's own reveal scrolls 379, 316 and 493 px; after Done the page stays at 379, 316 and 469 (9 of 9), and at 466 after the drag (3 of 3).
    • A plain HTML form with no script of its own: 411, 354 and 514; after Done, 411, 354 and 501 (9 of 9).
    • Two earlier builds of the scroll back, measured and dropped. One scrolled back from the blur: Safari carried each scroll as a visual-viewport offset until the hide report, so the page came back only 2 to 21 px. One set the scroll each frame after the report: Safari applied it a frame late, and the page jumped back by up to 20 px. So the scroll back waits for the report and uses the browser's smooth scroll.
    • This head changes neither path in Safari or Chrome: its wait for Firefox's fold starts only when the document reads where it was before the reveal, and its touch flag only holds while a finger is down. The unit set replays Safari's close (its own 7 px scroll at the focus, the rise, Done; again after a drag) and Chrome's, and finds the same decision at the report, with no wait.
  • No keyboard open, on builds of main and of this head, the sandbox page: desktop Chromium (1280 × 800, mouse) and the iOS 26.2 Simulator. Each field's drawn box, read from the page, is unchanged (largest change 0 px over 3 fields, page unscrolled) (captures and offsets). The new sandbox page is the only fixture change; no story's framing moves.
  • Chromium, on de4c4f0, whose pan this head keeps, with a real visual viewport (pinch-zoomed through the DevTools protocol so it can pan): window.scrollBy on a page with no range leaves visualViewport.offsetTop at 0; the fixed-probe pan to 100, then 250, reads 100, then 250, with the document unscrolled, before the call returns.
  • Chromium on de4c4f0, whose reveal this head keeps in Chromium, with the interaction specs' stand-in keyboard (336 px on 412 × 783), every frame for 30 frames after the report: at a short page's end, at a scroll area's end, in a low scroll area, in a floating panel and in a sheet of a set height, the field sits 10.0 px above the keyboard from the report on, with one pan and no second move. The docked Title is unchanged (10.2 px, no pan). On the previous head each stayed under the stand-in keyboard, left to Chrome's own pan of the keyboard's whole height.
  • Android (Chrome 133 and Gboard, emulator) per-frame data from the previous head, Remove animations, Message in the docked toolbar: Gboard reports 735.2 first; after the bar lands, Chrome scrolls the document from 154.3 to 498.3 and pans 48, and both persist after the real 447.2 report. 498.3 is the scroll that centres Message's document position in the 735.2 viewport (154.3 + 711.5 − 367.6 = 498.2); 48 is all the room that keyboard leaves. The ride's unit cases replay that sequence.
  • Android emulator (Android 16, Gboard; Chrome 133 at normal speed, 4× CPU and with animations removed; Firefox for Android 159 at normal speed, slow and with animations removed), on de4c4f0's code, whose reveal this head keeps (the scroll back and the rise are new since; the independent emulator set below took both at d619304): Chrome passes every take. With animations removed and Message focused in the docked toolbar, Chrome's document scrolls at 502 and 522 ms show in no painted frame (scrollY stays 154.3, offset 0), and the bar sits on the keyboard's top at 1023 ms. A short page's last field ends 9.9 px clear (36.2 before), and the page field and docked toolbar pass in both browsers. In Firefox for Android the pan moves nothing where the page has no scroll range, as before this change: the low scroll area's field ends 142.5 px under the keyboard, the floating panel's 122 px, and the sheet's 111 px (2.5 px at normal speed, after Firefox's own 108.5 px pan). Gap 11 records these cases; the scroll range is their fix. Firefox's short page was not recorded.
  • Android emulator (Android 16, Gboard, 420 dpi), an independent emulator set at d619304, the head before the wait for Firefox's fold and the drag fix; Chrome 133 and Firefox for Android 159 at normal speed, 4× or slow, and with animations removed, on the sandbox page: the open passes in all 6 cells (one frame, 9.6 px clear in Chrome and 10.0 px in Firefox, with no frame between); Chrome's close returns the page to its place before the reveal and keeps a person's scroll (3 of 3 each, 120 → 195 and 120 → 242). Firefox's close stayed at 76.4 in 2 of 4 takes, and at normal speed a drag's scroll was lost once (50.7 → 0 before the hide report).
  • Firefox for Android at normal speed on d619304, every scroll call logged: the only Core calls with the keyboard up were the reveal's scrollBy and two pan put-backs during a drag, each when the toolbar hid (visual viewport 523 → 530 → 553.5) with the finger still down; this head leaves that pan to the person. At Back, the hide report came 603 ms after it with a page scroll of 0 and an offset of 76.4, and Firefox then turned the offset into a document scroll of 76.4: the fold this head now follows. A drag's scroll (200.6) was kept after Back (463.9), and the loss to 0 did not recur.
  • iOS Simulator (iOS 26.2, iPhone 17 Pro, Safari), from the page's own per-frame numbers (the keyboard's top is the visual viewport's height):
    • On a build of de4c4f0, whose end positions this head keeps (the rise changes only the path to them; 10 takes): at the end of a full-height scroll area (3 takes), the field is claimed and ends 10.0 px clear in every settled frame, after the document's 311 px scroll past the page's end; on a build of the previous head's code (8d0cde8, 3 takes), Safari's own reveal left it 36.0 px clear. At a short page's end, in a low scroll area, in a floating panel and in a sheet of a set height, both builds end the field 10.0, 9.6, 10.0 and 10.0 px clear, so Safari takes no pan. Title ends 9.6 px clear after a 173 px scroll, with the toolbar resting under the keyboard; with Message focused (2 takes), the toolbar's container bottom meets the keyboard's top with no page scroll and is back at rest after the blur.
    • On a build of 8d0cde8, the previous head's code (19 takes, none discarded): Title's bottom goes from 540.4 to 367.4 against a keyboard top of 377 (computed reveal 173.4, carried out as a 173.3 px visual-viewport offset). Quantity (numeric keypad, keyboard top 404): 9.6 px clear after 222 px (computed 222.4). Notes, from a page scrolled to 200: 9.6 px clear after 178 (computed 178.4). The floating and own-translate toolbars are not moved (layout tops 658 to 657.7 and 666 to 665.7), and Safari's own reveal shows their field 28.3 and 20.3 px clear; their frames are left out because Safari draws its own UI over the page there. A scroll area in a full-viewport panel scrolls its field 9.6 px clear (133 px), with no page scroll and no visual-viewport offset. Without the hook (main), Safari's own reveal shows the floating panel's field 168 px clear and the sheet's 177 px clear.

Writing rubric results

R1 narration: none; present tense.
R2 research: none in the record.
R3 evidence: rows name methods and failures, not runs.
R4 rejected alternatives: none added.
R5 change references: none.
R6 open questions: none.

@vercel

vercel Bot commented Nov 3, 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 9, 2026 6:18am UTC

Request Review

@github-actions github-actions Bot added the needs:design-review Affects visuals — Design should review label Nov 3, 2026
@astracat-bot

astracat-bot Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🟡 Reviewed (for maintainers only) Nov 9, 2026, 6:49 AM UTC

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 3, 2026
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Nov 3, 2026
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · d3e9cce

Storybook · Sandbox · built from this PR's head

Modified components

Component Size LoC Visual · A11y · RTL
BottomSheet 68.1 kB (+1.7 kB) · 7.7 kB CSS (0) — ✗ · ✓ · —
BottomSheetSwitcher 18.0 kB (+134 B) · 3.4 kB CSS (0) — ✗ · ✓ · —
TextArea · Storybook 🔴 85.7 kB (+23.3 kB) · 9.3 kB CSS (0) 494 (+2) ✓ · ✓ · ✓
141 more components
Component Size LoC Visual · A11y · RTL
NumberInput · Storybook 🔴 94.8 kB (+23.1 kB) · 11.4 kB CSS (0) 1,342 (+2) ✓ · ✓ · ✓
TextInput · Storybook 🔴 90.6 kB (+22.9 kB) · 11.2 kB CSS (0) 418 (+2) ✓ · ✓ · —
lab/EmojiPicker 🔴 109.4 kB (+22.9 kB) · 751 B CSS (0) — — · — · —
Pagination 🔴 129.1 kB (+19.4 kB) · 17.8 kB CSS (0) — — · — · —
lab/TransferList 🔴 127.8 kB (+19.4 kB) · 1.7 kB CSS (0) — — · — · —
lab/TransferListSelector 🔴 134.4 kB (+19.4 kB) · 1.9 kB CSS (0) — — · — · —
richtext/RichTextEditorToolbar 🔴 296.8 kB (+19.1 kB) · 454 B CSS (0) — — · — · —
lab/ChatEmojiPicker 🔴 111.7 kB (+7.5 kB) · 773 B CSS (0) — — · — · —
lab/ChatReactionBar 🔴 112.8 kB (+7.5 kB) · 1.1 kB CSS (0) — — · — · —
lab/CommentComposer 🔴 154.5 kB (+5.0 kB) · 1.9 kB CSS (0) — — · — · —
lab/CommentThread 🔴 345.8 kB (+4.2 kB) · 2.4 kB CSS (0) — — · — · —
lab/Comments 🔴 352.5 kB (+4.2 kB) · 2.5 kB CSS (0) — — · — · —
PowerSearch 286.8 kB (+1.8 kB) · 24.1 kB CSS (0) — — · — · —
charts/ChartTooltip 181.9 kB (+1.8 kB) · 423 B CSS (0) — — · — · —
DateInput 186.6 kB (+1.7 kB) · 17.0 kB CSS (0) — — · — · —
charts/Chart 204.5 kB (+1.7 kB) · 494 B CSS (0) — — · — · —
TimeInput 162.0 kB (+1.7 kB) · 16.0 kB CSS (0) — — · — · —
DateTimeInput 208.3 kB (+1.7 kB) · 18.7 kB CSS (0) — — · — · —
CommandPalette 73.6 kB (+528 B) · 12.7 kB CSS (0) — — · — · —
lab/TourStep 96.3 kB (+507 B) · 590 B CSS (0) — — · — · —
Selector 100.1 kB (+504 B) · 17.4 kB CSS (0) — — · — · —
lab/ListInput 102.6 kB (+503 B) · 1.6 kB CSS (0) — — · — · —
Tokenizer 91.9 kB (+493 B) · 18.8 kB CSS (0) — — · — · —
MoreMenu 97.7 kB (+492 B) · 17.6 kB CSS (0) — — · — · —
DropdownMenu 97.5 kB (+489 B) · 17.6 kB CSS (0) — — · — · —
Popover 90.0 kB (+488 B) · 14.2 kB CSS (0) — — · — · —
ContextMenu 108.6 kB (+487 B) · 17.4 kB CSS (0) — — · — · —
Markdown 187.7 kB (+485 B) · 20.6 kB CSS (0) — — · — · —
ChatLayout 81.9 kB (+484 B) · 10.5 kB CSS (0) — — · — · —
Drawer 50.9 kB (+474 B) · 8.3 kB CSS (0) — — · — · —
Table 124.9 kB (+472 B) · 18.3 kB CSS (0) — — · — · —
DateRangeInput 92.9 kB (+470 B) · 12.5 kB CSS (0) — — · — · —
Carousel 70.3 kB (+467 B) · 10.9 kB CSS (0) — — · — · —
TableHeaderCell 109.5 kB (+464 B) · 17.7 kB CSS (0) — — · — · —
lab/Schedule 106.3 kB (+463 B) · 3.9 kB CSS (0) — — · — · —
MultiSelector 95.1 kB (+462 B) · 16.8 kB CSS (0) — — · — · —
TableCell 109.9 kB (+458 B) · 17.9 kB CSS (0) — — · — · —
SegmentedControl 94.8 kB (+223 B) · 6.9 kB CSS (0) — — · — · —
CheckboxList 68.0 kB (+213 B) · 7.7 kB CSS (0) — — · — · —
ChatComposerInput 75.8 kB (+207 B) · 11.3 kB CSS (0) — — · — · —
Thumbnail 64.8 kB (+206 B) · 11.6 kB CSS (0) — — · — · —
TopNavMegaMenu 69.9 kB (+206 B) · 11.8 kB CSS (0) — — · — · —
ChatComposerTokenElement 75.8 kB (+205 B) · 11.3 kB CSS (0) — — · — · —
AppShell 91.1 kB (+203 B) · 13.5 kB CSS (0) — — · — · —
lab/DialogHeroHeader 83.4 kB (+202 B) · 372 B CSS (0) — — · — · —
TableSelectionToolbar 70.7 kB (+201 B) · 13.2 kB CSS (0) — — · — · —
ListItem 70.5 kB (+200 B) · 11.4 kB CSS (0) — — · — · —
TopNav 72.4 kB (+200 B) · 10.9 kB CSS (0) — — · — · —
TabMenu 71.9 kB (+197 B) · 11.1 kB CSS (0) — — · — · —
ChatSendButton 60.9 kB (+196 B) · 10.0 kB CSS (0) — — · — · —
Banner 62.4 kB (+195 B) · 10.5 kB CSS (0) — — · — · —
ChatDictationButton 61.0 kB (+195 B) · 10.0 kB CSS (0) — — · — · —
DrawerHeader 63.4 kB (+194 B) · 12.9 kB CSS (0) — — · — · —
FileInput 69.3 kB (+194 B) · 10.9 kB CSS (0) — — · — · —
MobileNav 70.7 kB (+192 B) · 10.6 kB CSS (0) — — · — · —
AvatarGroup 46.1 kB (+191 B) · 5.7 kB CSS (0) — — · — · —
CheckboxListItem 71.4 kB (+191 B) · 11.4 kB CSS (0) — — · — · —
Calendar 71.4 kB (+190 B) · 10.6 kB CSS (0) — — · — · —
ComplexSelector 71.1 kB (+190 B) · 11.8 kB CSS (0) — — · — · —
Link 53.2 kB (+190 B) · 7.0 kB CSS (0) — — · — · —
MobileNavToggle 60.7 kB (+190 B) · 10.0 kB CSS (0) — — · — · —
Slider 107.9 kB (+190 B) · 8.8 kB CSS (0) — — · — · —
Avatar 43.3 kB (+189 B) · 5.5 kB CSS (0) — — · — · —
DialogHeader 63.3 kB (+189 B) · 11.5 kB CSS (0) — — · — · —
SideNavItem 70.6 kB (+189 B) · 11.1 kB CSS (0) — — · — · —
lab/CommentPin 57.3 kB (+189 B) · 1.2 kB CSS (0) — — · — · —
lab/DiffViewer 87.2 kB (+189 B) · 2.1 kB CSS (0) — — · — · —
ChatComposer 78.1 kB (+188 B) · 12.0 kB CSS (0) — — · — · —
HoverCard 27.7 kB (+188 B) · 4.4 kB CSS (0) — — · — · —
InfoTip 46.8 kB (+188 B) · 5.3 kB CSS (0) — — · — · —
Stepper 62.0 kB (+188 B) · 10.1 kB CSS (0) — — · — · —
BaseTypeahead 71.2 kB (+187 B) · 10.9 kB CSS (0) — — · — · —
ChatLayoutScrollButton 61.0 kB (+187 B) · 10.2 kB CSS (0) — — · — · —
AvatarGroupOverflow 49.0 kB (+186 B) · 5.6 kB CSS (0) — — · — · —
Button 60.4 kB (+186 B) · 10.0 kB CSS (0) — — · — · —
SideNavCollapseButton 61.3 kB (+186 B) · 10.0 kB CSS (0) — — · — · —
RadioList 56.5 kB (+185 B) · 7.3 kB CSS (0) — — · — · —
RadioListItem 67.6 kB (+185 B) · 10.1 kB CSS (0) — — · — · —
Switch 102.4 kB (+185 B) · 9.9 kB CSS (0) — — · — · —
AlertDialog 105.5 kB (+184 B) · 14.6 kB CSS (0) — — · — · —
lab/SelectableCardGroup 64.6 kB (+184 B) · 162 B CSS (0) — — · — · —
BreadcrumbItem 84.8 kB (+183 B) · 12.1 kB CSS (0) — — · — · —
IconButton 60.5 kB (+182 B) · 10.0 kB CSS (0) — — · — · —
TopNavMenu 74.7 kB (+182 B) · 11.2 kB CSS (0) — — · — · —
CodeBlock 75.2 kB (+181 B) · 11.3 kB CSS (0) — — · — · —
ContextMenuItem 29.5 kB (+180 B) · 6.3 kB CSS (0) — — · — · —
FieldLabel 51.2 kB (+180 B) · 5.7 kB CSS (0) — — · — · —
Lightbox 121.1 kB (+180 B) · 10.6 kB CSS (0) — — · — · —
Typeahead 79.8 kB (+179 B) · 12.4 kB CSS (0) — — · — · —
DropdownMenuSubMenu 70.4 kB (+178 B) · 9.7 kB CSS (0) — — · — · —
InputGroup 56.7 kB (+177 B) · 7.8 kB CSS (0) — — · — · —
Toast 98.3 kB (+176 B) · 10.3 kB CSS (0) — — · — · —
CheckboxInput 64.4 kB (+175 B) · 9.2 kB CSS (0) — — · — · —
SideNav 112.8 kB (+173 B) · 11.4 kB CSS (0) — — · — · —
richtext/RichTextView 276.4 kB (+173 B) · 2.3 kB CSS (0) — — · — · —
richtext/RichTextEditor 297.4 kB (+172 B) · 2.6 kB CSS (0) — — · — · —
TabList 44.0 kB (+171 B) · 6.5 kB CSS (0) — — · — · —
lab/LineClamp 14.0 kB (+171 B) · 168 B CSS (0) — — · — · —
CommandPaletteInput 31.7 kB (+169 B) · 7.5 kB CSS (0) — — · — · —
Field 55.0 kB (+168 B) · 7.2 kB CSS (0) — — · — · —
Toolbar 43.9 kB (+168 B) · 8.1 kB CSS (0) — — · — · —
Tooltip 29.7 kB (+168 B) · 4.4 kB CSS (0) — — · — · —
lab/NavigationScreen 81.0 kB (+168 B) · 1.5 kB CSS (0) — — · — · —
Timestamp 32.9 kB (+167 B) · 10.3 kB CSS (0) — — · — · —
SideNavHeading 75.7 kB (+166 B) · 11.3 kB CSS (0) — — · — · —
lab/NavigationStack 81.0 kB (+166 B) · 1.5 kB CSS (0) — — · — · —
ButtonGroup 41.7 kB (+165 B) · 3.7 kB CSS (0) — — · — · —
charts/ChartLegend 49.5 kB (+163 B) · 224 B CSS (0) — — · — · —
Text 11.8 kB (+162 B) · 5.4 kB CSS (0) — — · — · —
TopNavHeading 75.3 kB (+162 B) · 11.2 kB CSS (0) — — · — · —
ToggleButton 86.8 kB (+161 B) · 10.1 kB CSS (0) — — · — · —
Timer 14.0 kB (+160 B) · 6.0 kB CSS (0) — — · — · —
SelectorOption 21.1 kB (+159 B) · 6.1 kB CSS (0) — — · — · —
Spinner 24.7 kB (+159 B) · 6.2 kB CSS (0) — — · — · —
CommandPaletteItem 13.1 kB (+157 B) · 3.6 kB CSS (0) — — · — · —
ChatToolCalls 32.0 kB (+156 B) · 8.3 kB CSS (0) — — · — · —
Token 28.6 kB (+155 B) · 4.7 kB CSS (0) — — · — · —
StatusDot 31.4 kB (+153 B) · 5.3 kB CSS (0) — — · — · —
Collapsible 27.2 kB (+152 B) · 4.5 kB CSS (0) — — · — · —
Heading 12.4 kB (+152 B) · 5.4 kB CSS (0) — — · — · —
TopNavItem 13.9 kB (+151 B) · 4.0 kB CSS (0) — — · — · —
ChatMessageList 28.1 kB (+150 B) · 7.0 kB CSS (0) — — · — · —
Tab 14.5 kB (+149 B) · 4.2 kB CSS (0) — — · — · —
DropdownMenuRadioItem 23.0 kB (+147 B) · 6.6 kB CSS (0) — — · — · —
Dialog 24.0 kB (+146 B) · 6.9 kB CSS (0) — — · — · —
Step 31.1 kB (+146 B) · 5.6 kB CSS (0) — — · — · —
lab/PageControl 73.9 kB (+145 B) · 709 B CSS (0) — — · — · —
Item 17.6 kB (+144 B) · 5.5 kB CSS (0) — — · — · —
ToggleButtonGroup 39.3 kB (+144 B) · 3.7 kB CSS (0) — — · — · —
DropdownMenuCheckboxItem 22.9 kB (+143 B) · 6.6 kB CSS (0) — — · — · —
ClickableCard 18.1 kB (+142 B) · 7.3 kB CSS (0) — — · — · —
NavHeadingMenu 16.4 kB (+138 B) · 3.5 kB CSS (0) — — · — · —
TopNavMegaMenuItem 13.8 kB (+138 B) · 4.2 kB CSS (0) — — · — · —
TreeList 44.3 kB (+138 B) · 5.6 kB CSS (0) — — · — · —
DropdownMenuItem 22.1 kB (+137 B) · 6.3 kB CSS (0) — — · — · —
SelectableCard 18.3 kB (+137 B) · 7.5 kB CSS (0) — — · — · —
Outline 24.9 kB (+134 B) · 4.1 kB CSS (0) — — · — · —
SegmentedControlItem 17.5 kB (+134 B) · 4.3 kB CSS (0) — — · — · —
lab/TabBar 75.5 kB (+128 B) · 1.8 kB CSS (0) — — · — · —
ProgressBar 11.7 kB (+1 B) · 6.3 kB CSS (0) — — · — · —
lab/TabBarItem 3.8 kB (0) · 1.8 kB CSS (0) — — · — · —
Visual changes: 2 snapshots in 1 component

A change is a question for the reviewer, not a failure.

Component · story Before After Diff
BottomSheet · Text Only (neutral, dark) before frame after frame diff frame
BottomSheet · Text Only (neutral, light) before frame after frame diff frame

Package size: @astryxdesign/core 7.19 MB packed (+14.3 kB); no new dependencies.

API changes: none in this pull request

Committed API report behind this build: cli 29 rows, core 34 rows, theme-butter 1 row, theme-y2k 1 row; run pnpm api:report.

Touched components checked; add ci:run-visual, ci:run-a11y or ci:run-rtl to run the full suites.


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.

This pull request needs an engineering owner's spec decision before it can be approved, and it also needs code changes.

Approval waits on an engineering owner to provide or identify the governing spec for TextInput, TextArea and NumberInput keeping themselves above the on-screen keyboard: AST-099 FR9 and input-fields FR13 exist only in open #9750, and the docked-bar ride is an exception to AST-099 FR3. The same owner decides on this head's FR8 exception and DEC-5, including which decision keeps the number DEC-5. An engineering owner records that by commenting /approve-spec <full head SHA>, and the author may do so as an engineering owner.

Required changes:

  • A field claims the browser's own reveal but stays hidden when it is above the view, reached by a hardware keyboard's Tab, or low in a Dialog or Hug BottomSheet, because reveal in keyboardTarget.ts only lifts a field covered from below, through one scroller.
  • The field path re-implements the sheet's reveal without its upper-edge branch and reads the coarse-pointer gate once at attach, so the two paths have already drifted; share one reveal routine and key the field's attachment on the same touch state.
  • On touch screens, the tap that focuses a field cancels the touch and swallows the click, so a caller's onClick on TextInput never runs for that tap, which family:input-fields FR10 does not allow.
  • The changeset says the fields keep themselves above the keyboard, that focus never pans the page and that nothing changes under a mouse; the tests do not show this and the cases above contradict it, so fix the code or narrow the wording.
  • AST-099 DEC-3 requires simulator and emulator evidence for the three fields before merge, and none is attached yet.
  • At 8,101 authored lines the diff includes the unlanded #8559 and #9776; land them first so this pull request carries only its own 277 lines.

[Automated review]

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

This pull request needs an engineering owner's spec decision before it can be approved, and it also needs code changes.

Approval waits on an engineering owner to provide or identify the governing spec for TextInput, TextArea and NumberInput keeping themselves above the on-screen keyboard: AST-099 FR9 and input-fields FR13 exist only in open #9750, and moving an app's fixed bottom bar is an exception to AST-099 FR3. The same decision covers this head's AST-099 FR8 exception and DEC-5, including which record keeps the number DEC-5, since #9750 uses it for a different decision. An engineering owner records it by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner.

Required changes:

  • A field now refuses the browser's own reveal but only scrolls itself up when the keyboard covers it from below, so a field focused above the view, reached by a hardware keyboard's Tab, or low in a Dialog or a Hug or Capped BottomSheet stays hidden, and the BottomSheet height docs still say the browser's reveal is left in place there.
  • On touch screens the tap that focuses a field cancels its touchend and swallows the click, so a caller's onClick on TextInput, TextArea or NumberInput never runs for that tap, which input-fields FR10 does not allow.
  • The field's reveal in keyboardTarget.ts re-implements the sheet's scrollControlIntoSafeArea without its upper-edge branch; share one reveal routine so the two paths cannot drift.
  • The changeset and title say the fields keep themselves above the keyboard and that focus never pans the page, which the tests do not show and the cases above contradict; fix the code or narrow the wording.
  • AST-099 DEC-3 requires simulator and emulator evidence for the three fields before merge, and none is attached yet.
  • At 8,400 authored lines the diff carries the unlanded #8559 and #9776; land them first so this pull request carries only its own 277 lines.

[Automated review]

@vjeux vjeux changed the title feat(TextInput, TextArea, NumberInput): the focused field stays above the on-screen keyboard feat(TextInput, TextArea, NumberInput): fields stay above the keyboard Nov 6, 2026
@github-actions github-actions Bot removed needs:design-review Affects visuals — Design should review needs:spec-owner-review Current knowledge records await owner approval labels Nov 6, 2026

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

Changes requested. The direction is settled: spec:AST-099 FR9 and DEC-5 and family:input-fields FR13 are now current, so no spec approval is needed here, and the page reveal and the docked-bar ride behave as FR9 describes.

  • A field near the end of its scroller, such as a short form's last field, stays under the keyboard: the field path claims the focus but cannot add the missing scroll range that AST-099 FR9 and architecture:react-component-runtime INV11 require, so with a stand-in 300 px keyboard in Chromium the last field of a page that scrolls 84 px stays 264 px covered, where main lets the browser reveal it.
  • AST-099 DEC-3's iOS Simulator and Android emulator evidence is still missing, and AST-099 Gaps 1, 2 and 11, and BottomSheet.doc.mjs's "the browser's own focus reveal is left in place" for these fields in a Hug or Capped sheet whose body scrolls, stop being true with this change, so those edits should land with the evidence.
  • The changeset says a field in the page or a scrolling area "is scrolled into place 10 px above the keyboard as it opens", which the first point shows is not true near a scroller's end, so fix that case or narrow the wording.

Non-blocking: the comment // The on-screen keyboard never covers the focused field. in each field restates the hook's name and can go. CI has not run on this head yet.

[Automated review]

@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Nov 7, 2026

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

This pull request needs an engineering owner's spec decision before it can be approved, and it also needs code changes.

Approval waits on an engineering owner to approve or change the spec:AST-099 text this head adds: the FR9 sentence that a document scroll carried out as a visual-viewport offset is the reveal and not a pan, the new Firefox and Chrome FR9 verification rows, and the docked-toolbar exception on the Safari and Chrome row. An engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so as an engineering owner.

Required changes:

  • A TextInput, TextArea or NumberInput in a scroll area whose top sits below the keyboard's top is claimed by canBringUp (keyboardTarget.ts:824-834) but its scroller cannot lift it, so it stays under the keyboard (117 px with a stand-in 300 px keyboard) where main leaves the browser's own reveal to show it.
  • AST-099 DEC-3 makes Android emulator evidence at the exact head a merge gate, and it is still pending, including for a short page's last field and the docked toolbar's placement at Chrome's first report.
  • The changeset says every page field is scrolled 10 px above the keyboard and that a scroll area which can bring the field to its top does the same, but a page with nothing to scroll keeps the browser's reveal and the first point disproves the second.

The earlier findings on the scroll area's end, the end-of-page stories, the focusing tap's click and pointer changes are fixed at this head.

[Automated review]

vjeux added a commit to vjeux/astryx that referenced this pull request Nov 7, 2026
Page content only: each keyboard frame ends at the keyboard top (dark
line). Replaces the frames at 6218413.
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Nov 8, 2026
@cixzhang

cixzhang commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

/approve-spec a0f647d

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Nov 8, 2026

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

Changes requested: one change remains.

The spec direction is settled: an engineering owner's /approve-spec covers this head's spec:AST-099 and family:input-fields text. With #10462 now stacked on this head, the size is no longer a blocker, and the Safari Simulator take of the keyboard-close scroll back is now reported.

  • Device evidence: spec:AST-099 Platform support makes Android emulator evidence at the exact head a merge gate, but the keyboard-close scroll back (keyboardScrollBack.ts) has no Chrome-with-Gboard run, and the Chrome and Firefox for Android runs in the description predate it; record a page field closed by Done and by Back at normal speed, with animations removed and on a slow profile, plus the Firefox page take.

One earlier non-blocking comment suggestion still applies. This pull request lands only once it is approved; #10462 lands after it.

[Automated review]

vjeux added a commit to vjeux/astryx that referenced this pull request Nov 8, 2026
This head's tests, with keyboardHeight.ts, keyboardTarget.ts,
useMobileKeyboard.ts and keyboardScrollBack.ts as at meta#9777's head
(933a93e).
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Nov 8, 2026
@cixzhang

cixzhang commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

/approve-spec 933a93e

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Nov 8, 2026

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

Changes requested: an engineering owner's approval of this head settles the spec direction, and two changes remain.

  • Miss: the keyboard-close scroll back reads only prefers-reduced-motion (keyboardScrollBack.ts:149-153), so under a theme with reducedMotion: 'always' the page glides back on every keyboard close, against design:reduced-motion DR2a; decide it, and risesSmoothly, from the motion module's shared reduced-motion answer (the one reducedMotionFade gives), and add a scroll-back test under the theme's mark.
  • spec:AST-099 Platform support asks for Android emulator evidence at the exact head, and no Chrome or Firefox for Android run covers this head or the keyboard-close scroll back yet.

Thanks for the Safari Simulator runs of this head: they cover the rise and close rows. Non-blocking suggestions from the last review's inline comments still apply.

[Automated review]

vjeux added a commit to vjeux/astryx that referenced this pull request Nov 8, 2026
This head's tests, with keyboardHeight.ts, keyboardTarget.ts,
useMobileKeyboard.ts and keyboardScrollBack.ts as at meta#9777's head
(d619304).
@github-actions github-actions Bot added the needs:spec-owner-review Current knowledge records await owner approval label Nov 8, 2026
@cixzhang

cixzhang commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

/approve-spec d619304

@github-actions github-actions Bot removed the needs:spec-owner-review Current knowledge records await owner approval label Nov 8, 2026

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

Changes requested: an engineering owner's approval of this head settles the spec direction, and one change remains.

  • spec:AST-099 Platform support makes Android emulator evidence at the exact head a merge gate, and no Chrome on Android (Gboard; normal speed, animations removed, slow profile) or Firefox for Android emulator run covers this head or the keyboard-close scroll back yet.

The iOS Simulator open and close runs now reported for this head cover the Safari side, so the Android takes are all that is left.

[Automated review]

@cixzhang

cixzhang commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

/approve-spec e6280ea

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

Changes requested: an engineering owner's approval of this head settles the spec direction, and two changes remain.

  • keyboardHeight.ts keeps its own record of whether a finger is down (isDown with its own touchend and touchcancel subscriptions, lines 510-521) beside Core's shared ones (the gesture host's fingersDown() and getDownPresses in utils/inputStream.ts), so read or extend one of them here, or say beside isDown why neither can serve.
  • spec:AST-099 Platform support makes Android emulator evidence at the exact head a merge gate, and no Chrome on Android (Gboard; normal speed, animations removed, slow profile) or Firefox for Android run covers this head yet, including the Firefox close row it adds and the scroll back's wait for Firefox's late document scroll.

The description now shows the Firefox behaviour that wait relies on and the red-first arms for both new paths, which covers the earlier ask about it. The one failing CI job is an interaction-harness self-test in files main changed after this branch's base, so updating the branch onto main should clear it.

[Automated review]

| FR9 | TBD (Gap 1–11): Safari on iOS in the iOS Simulator and Chrome on Android in the emulator, top-level document | TextInput in the page, ChatComposer in its dock, a Selector search row in its sheet, and a TextInput in a fixed Toolbar | The field ends under the keyboard, or the page lurches |
| FR9 | TBD (Gap 1–11): Safari on iOS in the iOS Simulator and Chrome on Android in the emulator, top-level document | TextInput in the page, ChatComposer in its dock, a Selector search row in its sheet, and a TextInput in a fixed Toolbar | The field ends under the keyboard, or the page lurches. A docked toolbar that rests under the keyboard while a field outside it has focus is not a failure |
| FR9 | Firefox for Android in the emulator, top-level document | TextInput in the page, revealed by the document | The visual viewport's offset differs from the reveal's computed distance (the field's bottom plus 10 CSS px, less the keyboard's top), or the field ends under the keyboard |
| FR9 | Firefox for Android in the emulator, top-level document | TextInput in the page: focus it, then close the keyboard with Back, at normal speed and on a slow profile | The page ends other than where it was before the reveal (to 0.5 CSS px) |

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.

No run of this row is reported, and the description does not show the Firefox behaviour the scroll back's fold wait relies on. spec:AST-099 Platform support makes Android emulator evidence at the exact head a merge gate: record this take at normal speed and on a slow profile, with the Chrome-with-Gboard takes of this head, in the description.

Comment thread packages/core/src/keyboard/README.md Resolved
@cixzhang

cixzhang commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

/approve-spec 23f26c6

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

Approval waits on an engineering owner's decision on one behaviour, and two required changes remain.

  • Spec decision: after the keyboard's first report, a report that arrives while a finger is on the screen now reveals nothing (keyboardTarget.ts:770), so a real keyboard height change during a touch also leaves the field where it is; no spec:AST-099 sentence states this, so an engineering owner needs to approve the rule as written or ask for a change (an FR9 sentence that states it would be recorded with /approve-spec <full head SHA>, which the author may comment as an engineering owner).
  • Miss: risesSmoothly (keyboardTarget.ts:376-383) re-implements Core's iOS WebKit check instead of calling isIosWebKit from utils/touchQueries.ts, so please call it (return isIosWebKit(win) && !reducesMotion(field);) and add the keyboard rise to its list of named uses.
  • Required change: spec:AST-099 Platform support makes Android emulator evidence at the exact head a merge gate, and no Chrome with Gboard or Firefox for Android run covers this head yet, so please record the Firefox for Android rows (spec.md:442-443) and the Chrome with Gboard row (spec.md:444) on this head, including a drag while the toolbar hides.

The isIosWebKit point was missed in earlier reviews of this code. The three non-blocking inline notes from the last review still apply.

[Automated review]

Comment thread packages/core/src/utils/inputStream.ts Resolved
@cixzhang

cixzhang commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

/approve-spec 24e337d

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

Changes requested: two required changes remain.

The engineering owner's /approve-spec for this head settles the spec direction, and the description's Testing section now describes this head.

  • Miss: risesSmoothly (keyboardTarget.ts:377-383) re-implements Core's iOS WebKit check instead of calling isIosWebKit from utils/touchQueries.ts, so please call it (return isIosWebKit(win) && !reducesMotion(field);) and add the keyboard rise to its list of named uses.
  • spec:AST-099 Platform support makes Android emulator evidence at the exact head a merge gate, and no Chrome-with-Gboard or Firefox-for-Android run covers this head's touch hold, so please record the Firefox for Android rows (spec.md:444-445) and the Chrome with Gboard row (spec.md:446) on this head, including a drag while the toolbar hides and a long press that focuses a field while Gboard grows.

The isIosWebKit point was raised in the last two reviews and missed in earlier ones. The non-blocking note on the startsAtReveal comment (keyboardTarget.ts:716) still applies.

[Automated review]

vjeux added 2 commits November 8, 2026 20:28
Each passes its input to `useMobileKeyboard` from its own render, with
no prop and no Effect of its own. On focus the field decides where it
is: inside a keyboard target such as a Tall BottomSheet it moves
nothing; in an app's bar docked at the viewport's bottom, the bar rides
the keyboard, its bottom on the keyboard's top; in scrolling content,
its nearest scroller, the document included, scrolls it 10 px above the
keyboard in the keyboard's report. In a fixed container the app
positions itself, a scroller around the field reveals it, and with none
the browser's own reveal stays. Its first tap and focus moves into it
arrive without the browser's reveal where the module places the field,
so the page never pans there.

A scroll area does not gain the range it lacks at its end yet. Where a
field's scroll area cannot place it, the document reveals it, in the
page and in a fixed container where the document's scroll moves the
container, as in Safari while its keyboard shows; elsewhere Core scrolls
the document back. Where the document cannot scroll far enough, as at a
short page's end in Chrome, a pan of the visual viewport takes the rest
in the same frame (`panVisualViewportTo`): a probe fixed in the layout
viewport is scrolled into view, as the reveal-pan watch puts a pan back.
The watch takes Core's own pan as the new rest. Firefox for Android does
not pan for the probe. A page with nothing to scroll keeps the browser's
own reveal.

On Chrome's show, a docked bar first sets its field 4 px above the
keyboard's top at once, as the Tall sheet does, and only the rest rides,
so Chrome's own pan finds the field clear. Every viewport report now
feeds the ride's reveal-pan watch, so a pan Chrome still makes is put
back, and a pan made at one keyboard height goes back at the next.
Under Remove animations, Gboard first reports a keyboard 48 px tall,
and Chrome may then reveal the bar's field itself: it scrolls the page
behind the bar, then pans. In Chrome, the ride's watch scrolls the
document back as well, when the scroll and the pan centre the field as
Chrome's reveal does; the app's own scroll to another element stays.

On iOS, a reveal that places the field rises on the browser's smooth
scroll from the keyboard's first report, which comes as the keyboard
starts to slide, so the field rises beside it (`risesSmoothly`, which
reads Core's iOS WebKit check, `isIosWebKit`). The scroll back records
where the rise is headed, and a rise still on its way when the keyboard
changes height carries on from its target. Under Reduce Motion, in a
theme that reduces motion for everyone, and in Chrome, which checks the
field in the resize's own commit, the reveal moves at once. The rise and
the scroll back ask the motion module's new `reducesMotion`, beside
`reducedMotionFade`, which now uses it, so a theme that reduces motion
for everyone counts as the setting for both. A scroll area's reveal goes
through its own `scrollBy` with an explicit behavior where it has one,
so a smooth `scroll-behavior` on the area cannot slow an instant reveal,
as the Tall sheet's reveal already does.

When the keyboard closes, each scroller a reveal moved scrolls back to
where it was (keyboardScrollBack.ts), on the browser's smooth scroll
from the keyboard's report that it has gone. While Safari's keyboard
slides down, Safari holds the visual viewport, so a scroll then shows
only at the slide's end; and Safari applies a scripted scroll a frame
late, so a scroll set each frame jitters. A scroller the person
scrolled meanwhile keeps their scroll. A sandbox page puts three fields
in the middle of a page that scrolls the document.

Each field's `.keyboard.test.tsx` drives the keyboard module's test
double. Mobile keyboard stories on TextInput place the fields low in the
page, a TextInput in a toolbar fixed at the bottom (docked, floating
above the edge, or with its own `translate`), a last field at the end of
a short page and of a scroll area, a scroll area low on the page, a
scroll area in a floating panel and in a full-viewport panel, and a
field in a sheet of a set height.

Firefox for Android can carry the reveal's document scroll as a
visual-viewport offset, show the document where it was before the
reveal at the keyboard's hide report, and turn the offset back into a
document scroll only after it. The scroll back then waits up to a
second for the first document scroll: one that lands where Core left
the document is the browser's own, and the scroll back follows it. A
touch, a keyboard that comes back, the field's page going, or the
wait's end puts nothing back.

The reveal-pan watch's touch flag now holds through a report that comes
while a finger is on the screen. In Firefox the toolbar hides in a drag,
which changes the measured keyboard height mid-drag; the watch then took
the drag's own pan for the browser's and put it back. The input stream
now counts the fingers on the screen from touch events
(`isTouchOnScreen`), only while `touchend` and `touchcancel` are heard.
A native pan keeps a finger there, though its `pointercancel` drops the
press from `getDownPresses`, so that record cannot serve. The watch's
shared touch subscription hears those two types, and a report reads the
count.

After the show's first reveal, a keyboard report reveals nothing while a
press that started after the last reveal is down. In Firefox the toolbar
hides in a drag, and the reveal re-applied itself with small scrolls
under the person's finger. The report's height is kept, so the field
stays where the person put it, and a later report with the finger up
reveals it as before. The press that focused the field holds nothing: a
long press focuses with the finger still down, and Gboard can report a
48 px keyboard first, then its full height. The input stream also counts
the touches that start (`getTouchStartCount`), and each reveal notes
that count. The keyboard module reads the finger count through
`isFingerOnScreen`: a finger down at the keyboard's last hide counts no
more until a touch starts, since a touch whose target leaves the page
ends at a node the window never hears. The count includes a stylus,
whose drag moves the page as a finger does.

Six unit tests cover the scroll back: the page's smooth scroll at the
report (nothing before it), Reduce Motion, a theme that reduces motion,
a person's scroll kept, a scroll area after Back, and a focus move to
another field. Firefox-shaped cases carry the reveal as an offset and
close the keyboard before the fold: the scroll back follows the fold, a
touch-drag control keeps the page, only the first document scroll
counts, and each way the wait ends leaves no scroll listener. Safari's
and Chrome's close sequences still decide at the report, with no wait. A
Firefox-shaped drag grows the viewport twice with the finger down and
finds no put-back; after the finger is up, a browser pan is put back.
Another drags the page with the toolbar hiding twice and finds no reveal
scroll until the finger is up. A long press focuses the field while
Gboard grows from 48 to 290 px, and the field is revealed at 290; the
next show's first reveal runs again under the same press, and a press
that starts after the reveal holds the next report until it lifts. After
the keyboard goes with no finger down, a long press that brings it back
is revealed with that press still down. Another holds a report while the
long press's finger stays down after a second finger tapped and lifted.
Another misses a touch's end, as when its target leaves the page, and
finds a browser pan put back once the keyboard has hidden and shown
again. The input stream's tests count fingers through a second finger, a
cancel and a native pan's `pointercancel`, count nothing while a touch
end could go unheard, and count the touches that start. Six cover the
rise on iOS: its smooth scroll, Reduce Motion, a theme that reduces
motion, the scroll back after a rise drawn later, a rise carried on at a
new height, and no second reveal while a scroll area's rise is drawn.
Their CSS.supports stub answers both of Safari's forms, a property and
value or a condition. Another drives a scroll area with a smooth
`scroll-behavior` and finds its own `scrollBy` called at once. Two in
the motion module cover `reducesMotion`: the setting, and a theme that
reduces motion for everyone.
FR9's scrolling-content bullet adds the spec owners' sentence: where a
browser carries out the document's scroll by moving the visual viewport,
that offset is the reveal, not a pan. Gap 2 is met. Gaps 1, 10 and 11
say what the three fields now do. Gap 11 keeps the missing scroll range
as the fix; until a scroller gains it, Core pans the visual viewport
where the document cannot scroll far enough.

Two verification rows cover Firefox for Android (the offset equals the
computed reveal) and Chrome (no pan added after Core places the field).
Chrome's names the cases Core pans, and fails a field Core pans that
ends other than 10 CSS px above the keyboard. In Firefox for Android
the pan moves nothing where the page has no scroll range; Gap 11 names
those cases.
The device-evidence row records that a docked toolbar resting under the
keyboard while a field outside it has focus is not a failure.
BottomSheet's height doc says what such a field gets in a sheet that is
not a fully expanded Tall sheet.

FR9's scrolling-content bullet adds the close: each scroller that
revealed the field scrolls back to where it was before the reveal, and
a scroll the person made while the keyboard showed is kept. A
verification row covers a field in the middle of a page that scrolls
the document.

FR9's scrolling-content bullet adds the open: on iOS the field rises on
the browser's own smooth scroll beside the keyboard's slide, settling
within 0.15 s after the keyboard settles, and it moves in one frame
under Reduce Motion, in a theme that reduces motion for everyone, and in
other browsers. A verification row reads every drawn frame of the rise.
Gap 13 records Chrome's rise, which stays in one frame because Chrome
checks the field in the resize's own commit; Firefox for Android takes
the same path. The device-evidence row's
failure for a reveal later than the keyboard's report notes that on iOS
the rise starts in that frame and settles as FR9 states.

FR9's scrolling-content bullet adds the hold: a show's first report
always places the field, and a later report that comes while a finger
is down, after a touch has begun since the field's last placement,
moves nothing.

FR9's close adds that at the hide report, Firefox for Android can still
show the document where it was before the reveal, and turns the reveal
back into a document scroll only after that report; the scroll back
follows that scroll. A row closes the keyboard with Back in
Firefox for Android at normal speed and on a slow profile.

The input-fields family's FR13 status says the same: the three fields
meet FR13 through FR9, apart from Gap 11.
@cixzhang

cixzhang commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

/approve-spec d3e9cce

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

Changes requested: one required change remains.

The engineering owner's /approve-spec for this head settles the spec direction, and the description now describes this head.

  • spec:AST-099 Platform support makes Android emulator evidence at the exact head, with animations at normal scale and removed plus a slow-device profile, a merge gate, and the description's Chrome and Firefox for Android takes are of the previous commit at normal speed only, so please record the Firefox for Android rows (spec.md:444-445) and the Chrome with Gboard row (spec.md:446) on this head in all three profiles, including the drag while Firefox's toolbar hides.

A long-press take is not needed: no row names it, and the unit cases cover it.

[Automated review]

This branch was successfully deployed

1 active deployment
Preview — d3e9cce5 Deployed Nov 9, 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. needs:design-review Affects visuals — Design should review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants