diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts new file mode 100644 index 00000000..d8c76c03 --- /dev/null +++ b/src/hooks/useMediaQuery.ts @@ -0,0 +1,62 @@ +import { useState, useEffect } from 'react' + +/** + * Hook to detect if a media query matches + * @param query The media query string (e.g., '(max-width: 768px)') + * @returns true if the media query matches, false otherwise + */ +export function useMediaQuery(query: string): boolean { + const [matches, setMatches] = useState(() => { + if (typeof window === 'undefined') return false + return window.matchMedia(query).matches + }) + + useEffect(() => { + if (typeof window === 'undefined') return + + const mediaQuery = window.matchMedia(query) + + // Update state if the media query changes + const handleChange = (event: MediaQueryListEvent) => { + setMatches(event.matches) + } + + // Modern browsers + if (mediaQuery.addEventListener) { + mediaQuery.addEventListener('change', handleChange) + return () => mediaQuery.removeEventListener('change', handleChange) + } + // Legacy browsers + else { + mediaQuery.addListener(handleChange) + return () => mediaQuery.removeListener(handleChange) + } + }, [query]) + + return matches +} + +/** + * Hook to detect if the user is on a coarse pointer device (touch) + * @returns true if the user is using a coarse pointer (touch), false otherwise + */ +export function useIsCoarsePointer(): boolean { + return useMediaQuery('(pointer: coarse)') +} + +/** + * Hook to detect if the viewport is mobile-sized + * @returns true if viewport width is <= 768px, false otherwise + */ +export function useIsMobile(): boolean { + return useMediaQuery('(max-width: 768px)') +} + +/** + * Hook to detect if the viewport is tablet-sized + * @returns true if viewport width is <= 1024px, false otherwise + */ +export function useIsTablet(): boolean { + return useMediaQuery('(max-width: 1024px)') +} +