Repository navigation
fix(plugin-form): keep a 1rem viewport gutter on lg/xl ModalForm dialogs from sm up (objectui#12593) - #12598
Conversation
…ogs from sm up An lg (672px) or xl (1024px) dialog took the primitive's w-full, so on any window between sm and its max width it was as wide as the window, at x = 0. Both bounded wide sizes now also read one shared class, sm:w-[calc(100%-2rem)], under their own max-w. sm, default and full, and the phone sheet below sm, are unchanged. Claude-Session: https://claude.ai/code/session_01JZnppgHRp4sREcouhkQYDB Co-authored-by: Claude <noreply@anthropic.com>
…zes (objectui#12593) Claude-Session: https://claude.ai/code/session_01JZnppgHRp4sREcouhkQYDB Co-authored-by: Claude <noreply@anthropic.com>
…bjectui#12593) Also words the 100vw note exactly: each gutter would lose half the scrollbar's width, as measured in Chromium with classic scrollbars. Claude-Session: https://claude.ai/code/session_01JZnppgHRp4sREcouhkQYDB Co-authored-by: Claude <noreply@anthropic.com>
…e, not any Claude-Session: https://claude.ai/code/session_01JZnppgHRp4sREcouhkQYDB Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Seat ACCEPT: objectui PR #12598 at
|
| Viewport | xl before |
xl after |
|---|---|---|
| 1440 | 1024 · 208/208 | 1024 · 208/208 |
| 1024 | 1024 · 0/0 | 992 · 16/16 |
| 768 | 768 · 0/0 | 736 · 16/16 |
| 390 | 390 · 0/0 (sheet) | 390 · 0/0 (sheet) |
lg at 700 px is 668 · 16/16, and it stays 672 above 704 px. These are exactly the card's three pins. The seat accepts this reading in place of a served HotCRM reading:
- The defect and the fix are a pure function of the class list and the emitted CSS, and both were read from real sources.
- The PR's "What the landing seat's browser reading should show" table stays for a post-landing check.
Gates:
- Every
@object-ui/plugin-formsuite: 2291 passed, 1 skipped. - The objectui#12521 pins on both sides: 26 passed.
- Type-check after a closure build, the census and ratchet sweep (173), the changeset gates, the import and mock gates,
check:control-bytes,check:new-line-citationsand eslint: all exit 0.check-governed-queue-guard --testreports NOT GOVERNED. - CI on
c57843d95d: 36 runs, 33 success, 3 expected skips (dependabot and the coverage pair), 0 failures.mergeable_stateisclean. The path face (get_files) is.changeset/,packages/plugin-form/src/and its__tests__/: no governed path, 205 lines.
Accepted deviation: the shared value is a constant declared directly above modalSizeClasses, not inside the map. It is the same file, and no other symbol is touched.
Out of scope:
- Filed objectui#12603 (
finding): the same edge-to-edge defect in the action modal'sSIZE_CLASS(useActionModal.tsx; itsxlwas measured at 1024 · 0/0 and 768 · 0/0) and in six more wide centredDialogContentsites, enumerated there with an ask for one shared rule and a census pin. - Acceptance notes:
content/docs/plugins/plugin-form.mdxstates no widths, so nothing there becomes false. - Housekeeping noted, not repository content: a 5-byte
/build.pidthe dev left at the container root, outside every repository.
Landing: ready and auto-merge now. Its merge closes objectui#12593.
Generated by Claude Code
Fixes #12593
Clause-②: no
What changed
ModalForm's two bounded wide sizes arelg(sm:max-w-2xl, 672px) andxl(sm:max-w-5xl, 1024px). They now also read ONE shared class,BOUNDED_SIZE_VIEWPORT_GUTTER = 'sm:w-[calc(100%-2rem)]', defined once inModalForm.tsxnext tomodalSizeClasses.left-[50%]andtranslate-x-[-50%]centre it, so it keeps 1rem on each side at every width fromsmup.xl, 704px forlg), each size is exactly as wide as before.The class strings stay literal in source, so Tailwind's scanner sees both. The 2rem literal is written once.
Unchanged:
sm(sm:max-w-sm),default(sm:max-w-lg) andfull(sm:max-w-[95vw] sm:w-full);sm;h-[100dvh] sm:h-auto sm:max-h-[90vh];There is no edit to
packages/components/**,custom/mobile-dialog-content.tsx, any app-shell host or any other dialog. Nothing is added: no prop, export, type member ormodalSizevalue.Files:
packages/plugin-form/src/ModalForm.tsx: the constant and thelg/xlrows of the map;packages/plugin-form/src/__tests__/modalViewportGutter-12593.test.tsx;.changeset/12593-modal-viewport-gutter.md(@object-ui/plugin-form, patch).Mechanism assumptions, as measured
@tailwindcss/postcss, the pluginapps/console/postcss.config.jsnames, overapps/console/src/index.css. That file's@sourcelines includepackages/plugin-form/srcand exclude__tests__.width: calc(100% - 2rem)under the selector forsm:w-[calc(100%-2rem)].smmedia query (a width of at least 40rem), after the base.w-fullrule.git grepexits 1. The control,sm:max-w-5xlinModalForm.tsx, exits 0. So the emitted rule comes from this change.100%was chosen. The dialog isfixed, so100%is its containing block: the viewport less any classic scrollbar, the same boxleft-[50%]centres in.--hide-scrollbarsand made the page scroll, which gives a 10px scrollbar.100%form gives 16/16px gutters at 1024 and at 768.calc(100vw - 2rem)width, tried in the harness only, gives 11/11px: each gutter loses half the scrollbar.sm:max-w-5xl/sm:max-w-2xlas their own tokens and adds a separate width token.toContain('sm:max-w-5xl')(a heavy object opensxl) andtoContain('sm:max-w-2xl')(a light one openslg) pass as written, indialogDefaultLayout-12521.test.tsxand in app-shell'sAppContent.recordDialogInterimBound-12521.test.tsx.min(...)max-width shape. That shape writes the 2rem literal once per size, so it was not taken.Real-layout reading (Chromium, static harness)
The harness is the dialog element's class list from a real
ModalFormrender, aftertailwind-merge, over the console's emitted CSS. It ran in headless Chromium 1194 at a viewport height of 900. "Before" is the same class list without the gutter token. Each cell is width · left/right gutter, in px.xlbeforexlafterlgbeforelgaftersmdefaultfullThe
sm,defaultandfullcolumns read the same before and after. Their class lists do not change.What the landing seat's browser reading should show
HotCRM Leads → New. It is a field-heavy create, so it opens
xl, as the card measured. The numbers are predicted from the harness above:At 1024 and 768, the card measured the base at 1024px and 768px, both at x = 0. A reading that still shows x = 0 means the console CSS it served was not rebuilt from this head.
Tests and gates
Run on head
c57843d95dunless a line names another commit. Heavy runs went through the shared verify lock. CI runs the full farm.modalViewportGutter-12593.test.tsx, 4 tests:lgandxleach carry exactly one sm-tier width, the same gutter token, and keep their own max width;default,lg/xldiffer only insm:-prefixed tokens, and the phone sheet and height classes are still present;sm,defaultandfullread no gutter.@object-ui/plugin-formsuite:pnpm exec vitest run --maxWorkers=2 packages/plugin-form/gave Test Files 187 passed (187), Tests 2291 passed, 1 skipped (2292). The run printedc57843d95das its head. The new pin plus both 12521 files, including app-shell's, also ran on their own: 3 files, 26 tests passed.pnpm turbo run build --filter='@object-ui/plugin-form^...' --concurrency=2exited 0 (11 tasks). Thenpnpm --filter @object-ui/plugin-form type-check(tsc --noEmit && tsc -p tsconfig.test.json) exited 0.--listFilesshows thattsconfig.test.jsoncompiles the new pin.ModalForm.tsx's warnings were all there before this change.product-neutral-renderer.ratchet,no-component-any-cast.ratchet,iconOnlyButtonCensus-12143,ariaLabelLiteralCensus-12145;changeset-polarity-census,check-changeset-no-major;test-file-timings,test-shard-partition.check-changeset-presence(1 changeset declared for 2 source files of 1 released package),check-changeset-no-major,check-changeset-overwrite,check-changeset-fixed,check:changeset-claims,check:pending-changeset-literals,census:changeset-polarity.check:control-bytesandcheck:new-line-citationsexit 0 onc57843d95d, with 0 new citations.check:phantom-deps,check:self-import,check:unreferenced-sources,check:handler-key-reads,check:metadata-write-doors,check:i18n-keys,check:published-tsconfig-exclude,check:test-path-roots,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:shell-escape-residue.check-governed-queue-guard --teston the three paths answers NOT GOVERNED.Ablation
The fix was committed first. The mutation went through
ablation-replacewith a trapped restore: the constant's value'sm:w-[calc(100%-2rem)]'became''. On disk the anchor count went from 1 to 0 and the blob changed.lg and xl both carry the one shared gutter,a field-heavy dialog … carries itandbelow sm nothing changes ….git diff HEADis empty.Acceptance notes
useActionModal. This is the console'stype: 'modal'action path, and the dispatch keeps it off this PR.SIZE_CLASSmapslgtosm:max-w-2xlandxltosm:max-w-5xlon the ShadcnDialogContentprimitive'sw-full max-w-lg.xl.examples/app-showcase'sshowcase_quick_view(type: 'modal', target pageshowcase_component_gallery, no size).content/docs/plugins/plugin-form.mdxstates no widths, so nothing in it becomes false. No docs edit.Generated by Claude Code