Files
warmbly/web/src/hooks/useMediaQuery.ts
T

36 lines
1.4 KiB
TypeScript

import * as React from "react"
// useMediaQuery — a media query as reactive state.
//
// The first value is read synchronously from matchMedia rather than defaulting
// to false in an effect: a layout that keys off it (the unibox contact rail,
// the sidebar rail) would otherwise render its narrow form for one frame.
export function useMediaQuery(query: string): boolean {
const read = React.useCallback(
() => (typeof window === "undefined" || !window.matchMedia ? false : window.matchMedia(query).matches),
[query],
)
const [matches, setMatches] = React.useState(read)
React.useEffect(() => {
const mq = window.matchMedia?.(query)
if (!mq) return
const onChange = () => setMatches(mq.matches)
// Re-read on subscribe: the query can have flipped between the initial
// render and this effect.
onChange()
mq.addEventListener("change", onChange)
return () => mq.removeEventListener("change", onChange)
}, [query])
return matches
}
// Tailwind's breakpoints are rem, and rem in a media query resolves against the
// browser's default font size, not the page's. Spelling these in px would put
// JS and CSS on different lines for anyone who is not on a 16px default, which
// is how a panel ends up in its overlay form while its `lg:` static styles have
// already applied.
export const MD_QUERY = "(min-width: 48rem)"
export const LG_QUERY = "(min-width: 64rem)"