diff --git a/src/hooks/use-is-mobile.ts b/src/hooks/use-is-mobile.ts new file mode 100644 index 0000000..70d00eb --- /dev/null +++ b/src/hooks/use-is-mobile.ts @@ -0,0 +1,29 @@ +'use client'; + +import { useEffect, useState } from 'react'; + +const MOBILE_QUERY = '(max-width: 1023.98px)'; + +/** + * Returns true when the viewport is below the `lg` Tailwind breakpoint. + * Backed by a media-query listener; safe to call from any client component. + * Server renders return `false` (desktop default) — clients hydrate to the + * true viewport state on mount. + * + * 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 { + const [isMobile, setIsMobile] = useState(false); + + useEffect(() => { + const mq = window.matchMedia(MOBILE_QUERY); + const update = (e: { matches: boolean }) => setIsMobile(e.matches); + setIsMobile(mq.matches); + mq.addEventListener('change', update); + return () => mq.removeEventListener('change', update); + }, []); + + return isMobile; +}