e4fb425d0584447b957c42639fa7105be546f020
User reported: "when I refresh the page with this size viewport it switches between tablet and desktop view." The root cause was the two-step tier resolution: 1. Server renders shell based on User-Agent (mobile vs desktop only). 2. Client mounts with that hint, useEffect runs matchMedia, may flip. When the UA says "desktop" but the viewport is actually 900px (so matchMedia says "tablet"), the chrome visibly switches mid-render. Most painful on macOS Safari dragged below 1024. Fix: AppShell writes a `pn-crm.viewport-tier` cookie (1-year, Lax) on every matchMedia evaluation. The dashboard layout reads the cookie and prefers it over the UA classifier for `initialFormFactor`. First visit can still flicker (no cookie yet); every subsequent reload uses the resolved tier and renders the correct chrome on first paint. The cookie values are 'mobile' / 'tablet' / 'desktop' but the server's initialFormFactor prop only accepts 'mobile' | 'desktop' (binary by design — AppShell's useEffect resolves the actual tier client-side from matchMedia). 'tablet' from the cookie collapses to 'desktop' on SSR; AppShell's useEffect re-resolves to tablet immediately. The fluent path on cookie hit is desktop -> tablet (no flicker because both shells render the desktop tree; only the sidebar Sheet wrapper differs, and that's invisible until opened). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Description
No description provided
Languages
TypeScript
98.7%
HTML
1%
CSS
0.1%
Shell
0.1%