'use client'; import { useSyncExternalStore } from 'react'; const MOBILE_QUERY = '(max-width: 1023.98px)'; function subscribe(callback: () => void): () => void { const mq = window.matchMedia(MOBILE_QUERY); mq.addEventListener('change', callback); return () => mq.removeEventListener('change', callback); } function getSnapshot(): boolean { return window.matchMedia(MOBILE_QUERY).matches; } function getServerSnapshot(): boolean { // Server has no window — default to desktop. Client hydrates to the // true viewport state without a flash because useSyncExternalStore // is React 18's "this is server-mismatch safe" primitive. return false; } /** * Returns true when the viewport is below the `lg` Tailwind breakpoint. * Backed by useSyncExternalStore so render reads stay pure (no * useEffect → setState cascade); React Compiler-safe. * * Not unit-tested: the repo's vitest is configured for environment='node' * (no @testing-library/react / DOM env). Verified through the mobile-shell * Playwright visual snapshots in Task 23. */ export function useIsMobile(): boolean { return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); }