Repository navigation
spec(AST-099): Core text inputs stay above the on-screen keyboard - #9750
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
|
/approve-spec 13ee3ee |
There was a problem hiding this comment.
Requesting changes: the keyboard rule itself is owner-approved, but one requirement needs fixing before it can guide implementation.
Blocking
- For a field in the page, FR9 requires the field to stay visible "without moving the document", and its first verification row fails on "a document scroll" (
docs/specs/AST-099-mobile-keyboard/spec.mdlines 162–164 and 287); this contradicts FR4, where the surface reveals the control with its own scroller, so an in-page field (Gap 2) has no conforming way above the keyboard; please state how an in-page field is revealed, and narrow the row's failure to a scroll the browser makes or one left in place.
Smaller suggestions: Gap 7 could also cover PowerSearch's experimental bottom-sheet presentation. FR9's "already applies" wording for the chat dock contradicts Gap 9. Adding the Selector and MultiSelector directories to the path list would route Gap 6's work to this record.
[Automated review]
| internal path, the keyboard module (`architecture:react-component-runtime` | ||
| INV11): the height comes from FR2, focus does not pan the page (FR4), and no | ||
| field measures the keyboard or listens to the visual viewport itself. The | ||
| field keeps itself visible without moving the document. A Core host that |
There was a problem hiding this comment.
For a field in normal page flow, the document is the only scroller that can reveal it. FR4 lets the surface bring the control into view "with its own scroller". This sentence, and the "a document scroll" failure at line 287, rule that scroller out. Could FR9 say how an in-page field is revealed? For example: its nearest scroller, the document included, scrolls it into view after a focus that does not scroll, and that scroller's range grows by the keyboard height when the field sits at its end.
| | FR8 | Firefox for Android, emulator or device, top-level document | focus routes, keyboard shown and hidden, page panned and pinch-zoomed, a sheet moved | The page lurches, the field ends under the keyboard, or the height is wrong after a pan or zoom | | ||
| | FR2–FR5 | Safari on iOS in the iOS Simulator or on a device, top-level document | focus routes, keyboard shown and hidden, page panned and pinch-zoomed, a sheet moved, in Safari and as a home-screen app | The page lurches, the field ends under the keyboard, or the height is wrong after a pan or zoom | | ||
| | FR2–FR5, FR8 | Release gate: physical devices in a top-level document (DEC-3) | iPhone Safari with the toolbar expanded and collapsed; iPhone home-screen app; Firefox for Android (FR2–FR5); Android Chrome with Gboard and Samsung Keyboard at the phone's refresh rate | The page lurches, the field ends under the keyboard, or the surface does not follow the keyboard | | ||
| | FR9 | TBD (Gap 1–10): a focus test per covered field, `<Field>.keyboard.test.tsx` beside each one, driving one keyboard change through the keyboard module's test double | each covered field in the page, a Tall and a `hug` BottomSheet, ChatLayout's fixed and sticky dock, an app's fixed bottom bar, and a fixed Toolbar; focus by tap, Tab, and `focus()` | A focused field left under the keyboard, a document scroll, a second offset inside a host that already applies the height, or a field reading the visual viewport itself | |
There was a problem hiding this comment.
"a document scroll" fails every way to reveal a covered field "in the page". Suggest FR4's wording instead: a scroll the browser makes, or a slipped scroll left in place.
| pointer the row sits in a `hug` BottomSheet, which BottomSheet's hook does | ||
| not cover, and it has no keyboard focus handling of its own. | ||
| - **Gap 7 — PowerSearch.** Its query input (Gap 4) and the text and number | ||
| fields of its value editor, which open in a popover, have no keyboard focus |
There was a problem hiding this comment.
Non-blocking: with experimental.presentation="bottom-sheet", PowerSearch's content search and value editor sit in pinned tall sheets, not a popover. There the sheet hook partly covers them, as Gap 5 says for Tokenizer.
|
/approve-spec 2526d84 |
There was a problem hiding this comment.
Requesting changes: the keyboard rule and the docked ride are owner-approved, but two requirements need fixing before they can guide implementation.
Blocking
- FR9 says "The document never moves" and its first verification row fails "a document scroll" for a field in the page (
docs/specs/AST-099-mobile-keyboard/spec.mdlines 170 and 298), so under FR4 and FR2 a field in page flow, or in an app's sticky bottom bar, has no conforming way above the keyboard; please say how such a field is revealed and narrow the row's failure to a scroll the browser makes or one left in place. - The ride that moves an app-owned "nearest fixed-position ancestor" (lines 159-161, and
docs/families/input-fields.mdlines 261-263) contradictsarchitecture:react-component-runtimeINV11, which lets a component apply the keyboard height only through a StyleX style on its own element; please amend INV11 in this pull request to cover the ride and who puts the element back, or drop the app-owned case.
Smaller points: FR9 calls the chat dock a host that "already applies" the height while Gaps 9 and 11 say it does not, the app-owned container needs a bound so a full-viewport fixed app shell does not ride as a whole, "a small gap" needs a value, and Gap 7 could also cover PowerSearch's bottom-sheet presentation.
[Automated review]
| internal path, the keyboard module (`architecture:react-component-runtime` | ||
| INV11): the height comes from FR2, focus does not pan the page (FR4), and no | ||
| field measures the keyboard or listens to the visual viewport itself. The | ||
| document never moves; only the docked container does. A Core host that already |
There was a problem hiding this comment.
For a field in normal page flow, the document is the scroller that can reveal it: current FR4 has the surface bring the control into view "with its own scroller", and FR2 puts back a visual-viewport pan. With the document never moving, a TextInput low in a form (Gap 2), or a field in an app's sticky bottom bar (Gap 10), has no conforming way above the keyboard. Could FR9 say how such a field is revealed? For example: after a focus that does not scroll, its nearest scroller, the document included, brings it into view.
| | FR8 | Firefox for Android, emulator or device, top-level document | focus routes, keyboard shown and hidden, page panned and pinch-zoomed, a sheet moved | The page lurches, the field ends under the keyboard, or the height is wrong after a pan or zoom | | ||
| | FR2–FR5 | Safari on iOS in the iOS Simulator or on a device, top-level document | focus routes, keyboard shown and hidden, page panned and pinch-zoomed, a sheet moved, in Safari and as a home-screen app | The page lurches, the field ends under the keyboard, or the height is wrong after a pan or zoom | | ||
| | FR2–FR5, FR8 | Release gate: physical devices in a top-level document (DEC-3) | iPhone Safari with the toolbar expanded and collapsed; iPhone home-screen app; Firefox for Android (FR2–FR5); Android Chrome with Gboard and Samsung Keyboard at the phone's refresh rate | The page lurches, the field ends under the keyboard, or the surface does not follow the keyboard | | ||
| | FR9 | TBD (Gap 1–11): a focus test per covered field, `<Field>.keyboard.test.tsx` beside each one, driving one keyboard change through the keyboard module's test double | each covered field in the page, a Tall and a `hug` BottomSheet, ChatLayout's fixed and sticky dock, an app's fixed bottom bar, and a fixed Toolbar; focus by tap, Tab, and `focus()` | A focused field left under the keyboard, a document scroll, a second offset inside a host that already applies the height, or a field reading the visual viewport itself | |
There was a problem hiding this comment.
"a document scroll" fails every way to reveal a covered field "in the page". Suggest FR4's wording instead: a scroll the browser makes, or a slipped scroll left in place.
| container the app owns as well: the field moves its nearest fixed-position | ||
| ancestor. It is the one place Core moves layout it does not own, an exception |
There was a problem hiding this comment.
Moving the app's nearest fixed-position ancestor means styling an element the field does not render. architecture:react-component-runtime INV11 allows the keyboard height only "through a StyleX functional style on its own element", and this pull request leaves INV11 unchanged. Could INV11 be amended here to say how the keyboard module moves that element and puts it back on keyboard hide, blur and unmount, or the app-owned case be dropped?
| INV11): the height comes from FR2, focus does not pan the page (FR4), and no | ||
| field measures the keyboard or listens to the visual viewport itself. The | ||
| document never moves; only the docked container does. A Core host that already | ||
| applies the keyboard height, such as a fully expanded Tall BottomSheet or the |
There was a problem hiding this comment.
Non-blocking: Gaps 9 and 11 say ChatLayout's dock does not apply the keyboard height. Suggest "a Core host that applies the keyboard height under FR3", so the composer input never adds its own offset or rides while Gap 9 is open.
…e content scrolls into place
|
/approve-spec e6e335f |
There was a problem hiding this comment.
Requesting changes: the keyboard rule, the docked ride and their scope are owner-approved at this head, and the new nearest-scroller reveal settles the earlier document-scroll conflict, but one requirement still contradicts a current record.
Blocking
- The app-owned ride (
docs/specs/AST-099-mobile-keyboard/spec.mdlines 159–162, and FR13 indocs/families/input-fields.md) contradictsarchitecture:react-component-runtimeINV11, which allows the keyboard height only through a StyleX style on a component's own element; amend INV11 in this pull request to cover writing and removing the translate on an app element, or drop the app-owned case from FR9, FR13 and DEC-5.
Smaller wording notes are optional: name the own-element offset that gives a field near a scroller's end its room, reconcile the chat dock's "already applies" with Gaps 9 and 11, give the gap a size, say how the ride works on iOS, scope the FR4 row, and update Gap 7, the stale current-state line and the path list.
[Automated review]
| ride (DEC-2), keeping a small gap above the keyboard. This holds for a docked | ||
| container the app owns as well: the field moves its nearest fixed-position | ||
| ancestor that is docked to the viewport's bottom edge (its bottom sits at the | ||
| viewport's bottom); a fixed container that fills the viewport is never moved. | ||
| It is the one place Core moves layout it does not own, an exception to FR3 the |
There was a problem hiding this comment.
Blocking: moving an app-owned ancestor contradicts architecture:react-component-runtime INV11 ("each consumer applies them through a StyleX functional style on its own element"), which this pull request leaves unchanged and which FR3 and FR9 still defer to (lines 97–98, 168–170). Amend INV11 here to say how the keyboard module writes and removes the translate on an element the field does not render (INV5), or drop the app-owned case from FR9, FR13 and DEC-5.
|
/approve-spec 06f6ec0 |
There was a problem hiding this comment.
Requesting changes: the keyboard rule, the docked ride, its narrowed scope and the INV11 amendment are owner-approved at this head, and the earlier promise-versus-exclusion, eased-lift and FR3 problems are fixed, but three record defects remain. Line numbers are in docs/specs/AST-099-mobile-keyboard/spec.md unless another file is named.
Blocking
- A bar the app lifts from script each frame (a JavaScript spring or tween) runs no CSS transition or animation, so at Core's one measurement it looks like a bar that stays put or one lifted part of the way and Core adds the rest, lifting it twice against "never lifted twice" (lines 190–192 and 457) and "behaves as before" (lines 257–258); please either narrow those promises to CSS transitions and animations and say what a script-moved bar gets, or have Core leave a bar whose drawn position is still changing, and add that state to the ride row (line 359).
- Three verification rows fail what the narrowed text now allows: the ride row (line 359) and INV11's row (
docs/architecture/react-component-runtime.mdline 508) fail a move left after the field blurs although FR9 keeps it until the keyboard hides after focus leaves (lines 195–197), INV11's row fails the docked bar's extra layout read that INV11 allows (lines 193–194), and FR13's row (docs/families/input-fields.mdline 441) fails a field left under the keyboard in any fixed host although FR13 leaves floating and own-translatehosts to the app, so please align all three with the text. - Gap 7 (lines 313–315) calls PowerSearch's sheet flow its "experimental touch presentation", but on
mainthe PowerSearch record's DEC-3 makes it a stable presentation and theadaptivedefault on compact coarse-pointer screens, so please rebase and describe it as the default sheet flow, with the query input reached there only throughpresentation="popover".
One non-blocking note, on the eligibility tests reading Core's own translate, is inline.
[Automated review]
| lifts part of the way moves the rest, and a bar that stays put moves by the | ||
| full overlap. Core does not move a bar the app is already moving (a running | ||
| transition or animation on its position at the time Core measures); it | ||
| leaves that bar to the app, so it is never lifted twice. The move adds to |
There was a problem hiding this comment.
A lift the app drives from script each frame (a JavaScript spring or tween) runs no CSS transition or animation, so at Core's one measurement the bar looks like one that stays put or one lifted part of the way, and Core adds the rest: once the app's motion finishes, the bar is lifted twice. Either narrow "never lifted twice" (here, DEC-5 line 457 and the impact bullet at lines 257–258) to CSS transitions and animations and say what a script-moved bar gets, or have Core leave a bar whose drawn position is still changing when it measures; then add that state to the ride row (line 359).
| - **Gap 7 — PowerSearch.** Its query input (Gap 4) and the text and number | ||
| fields of its value editor, which open in a popover, have no keyboard | ||
| handling. Keeping the popover itself above the keyboard is `spec:AST-059`'s. | ||
| In its experimental touch presentation the filter work sits in Tall bottom |
There was a problem hiding this comment.
On main, the PowerSearch record's DEC-3 makes this sheet flow a stable presentation and the adaptive default on compact coarse-pointer screens, so it is no longer experimental, and on a phone the query input does not render unless presentation="popover" is set. Suggest describing the sheet flow as PowerSearch's default there.
| | FR9 | TBD (Gap 1–11): a focus test per covered field, driving one keyboard change | each covered field in the page, a Tall and a `hug` BottomSheet, ChatLayout's fixed and sticky dock, an app's fixed bar docked to the bottom edge with no `translate` of its own, and a fixed Toolbar docked there; focus by tap, Tab, and `focus()`; a field in scrolling content in the page and in a nested scroller; a field near its scroller's end | A focused page field or Core-host field left under the keyboard or less than 10 CSS px above it, a field in an app's docked bar whose bar's bottom does not meet the keyboard's top, scroll range left at the scroller's end after the keyboard hides, a page pan or translate, a reveal later than the frame the keyboard is reported in, or a second offset inside a host that already applies the height | | ||
| | FR9 | TBD (Gap 1): a source boundary check over the covered fields | every covered field's props type and source | A public prop, option, or context for the behavior | | ||
| | 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 11): a ride test per covered field, plus physical-device evidence under DEC-3 | each covered field in a fixed bottom bar, a sheet, an app-owned bar whose layout box sits at the viewport's bottom edge; an app bar the app lifts to the keyboard's top on the same visual-viewport resize, with its listener running before and after Core's; an app bar the app lifts part of the way with its own transform; an app bar the app lifts behind a CSS transition or animation still running when Core measures; an app bar centred with its own `translate`; an app bar floating above the bottom edge; an app-owned fixed container that fills the viewport; keyboard show and hide; the field blurs; the field unmounts; reduced motion on | The docked container stays put, lags or jumps after the keyboard settles, a Core host or content field less than 10 CSS px above the keyboard, an app bar left under the keyboard or above its top edge once the keyboard settles, a move left on an app element after the keyboard hides, the field blurs, or it unmounts, any other change on an app element, a bar the app lifts moved a second time, a bar the app is still moving moved by Core, a floating bar moved, the app's transform replaced, a bar with its own `translate` moved or its `translate` changed, the page is panned or translated, a fixed container that fills the viewport, or whose layout box is not at the bottom edge, moves, or a Core host that already rides is moved twice | |
There was a problem hiding this comment.
FR9 now keeps the move until the keyboard hides after the field loses focus (lines 195–197), so "a move left on an app element after the keyboard hides, the field blurs, or it unmounts" fails a conforming implementation when focus moves to another field with the keyboard still up. Suggest: "a move left after the keyboard hides once focus has left the field, or after the field unmounts".
| | INV9 | Provider/external-store identity and render-count tests | Unchanged semantic payload changes identity, or snapshot identity creates a render loop | | ||
| | INV10 | Shared test over every adopter of the announcement module | An adopter holding its own Effect, observer, timer, or listener for its own-content announcement | | ||
| | INV11 | Source boundary check plus hook tests across StrictMode replay and unmount | A second keyboard listener or measurement in Core; a consumer reading a CSS custom property on the root or a shared ancestor for the height; an export of the hook; a component outside Core measuring by a rule other than AST-099 FR2, or riding the keyboard; a listener left after unmount; a Core rider driven by JavaScript per frame or by its own transition; or more than one layout read per keyboard change | | ||
| | INV11 | Source boundary check plus hook tests across StrictMode replay and unmount | A second keyboard listener or measurement in Core; a consumer reading a CSS custom property on the root or a shared ancestor for the height; an export of the hook; a component outside Core measuring by a rule other than AST-099 FR2, or riding the keyboard; a listener left after unmount; a translate left on an app element after the keyboard hides, the field blurs, or it unmounts; any other write on an app element; a translate on a container whose layout box is not at the bottom edge, that fills the viewport, or whose own `translate` is not `none`; a translate on a container whose position the app is animating when the module measures; a translate measured before the page's own keyboard listeners run; a covered field deciding its keyboard behavior in an Effect that reacts to focus; a Core rider driven by JavaScript per frame or by its own transition; or more than one layout read per keyboard change | |
There was a problem hiding this comment.
Two failures here lag INV11's text: "a translate left on an app element after the keyboard hides, the field blurs, or it unmounts" (INV11 now removes it once the keyboard hides after focus leaves the field, lines 199–200), and "more than one layout read per keyboard change" (INV11 now exempts the docked bar's read, lines 193–194).
| gets none, and rides the hook's one transition to that value like every Core | ||
| rider. The read of that bar is the one exception to the hook's single layout | ||
| read per keyboard change, stated below. The `translate` property composes with | ||
| a `transform` the app sets. The module writes nothing on an ancestor whose own |
There was a problem hiding this comment.
Non-blocking: after Core's first move the bar's computed translate is Core's own and Core's ride runs on it, so read literally these tests (here and lines 186–188, FR9 spec.md lines 193–194) stop Core from updating the bar on a later keyboard report, such as a taller emoji or numeric keyboard with focus still in the bar. Consider saying they read only what the app sets, and adding a keyboard-height change with the field still focused to the ride row.
| | FR10 | A type test over every Field-composed component's props that fails if one accepts root `xstyle`, `className`, or `style`; per-component render tests for every base prop, `container*`, and `control*` prop and for both development warnings | every Field-composed component standalone, in horizontal-label FormLayout, and inside InputGroup where admitted | A component type-checks with root styling, a base prop lands off the primary semantic control, `container*` lands off the Field box or on the control box, `control*` lands off the control box, or a warning is missing | | ||
| | FR11 | `InputClearButton.test.tsx` tooltip and accessible-name assertions, plus real-Chromium hover captures | a long field label; a locale override; light and dark | The tooltip repeats the field label, or the accessible name loses the field label | | ||
| | FR12 | `Typeahead.test.tsx`, `BaseTypeahead.test.tsx`, `Tokenizer.test.tsx`, `Selector.test.tsx`, and `MultiSelector.test.tsx` cases for both attributes, with Enter keeping its member-owned effect; `SearchKeyboard.interaction.browser.spec.ts` on a touch-emulated phone, where the search input a tap focuses carries both | Typeahead; Tokenizer; Selector and MultiSelector search row; a phone viewport with touch emulation | A missing attribute, or an Enter whose effect changes with the label, fails | | ||
| | FR13 | TBD (gap): the per-field keyboard focus tests and simulator evidence `spec:AST-099` FR9 names | each member FR13 covers, in the page, a sheet, and a fixed host | A focused field left under the keyboard, or a public prop for the behavior | |
There was a problem hiding this comment.
FR13 now leaves a member in a floating or own-translate fixed container unmoved (lines 263–265), so "a fixed host" here fails a conforming implementation; suggest "a fixed bar spec:AST-099 FR9 moves".
…uts-work-with-the-keyboard
…te; browser reveal for fields Core does not move; PowerSearch sheet default
|
/approve-spec 4a6f8c8 |
There was a problem hiding this comment.
Requesting changes. The keyboard rule, the docked ride, its scope and the INV11 amendment are owner-approved at this head, and the earlier script-lift, stale-row and Gap 7 problems are fixed. Two record defects need fixing before these records can guide implementation.
Blocking
- The FR9 ride row (
spec.md:369) fails a floating or own-translatebar left under the keyboard, and any page pan, although FR9 leaves those bars to the app and now lets Safari's own reveal pan the page for their fields (spec.md:169-171); also, "a field Core does not move" (:169-170,:366,:457-458) reads as covering page fields that Core reveals by scrolling, so name the fixed containers FR9 leaves to the app and give the ride row the exemption FR13's row already has. - DEC-5 (
spec.md:467-469) and the impact bullet (:262-265) now say Core leaves any bar the app moves with a CSS transition, which drops FR9's "running when Core measures" condition, so a bar whose CSS lift starts after the app's listener returns is lifted twice; restore the condition and say that a later CSS lift can be lifted twice.
Also worth fixing: INV11's "no CSS transition or animation on its position is running" (react-component-runtime.md:187-188) should limit this to the app's motion, as its row and FR9 do, so that Core's own ride does not block its next update.
[Automated review]
| | FR9 | TBD (Gap 1–11): a focus test per covered field, driving one keyboard change | each covered field in the page, a Tall and a `hug` BottomSheet, ChatLayout's fixed and sticky dock, an app's fixed bar docked to the bottom edge with no `translate` of its own, and a fixed Toolbar docked there; focus by tap, Tab, and `focus()`; a field in scrolling content in the page and in a nested scroller; a field near its scroller's end | A focused page field or Core-host field left under the keyboard or less than 10 CSS px above it (a field in a container Core does not move keeps the browser's own reveal and is not a failure), a field in an app's docked bar whose bar's bottom does not meet the keyboard's top, scroll range left at the scroller's end after the keyboard hides, a page pan or translate, a reveal later than the frame the keyboard is reported in, or a second offset inside a host that already applies the height | | ||
| | FR9 | TBD (Gap 1): a source boundary check over the covered fields | every covered field's props type and source | A public prop, option, or context for the behavior | | ||
| | 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 11): a ride test per covered field, plus physical-device evidence under DEC-3 | each covered field in a fixed bottom bar, a sheet, an app-owned bar whose layout box sits at the viewport's bottom edge; an app bar the app lifts to the keyboard's top on the same visual-viewport resize, with its listener running before and after Core's; an app bar the app lifts part of the way with its own transform; an app bar the app lifts behind a CSS transition or animation still running when Core measures; an app bar the app lifts from script with its own `translate`; a keyboard that changes height while focus stays in a docked bar; an app bar centred with its own `translate`; an app bar floating above the bottom edge; an app-owned fixed container that fills the viewport; keyboard show and hide; the field blurs; the field unmounts; reduced motion on | The docked container stays put, lags or jumps after the keyboard settles, a Core host or content field less than 10 CSS px above the keyboard, an app bar left under the keyboard or above its top edge once the keyboard settles, a move left on an app element after the keyboard hides once focus has left the field, or after the field unmounts, any other change on an app element, a bar the app lifts moved a second time, a bar the app is moving with CSS motion moved by Core, a bar with its own `translate` moved, a docked bar not updated when the keyboard changes height, a floating bar moved, the app's transform replaced, a bar with its own `translate` moved or its `translate` changed, the page is panned or translated, a fixed container that fills the viewport, or whose layout box is not at the bottom edge, moves, or a Core host that already rides is moved twice | |
There was a problem hiding this comment.
This row lists a floating bar and a bar with its own translate as states, but it fails "an app bar left under the keyboard" and "the page is panned or translated" with no exception. FR9 leaves those bars to the app (:271-273) and lets Safari's own reveal pan the page for their fields (:169-171). Add the exemption that FR13's row has: such a bar left under the keyboard, or the browser's reveal panning the page for a field in it, is not a failure.
| layout box, before any transform, sits at the viewport's bottom edge, and it | ||
| moves by the distance from its bottom as drawn, after the app's own lift, to | ||
| the keyboard's top, so a bar the app already lifts is not lifted twice and no | ||
| opt-out is needed. Core leaves a bar the app moves with a CSS transition or |
There was a problem hiding this comment.
FR9 leaves a bar alone only while its CSS transition or animation is "running on its position when Core measures" (:193-195). This sentence, and the impact bullet at :262-265, drop that condition. A bar whose CSS lift starts after the app's listener returns is at rest when Core measures, so Core moves it, and then the app lifts it again. Put the condition back, and say that a later CSS lift can be lifted twice.
…running when Core measures; app-own motion in INV11
|
/approve-spec 6b80579 |
There was a problem hiding this comment.
Requesting changes. The keyboard rule, the docked ride, its scope and the INV11 amendment are owner-approved at this head, and the earlier ride-row, browser-reveal and measuring-condition problems are fixed. Two record contradictions remain in how FR9 treats a field inside an app's tall fixed container.
Blocking
- FR9 now gives a field inside a full-viewport fixed container the browser's own reveal and both FR9 rows pass it left under the keyboard (
spec.md:169-172,:368,:371, DEC-5:459-462), while FR9's scrolling-content rule (:173-177) has that field's nearest scroller reveal it, so an app whose page scrolls inside aposition: fixed; inset: 0shell gets two conflicting rules; limit the reveal clause to a field with no scroller between it and the container, or treat a field in a scroller inside a full-viewport container as scrolling content. - The docked test (
spec.md:185-187, DEC-5:466-467, INV11react-component-runtime.md:184-186) has no height bound, so a full-height side drawer or a pane below a fixed header moves up by the whole keyboard height and a field near its top leaves the screen, against FR9's "stays visible above it" (:159) and "Nothing else moves" (:177); let a container move only when its drawn box stays inside the room above the keyboard, send a field in a taller container to the scrolling-content rule, and add those layouts to the FR9 ride row and INV11's row.
[Automated review]
| is never panned or translated for a field Core reveals or moves. A field | ||
| inside a fixed container that FR9 leaves to the app (one floating above the | ||
| bottom edge, one that sets its own `translate`, or a full-viewport one) keeps | ||
| the browser's own focus reveal, which in Safari may pan the page. |
There was a problem hiding this comment.
This list now includes "a full-viewport one", so a field in the scroller of an app's position: fixed; inset: 0 shell keeps the browser's reveal and passes both FR9 rows left under the keyboard (:368, :371). The Scrolling content rule below (:173-177) has that field's nearest scroller reveal it instead. Suggest limiting this clause to a field with no scroller between it and the container, here, in DEC-5 (:459-462) and in both rows.
| - **App-owned docked containers.** A container the app owns rides too when it | ||
| is fixed-position, does not fill the viewport, and is docked: its layout | ||
| box, before any transform, sits at the viewport's bottom edge. It moves up |
There was a problem hiding this comment.
Nothing bounds the container's height, so a full-height side drawer (top: 0; bottom: 0; width: 85%) or a pane below a fixed header (top: 56px; bottom: 0) is docked and moves up by the whole keyboard height. With a 300 px keyboard, a search field 16 px from the drawer's top ends 284 px above the viewport, against "stays visible above it" (:159) and "Nothing else moves" (:177). Suggest moving a container only when its drawn box stays inside the room above the keyboard, sending a field in a taller container to the Scrolling content rule, and adding both layouts to the ride row (:371) and INV11's row.
…d; too-tall docked containers move only as far as the field needs
|
/approve-spec 5ed56a2 |
There was a problem hiding this comment.
Approving. This records-only change writes the on-screen-keyboard rule for Core fields (spec:AST-099 FR9 and DEC-5), amends architecture:react-component-runtime INV11 to admit the one translate Core writes on an app's docked bottom bar, and points family:input-fields FR13 at FR9. The keyboard rule, the docked ride, its scope and the INV11 amendment are owner-approved at this head, and the earlier full-viewport-reveal and too-tall-container contradictions are now fixed: a field with a scroller inside an app-left fixed container follows the scrolling-content rule, and a too-tall docked container moves only as far as the field needs (or is revealed by its scroller) instead of by the whole keyboard height.
Two small, non-blocking wording nits you may want to tighten in a follow-up:
- FR3's exception sentence states the ride as "the distance from the bar's drawn bottom to the keyboard's top"; that is the fitting case, so it reads as slightly more than a too-tall container actually moves (FR9 has the exact rule).
- FR13's prose lists the app-left containers as floating or own-
translateand omits the full-viewport case that its own verification row and FR9 include.
Nothing ships until the implementation PRs follow against FR9.
[Automated review]
|
/approve-spec 29d1927 |
There was a problem hiding this comment.
Approving. This records-only change writes the on-screen-keyboard rule for Core fields (spec:AST-099 FR9 and DEC-5), amends architecture:react-component-runtime INV11 to admit the one translate Core writes on an app's docked bottom bar, and points family:input-fields FR13 at FR9. The keyboard rule, the docked ride, its scope and the INV11 amendment are owner-approved at this head, and the earlier full-viewport-reveal and too-tall-container contradictions are fixed: a field with a scroller inside an app-left fixed container follows the scrolling-content rule, and a too-tall docked container moves only as far as the field needs (or is revealed by its scroller) instead of by the whole keyboard height.
Two small, non-blocking wording nits you may want to tighten in a follow-up:
- FR3's exception sentence states the ride as "the distance from the bar's drawn bottom to the keyboard's top"; that is the fitting case, so it reads as slightly more than a too-tall container actually moves (FR9 has the exact rule).
- FR13's prose lists the app-left containers as floating or own-
translateand omits the full-viewport case that its own verification row and FR9 include.
Nothing ships until the implementation PRs follow against FR9.
[Automated review]
On a phone, a focused field never hides behind the keyboard in the page, in a Core sheet or dock, in a scroller, or in an app's bar docked to the bottom edge. That bar moves up and back with the keyboard.
Exact decision needed
None open. On 2026-11-03 the owner decided:
This PR records these decisions and amends
architecture:react-component-runtimeINV11 to admit the one write on an app element. The branch includes currentmain(PowerSearch's DEC-3).Existing authority searched
spec:AST-099(on-screen keyboard): owns the keyboard height, focus without page pan, dismissal, and the ride (FR8, DEC-2), but only for keyboard-aware surfaces.spec:AST-059FR9: the 10 px keyboard clearance for layers, reused here for page fields and Core hosts.architecture:react-component-runtimeINV2 and INV11. INV11 is amended here and holds every mechanism.architecture:knowledge-contractsINV18: product records state observable behavior only.family:input-fields: no keyboard-position rule. FR12 covers keyboard hints only.component:PowerSearchDEC-3: the sheet flow is theadaptivedefault on compact coarse-pointer screens.main: each covered field and host listed below,BottomSheet/useMobileKeyboard.ts, and the absence ofpackages/core/src/keyboard/.Decision
spec:AST-099FR9 and DEC-5, in observable terms. When the on-screen keyboard opens, a focused Core text-entry field stays visible above it, and the app does nothing, in two cases:translateof its own.translate, which Core never moves.transform. It is gone once the keyboard hides after the field loses focus, or once the field unmounts. Core changes nothing else on app elements.FR9 leaves some fixed containers to the app: one floating above the bottom edge, one with its own
translate, or a full-viewport one.translatedon't ride yet; usetransformor no translate.No prop, option, context, or other public API. Mechanism (the keyboard module, its
offsetconstant, thetranslatewrite, the too-tall and measuring rules, the own-element scroll-range offset, the focus-handler start) lives only in INV11.Covered fields, each checked on
main: TextInput, TextArea, NumberInput; the typed fields of DateInput, DateTimeInput, and TimeInput; the Typeahead and Tokenizer inputs (BaseTypeahead) and Tokenizer's sheet search field; the Selector and MultiSelector search row; PowerSearch's query input, popover value-editor fields, and its sheet flow's search and value fields; CommandPalette's search input; ChatComposer's input. Outside the rule (no keyboard): read-only touch picker fields, native date/time pickers, DateRangeInput's trigger, FileInput, Slider, and Selector and ComplexSelector triggers.Rejected alternatives
Claim scope and non-goals
spec:AST-059.translate.Changed records
docs/specs/AST-099-mobile-keyboard/spec.md:translatebars and floating bars do not ride; a field a scroller holds in them is still revealed. Too-tall docked containers move only as far as a scroller-less field needs.affects_families, and path applicability. Both FR9 test rows fail a scroller-held field left under the keyboard; the ride row adds a full-height drawer with a scroller (no move), and a tall pane with a field near its top (no move) or bottom (just enough).docs/architecture/react-component-runtime.md: INV11 admits atranslateon an app's docked fixed container under these conditions:translateisnone;The translate equals the drawn distance to the keyboard's top, measured after each keyboard report and focus move, after the page's listeners. It rides the hook's one transition and is removed once the keyboard hides after focus leaves, or on unmount. When the moved box would not fit, it writes no translate if a scroller holds the field, otherwise only as far as the field needs. Its verification row adds the too-tall layouts.
spec:AST-099/DEC-5is added to its deciding specs.docs/families/input-fields.md: FR13 narrowed the same way, citingspec:AST-099FR9. Its row fails a field under the keyboard in any scroller, a Core host, or a docked bar Core moves. Also an implementation-gap bullet andspec:AST-099/DEC-5indeciding_specs.Gap list (implementation gaps on
main)No covered field meets FR9 today. 11 gaps, all open:
packages/core/src/keyboard/does not exist. Only BottomSheet's hook handles the keyboard. It acts only in a fully expanded Tall sheet and gates focus on an iOS user agent.hugsheet, which the hook does not cover.presentation="popover", and have no handling.Follow-up (not a gap, outside FR9): riding floating bars and composing with an app's own
translate.Evidence and implementation relationship
Spec only. Evidence is current source on
main. Implementation follows in separate PRs against FR9, starting with the keyboard module.Writing rubric results
Rejected:line.Scope
Validation
pnpm check:knowledge: "Knowledge templates and records are aligned."git diff --check: clean