fix(builder): mobile-A2 hardening -- 16px inputs, shared sheet/modal chrome, z-scale + portal

- Force font-size:16px !important on Styles-sheet/topbar/Sitesmith inputs
  inside the mobile media query so inline 12px/14px styles stop triggering
  iOS zoom-on-focus.
- Lift sheet-open + Templates/Head Code modal-open state out of private
  useState into a shared MobileChromeContext (EditorShell), so Phase B can
  open/close sheets from outside MobilePanelBar.
- Add an explicit z-index layer scale, portal TemplateModal to
  document.body (was trapped under the tab bar inside .topbar's stacking
  context), align Sitesmith to the same --z-modal layer, and make opening
  a sheet close any open modal. Also fix modal backdrops swallowing tab
  bar taps (mirrors the sheet backdrop's existing tab-bar cutout).
- Drop BottomSheet's incorrect aria-modal; mobile-aware AssetsPanel empty
  state copy.
- Tests: useIsMobile (matchMedia mock incl. legacy fallback + cleanup),
  MobileChromeContext invariants (one sheet open, sheet closes modals),
  MobilePanelBar wiring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-13 07:30:12 -07:00
co-authored by Claude Opus 4.8
parent 979331b12d
commit 2a8a26687b
14 changed files with 686 additions and 52 deletions
+48 -5
View File
@@ -34,6 +34,24 @@
/* Phase A (mobile): fixed bottom tab bar height, used to size
.editor-container and the safe-area padding math below. */
--mobile-tabbar-height: 58px;
/* Mobile-A2: explicit overlay layer scale (was ad-hoc numbers scattered
across the mobile block + Modal.tsx, causing accidental stacking --
e.g. the Templates modal getting trapped under the tab bar inside
.topbar's own stacking context). Higher = closer to the user.
--z-selection-toolbar is reserved for Phase B's selection toolbar. */
--z-sheet-backdrop: 400;
--z-tabbar: 500;
--z-selection-toolbar: 600;
--z-modal: 5000;
--z-overflow: 10000;
}
/* --------------------------------------------------------------------------
Modals (shared by Modal.tsx -- TemplateModal, HeadCodeModal, SitesmithModal)
-------------------------------------------------------------------------- */
.modal-backdrop {
z-index: var(--z-modal);
}
/* --------------------------------------------------------------------------
@@ -1459,6 +1477,23 @@ body {
border-color: var(--color-accent);
}
/* --------------------------------------------------------------------
Full-screen modals (Modal.tsx -- TemplateModal/HeadCodeModal/
SitesmithModal share `.modal-backdrop`). Same tab-bar cutout as
`.mobile-sheet-backdrop` below, and for the same reason: without it,
the backdrop's default `inset: 0` sits ABOVE the tab bar (--z-modal >
--z-tabbar) and swallows every tap there, so "open Templates -> tap a
bottom tab" would silently just close Templates (a tap on the
backdrop) instead of ALSO opening the tapped sheet. `!important`
because Modal.tsx sets `bottom` via an inline style, which otherwise
beats this class rule. The modal's own content still sits above the
tab bar (it's a commit/cancel moment) -- only the dismiss-affordance
backdrop stops short, exactly like the sheet backdrop.
-------------------------------------------------------------------- */
.modal-backdrop {
bottom: calc(var(--mobile-tabbar-height) + env(safe-area-inset-bottom, 0px)) !important;
}
/* --------------------------------------------------------------------
"⋯" overflow menu (TopBarOverflowMenu.tsx)
-------------------------------------------------------------------- */
@@ -1466,7 +1501,7 @@ body {
position: fixed;
top: calc(var(--topbar-height) + env(safe-area-inset-top, 0px) + 6px);
right: calc(8px + env(safe-area-inset-right, 0px));
z-index: 10000;
z-index: var(--z-overflow);
min-width: 220px;
max-width: calc(100vw - 16px);
max-height: calc(100dvh - var(--topbar-height) - 24px);
@@ -1557,7 +1592,7 @@ body {
directly, instead of requiring a close-then-reopen round trip. The
backdrop itself stops short of the tab bar (see its `bottom` offset
below) so this is mostly belt-and-suspenders for the boundary. */
z-index: 500;
z-index: var(--z-tabbar);
display: flex;
height: calc(var(--mobile-tabbar-height) + env(safe-area-inset-bottom, 0px));
background: var(--color-bg-surface);
@@ -1605,7 +1640,7 @@ body {
being swallowed by the overlay sitting on top of it. */
bottom: calc(var(--mobile-tabbar-height) + env(safe-area-inset-bottom, 0px));
background: rgba(0, 0, 0, 0.5);
z-index: 400;
z-index: var(--z-sheet-backdrop);
display: flex;
align-items: flex-end;
}
@@ -1679,6 +1714,13 @@ body {
Touch ergonomics inside sheets -- 16px inputs kill iOS's auto-zoom on
focus; touch-action speeds up tap response on chrome that doesn't need
native scroll-gesture handling.
`!important` is required here: GuidedStyles/StylePanel inputs (see
`inputStyle` in `panels/right/styles/shared.tsx`) carry an INLINE
`font-size: 12px`, which otherwise beats this class rule on the
Styles sheet -- the most-used mobile surface -- and iOS zooms in on
focus. `.sitesmith-textarea` (ChatInput.tsx, inline font-size: 14)
has the same problem even though the Sitesmith modal isn't a sheet.
-------------------------------------------------------------------- */
.mobile-sheet-body button,
.mobile-sheet-body .block-item {
@@ -1693,8 +1735,9 @@ body {
.control-select,
.topbar input,
.topbar textarea,
.topbar select {
font-size: 16px;
.topbar select,
.sitesmith-textarea {
font-size: 16px !important;
}
.mobile-sheet-body .settings-tabs button {