Skip to content

Console at phone width: Forms flow audit and fixes at 375 and 390 px #29072

Closed

Description

@objectstack-fleet

domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

Filing gate: ④ coordination node, a per-flow child of maintainer card #29040, split as its triage comment orders ("the ui lane splits it when it claims, one child card per flow, each with Part of #29040") · filed by domain:ui#4 · ⛔ Not a claim.

Part of #29040.

Business reason (from #29040). objectstack-ai/mobile 2.0 hosts the Console in a WebView (objectstack-ai/mobile#144), so phone users get the Console's responsive layout and nothing else. This child covers the Forms flow: the create / edit form, lookups, date and select fields, and validation errors.

Ask. Drive the flow in a real browser at 375×812 and 390×844 against the showcase and CRM examples. Record what breaks and fix it. Check each of these:

  • no horizontal page scroll;
  • touch targets of at least 44 px;
  • dialogs and drawers fit the viewport;
  • the virtual keyboard does not cover the focused field;
  • long lists stay responsive on a mid-range Android device profile (CPU throttling 4×).

Acceptance.

Reader. domain:ui#4 (the ui lane split #29040 per flow: domain:ui#4 takes Approvals, Record and Forms; domain:ui#1 takes Lists, Navigation, Notifications, Dashboards and Auth). Size M.

Dedupe. Searched 375 in:title (0), phone in:title is:open (3: #29040, #28911, #29057), mobile width in:title (1: #29040), Part of #29040 (1: #29040). No per-flow child existed.

Links back: #29040, objectstack-ai/mobile#144.


Generated by Claude Code

Activity

  1. added
    enhancementNew feature or request
    pm:queueReady for the PM dispatch loop
    domain:uiDomain lane — seat card indexed by label:pm:seat
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    on Nov 2, 2026
  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

    Claim: PM loop round 11 · 2026-11-02T08:00Z
    Session: session_01QsR2jUqXiWNJKjMJYThRr1 (card thread, os-dev); dispatched by seat session session_01QsR2jR7ZFkpqmm4U8S1f9y
    Account: zhuangjianguo
    Branch: claude/project-thread-bi35xl (platform-assigned to the card thread; it can push only this branch)
    Worktree: objectstack-issue-29072
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: the create / edit form surfaces (form layout, lookup, date and select controls, validation-error display) in ui/apps/console/src/ and ui/packages/*/src/ that the fixes need, an optional phone-viewport spec under ui/e2e/, their tests, and a changeset; not the files #29019, #29021, #29078, #29107, #28915 and #29039 hold (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: default
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the mobile app now hosts the Console in a WebView, so a phone user fills in create and edit forms at 375 px and nothing else, and nobody has verified the keyboard, pickers and validation there | forms are where phone users enter data | every user of the mobile app
    Thread-read: none
    Premise: holds (at origin/main, no phone-viewport spec covers the create / edit form)
    Serial constraints cleared: #29019 and #29021 build beside it on other files; sheet.tsx is #29078's and dialog.tsx is #29107's


    Generated by Claude Code

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · zhuangjianguo · session_01QsR2jUqXiWNJKjMJYThRr1
    os-dev-report

    Draft PR up as of 2026-11-02T09:34Z; re-runs and landing are the seat's.

    {
      "issue": 29072,
      "status": "done",
      "branch": "claude/project-thread-bi35xl",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/29209",
      "head": "6034e7ec20a8e534f2919b3810f16d375afb46df",
      "session": "session_01QsR2jUqXiWNJKjMJYThRr1",
      "premise_still_valid": true,
      "measured": "Run first at merge base a4015ab4, Chromium (Playwright, isMobile, hasTouch, DPR 2) at 375x812 and 390x844, Console from source over examples/app-crm and examples/app-showcase (objectstack dev --seed-admin --fresh): CRM lead and opportunity, showcase task; list, phone create button, create form, select, date picker, lookup dropdown, browse-all record picker, keyboard check, validation submit, seeded record, Edit form. Before: only check 2 (touch targets) failed, at both widths on all three objects: inputs, select and lookup triggers 40 px, select options 32, calendar days 32, date buttons 28, lookup search 32, quick-create 36, picker search 40, Filters 36, picker Close 16x16, line-item cells 32, Add line 36, percent slider 8. Checks 1 (no horizontal scroll), 3 (fit), 4 (keyboard, adjustResize model) and validation (first error focused, in view) passed before and after. After at 6034e7ec's widget sources: every form control at least 44 px; the only check-2 failure left is the picker's DialogContent Close at 16x16 (dialog.tsx, #29107). Check 5 on the production bundle (pnpm console:build, served at /_console, 303 accounts, 4x CPU via CDP): before, Account lookup open max Event Timing 176 ms (375) and 192 ms (390), typing 64 and 72, scroll no long task; after, open 184 to 256 ms over 3 runs (2 of 6 samples over 200), typing at most 88, scroll tasks at most 51 ms. The diff adds classes only, so the open cost predates it: finding. Main merged at dedc3505 (26 commits, none touching fields, plugin-form or the calendar/input/button primitives).",
      "summary": "The create / edit form's registered field widgets in @object-ui/fields now carry the same phone touch floor the form renderer's built-in controls already use, min-h-[44px] sm:min-h-0, through a package-internal module widgets/touchTarget.ts (not re-exported). Covered: text, email, phone, URL, number, currency, percent inputs and the percent slider's hit area; select trigger and options; lookup trigger, clear, browse-all, dropdown search, show-all and quick-create; record picker search and Filters; date picker input (right padding pr-24 below sm so text stops short of the two 44 px buttons), clear and calendar buttons, Today/Tomorrow, and a 44 px calendar cell below sm ([--cell-size:2.75rem] sm:[--cell-size:2rem] handed to the Calendar primitive; seven days plus padding measure 332 px inside the 343 px column at 375); line-item grid cells, options, row buttons and Add line. plugin-form: the modal, drawer and master-detail form footers' Cancel/Reset/Submit. From sm up every control keeps its previous height. No primitive in components/src/ui is edited; FormPage.tsx is not touched.",
      "tests": "New: ui/packages/fields/src/widgets/touchTarget.formControls-29072.test.tsx, 13 cases pass; reverse-verified, 12 of 13 red with the widget sources restored from the merge base (the token-shape case reads only the new module). New: ui/e2e/form-controls-phone-touch.spec.ts, 3 cases pass (real Tailwind compiled for the classes read from source; 375 and 390 px controls and seven calendar days at least 44 px, calendar ends inside the column, no page scroll; 1024 px keeps 40/28/40/32); red on the merge base. Regression: the touched widgets' and form footers' existing test files, 106 files / 768 tests pass, before and after the merge of main. Type-check: turbo type-check for @object-ui/fields and @object-ui/plugin-form under os-verify-lock, 49 tasks, VERDICT command-exit 0. ESLint on the changed ts/tsx: 0 errors.",
      "gates": "dispatch-gates --commands derived 34; all 34 run, each exit 0 (check:type-check-debt under os-verify-lock). pnpm --dir ui exec vitest run scripts/ was SIGKILLed once in the sweep while the five run-record servers held about 6 GB; rerun alone under os-verify-lock it passed, 169 files, 4900 tests, VERDICT command-exit 0, and the reconciliation records that exit. --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. check-sdui-manifest 0 after the merge. PR-body gates on the exact body sent (GITHUB_EVENT_PATH): check-changeset-no-major --base origin/main 0, check-adr-0087-registration --base origin/main 0, check-closing-keyword-parity 0. CI check-runs on #29209: not read at report time.",
      "dogfood_battery": "n/a: no ! in the title and no (narrowing) arm",
      "line_budget": "19 files, +383 / -35 = 418 changed lines, under the 3000-line threshold",
      "files_changed": [
        ".changeset/29072-form-controls-phone-touch.md",
        "ui/e2e/form-controls-phone-touch.spec.ts",
        "ui/packages/fields/src/widgets/{CurrencyField,EmailField,GridField,LookupField,NumberField,PercentField,PhoneField,RecordPickerDialog,SelectField,TextField,UrlField}.tsx",
        "ui/packages/fields/src/widgets/date-picker/DatePickerField.tsx",
        "ui/packages/fields/src/widgets/touchTarget.ts",
        "ui/packages/fields/src/widgets/touchTarget.formControls-29072.test.tsx",
        "ui/packages/plugin-form/src/{DrawerForm,MasterDetailForm,ModalForm}.tsx"
      ],
      "deviations": [
        "The run record was driven by a Playwright harness against the Console served from source by Vite (proxying objectstack dev), not the production bundle; check 5 alone was read on the production bundle, because the dev server's unbundled React reads 2 to 3 times slower.",
        "Check 4 (virtual keyboard) uses the WebView adjustResize model: focus, shrink the layout viewport by 336 px, let the browser scroll the editable into view, assert its box and elementFromPoint. No real device IME was exercised.",
        "No object in either example declares the page or drawer form mode, so RecordFormPage was not reached; the drawer form footer gets the token for parity, unmeasured in the browser.",
        "A Vite watcher served a stale transform after the before-run source restore; restarted with --force and confirmed the served module carries the token before the after-run.",
        "Calendar cell size is set through the Calendar primitive's --cell-size variable by className; tailwind-merge keeps the later value, which the e2e fixture mimics.",
        "Brief rule over session context: the PR has no assignee and no review request, and no GitHub MCP write was made."
      ],
      "mcp_calls": "2: GitHub MCP issue_read get on #29072 x1 and get_comments on #29040 x1, both reads. No GitHub MCP write. Separately (claude-code-remote, not GitHub MCP): get_session x3, read_documentation x4.",
      "api_writes": "2: relay repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches x2 (pr_create #29209, run 41421493892, read back 11014 bytes identical; this os-dev-report comment via post-stamped). git push x2 is not a REST write.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: CRM lead / opportunity Account lookup and showcase task Project lookup, browse-all record picker, its Close button measures 16x16 px at 375x812 and 390x844 (target 44) · evidence: the picker renders the plain DialogContent, whose built-in close is the primitive's own; dialog.tsx is #29107's file · carrier: #29107 · dedupe words: record picker close button, DialogContent close 16px, lookup browse-all dialog",
        "class: b · reach: CRM opportunity create, open the Account lookup dropdown over 303 accounts at 4x CPU on the production bundle: one 150 to 215 ms task; click Event Timing 176 ms (375) and 192 ms (390) before this PR, 184 to 256 ms over three runs after (2 of 6 samples over 200 ms); typing at most 88 ms, scroll tasks at most 51 ms · evidence: this PR adds classes only, so the open cost predates it · dedupe words: lookup dropdown open slow, LookupField 4x CPU, Event Timing over 200 ms",
        "observation · reach: CRM opportunity create (MasterDetailForm) at 375x812: the Cancel / Create bar sits at the end of the scrolling body, not in a sticky footer like the modal form's, so Create is not on the first screen · none of the five checks covers it · dedupe words: master-detail footer not sticky, opportunity create button below fold"
      ]
    }
  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

    Claim amended · 2026-11-02T09:39Z · the file surface widens by one file. Branch, session and declaration lines are unchanged.

    Clause-②: no
    Metadata-protocol: none

    File surface (added): ui/packages/fields/src/widgets/FileField.tsx. It is the Forms flow's file field, which #29071's phone run measured at 36 px ("Take photo"). Two class additions use this PR's internal widgets/touchTarget.ts token: PHONE_TOUCH_HEIGHT on the camera button and PHONE_TOUCH_SQUARE on the 24×24 per-file remove button. They come with a pin in the touch-target test. No export, prop or behaviour changes.


    Generated by Claude Code

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

    Seat review · 2026-11-02T10:12Z · PR #29209 at 99a5359b: ACCEPT (on the PR; Clause-②: no, no contract review owed). It goes to the merge queue now.


    Generated by Claude Code

  7. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

    Landed · 2026-11-02T11:29Z · PR #29209 merged through the merge queue as 39951d2a. Record: ACCEPT 6574540165 at 99a5359b (Clause-②: no, no contract review owed).

    probe before (39951d2a~1) now (origin/main)
    PHONE_TOUCH_SQUARE = in ui/packages/fields/src/widgets/touchTarget.ts 0 1
    [--cell-size:2.75rem] sm:[--cell-size:2rem] in ui/packages/fields/src/widgets/date-picker/DatePickerField.tsx (the 44 px calendar day on a phone) 0 1
    PHONE_TOUCH_SQUARE) in ui/packages/fields/src/widgets/FileField.tsx (the per-file remove button) 0 1

    The card closed through Fixes. pm:dispatched comes off with this note. The record picker's 16 px Close stays with #29107 (dialog.tsx). Tracker #29040 keeps its remaining children.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:uiDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions