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
parent 979331b12d
commit 2a8a26687b
14 changed files with 686 additions and 52 deletions
+6 -4
View File
@@ -5,6 +5,7 @@ import { useWhpApi } from '../../hooks/useWhpApi';
import { usePages } from '../../state/PageContext';
import { useSiteDesign } from '../../state/SiteDesignContext';
import { useIsMobile } from '../../hooks/useIsMobile';
import { useMobileChrome } from '../../state/MobileChromeContext';
import { DeviceMode } from '../../types';
import { TemplateModal } from './TemplateModal';
import { HeadCodeModal } from './HeadCodeModal';
@@ -33,9 +34,10 @@ export const TopBar: React.FC<TopBarProps> = ({ device, onDeviceChange, showGuid
const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
const [publishStatus, setPublishStatus] = useState<'idle' | 'publishing' | 'published' | 'error'>('idle');
const [isDraft, setIsDraft] = useState(false);
const [templateModalOpen, setTemplateModalOpen] = useState(false);
const [headCodeModalOpen, setHeadCodeModalOpen] = useState(false);
const [overflowOpen, setOverflowOpen] = useState(false);
// Mobile-A2: lifted from private useState into MobileChromeContext so
// opening a mobile sheet can close these modals (item 3) -- behavior is
// otherwise identical for both the desktop and mobile branches below.
const { templateModalOpen, setTemplateModalOpen, headCodeModalOpen, setHeadCodeModalOpen, overflowOpen, setOverflowOpen } = useMobileChrome();
const isMobile = useIsMobile();
const { open: openSitesmith } = useSitesmithModal();
const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -247,7 +249,7 @@ export const TopBar: React.FC<TopBarProps> = ({ device, onDeviceChange, showGuid
<button
type="button"
className={`topbar-btn icon-only overflow-toggle${overflowOpen ? ' active' : ''}`}
onClick={() => setOverflowOpen((v) => !v)}
onClick={() => setOverflowOpen(!overflowOpen)}
aria-label="More options"
aria-expanded={overflowOpen}
aria-haspopup="menu"