Основные концепции
Формат конфига
Каждый брейкпоинт описывается через MediaQueryConfig — массив условий, объединённых через AND, либо вложенный массив групп, объединённых через OR.
AND (плоский массив)
// (min-width: 768px) and (max-width: 1023px)
;[
{ type: 'min-width', value: 768 },
{ type: 'max-width', value: 1023 },
]OR (вложенный массив)
// (max-width: 600px), (orientation: portrait) and (max-width: 1024px)
;[
[{ type: 'max-width', value: 600 }],
[
{ type: 'orientation', value: 'portrait' },
{ type: 'max-width', value: 1024 },
],
]Сырой тип медиа
Используйте type: 'raw', чтобы вставить значение как есть — полезно для типов медиа вроде print или screen:
// Совпадает с типом медиа 'print'
;[{ type: 'raw', value: 'print' }][
// screen and (max-width: 600px)
({ type: 'raw', value: 'screen' }, { type: 'max-width', value: 600 })
]Поддерживаемые типы условий
type | Пример значения | Сгенерированный запрос |
|---|---|---|
min-width | 768 | (min-width: 768px) |
max-width | 1023 | (max-width: 1023px) |
min-height | 600 | (min-height: 600px) |
max-height | 900 | (max-height: 900px) |
orientation | 'portrait' | (orientation: portrait) |
aspect-ratio | '16/9' | (aspect-ratio: 16/9) |
prefers-color-scheme | 'dark' | (prefers-color-scheme: dark) |
prefers-reduced-motion | 'reduce' | (prefers-reduced-motion: reduce) |
prefers-contrast | 'more' | (prefers-contrast: more) |
hover | 'none' | (hover: none) |
pointer | 'coarse' | (pointer: coarse) |
forced-colors | 'active' | (forced-colors: active) |
resolution | '2dppx' | (resolution: 2dppx) |
display-mode | 'standalone' | (display-mode: standalone) |
raw | 'print' | print (как есть) |
ConfigToState<T> выводит тип булева состояния из объекта конфига, так что использующему коду не нужно вручную описывать форму итогового состояния:
import type { ConfigToState, MediaQueryConfig } from 'responsive-media'
const config = {
sm: [{ type: 'min-width', value: 640 }],
lg: [{ type: 'min-width', value: 1024 }],
} satisfies Record<string, MediaQueryConfig>
type MyState = ConfigToState<typeof config>
// → { sm: boolean; lg: boolean }
const { sm, lg } = responsiveState.getState<MyState>()Глобальный синглтон
Библиотека экспортирует уже настроенный синглтон responsiveState, инициализированный конфигом ResponsiveConfig по умолчанию (mobile / tablet / desktop).
import { responsiveState, setResponsiveConfig, getResponsiveMediaQueries } from 'responsive-media'
// Перенастраиваем синглтон
setResponsiveConfig(
{
sm: [{ type: 'max-width', value: 767 }],
lg: [{ type: 'min-width', value: 1024 }],
},
{
order: ['sm', 'lg'], // для isAbove / isBelow / between
debounce: 50, // мс — троттлинг слушателей subscribe()
},
)
// Читаем стабильный снапшот
const { sm, lg } = responsiveState.getState()
// Живой доступ через прокси (никогда не дебаунсится)
console.log(responsiveState.proxy.sm)
// Получаем сгенерированные CSS-строки
const mq = getResponsiveMediaQueries()
// { sm: '(max-width: 767px)', lg: '(min-width: 1024px)' }Конфиг по умолчанию (ResponsiveConfig)
| Ключ | Диапазон |
|---|---|
mobile | ≤ 600px |
tablet | 601 – 960px |
desktop | ≥ 961px |
Создание независимых экземпляров
createResponsiveState()
Создаёт независимые экземпляры — полезно для SSR по-запросу, нескольких независимых контекстов или тестирования:
import { createResponsiveState, TailwindPreset, TailwindOrder } from 'responsive-media'
const layoutState = createResponsiveState(TailwindPreset, {
order: [...TailwindOrder],
})
const themeState = createResponsiveState({
dark: [{ type: 'prefers-color-scheme', value: 'dark' }],
reducedMotion: [{ type: 'prefers-reduced-motion', value: 'reduce' }],
})
layoutState.subscribe((s) => console.log('layout:', s))
themeState.subscribe((s) => console.log('theme:', s))
// Очистка по завершении (например, SSR по-запросу)
layoutState.destroy()ContainerState
Отслеживает размеры элемента через ResizeObserver и вычисляет условия брейкпоинтов в JavaScript — та же концепция, что и CSS Container Queries, но в JS.
API идентичен ReactiveResponsiveState — все методы подписки работают так же (см. API подписки и Утилиты).
import { createContainerState } from 'responsive-media/container'
// или: import { createContainerState } from 'responsive-media';
const card = document.querySelector('.card')!
const cardState = createContainerState(
card,
{
compact: [{ type: 'max-width', value: 300 }],
normal: [
{ type: 'min-width', value: 301 },
{ type: 'max-width', value: 599 },
],
wide: [{ type: 'min-width', value: 600 }],
},
{
order: ['compact', 'normal', 'wide'],
},
)
// Реактивное переключение классов
cardState.on('compact', (v) => card.classList.toggle('card--compact', v))
// Синхронизация CSS custom properties: --card-compact: 1; --card-wide: 0; …
cardState.syncCSSVars({ prefix: '--card-' })
// Получаем строки запросов, совместимые с @container
const strings = cardState.getMediaQueries()
// { compact: '(max-width: 300px)', wide: '(min-width: 600px)' }
// Очистка
cardState.destroy()Поддерживаемые типы условий для ContainerState
max-width, min-width, max-height, min-height, orientation, aspect-ratio
SSR и гидратация
Все API SSR-безопасны — они проверяют доступность window, matchMedia и ResizeObserver перед использованием и откатываются на false на сервере.
| Сценарий | Поведение |
|---|---|
typeof window === 'undefined' | Все слушатели пропускают настройку; proxy / getState() возвращают false для всех ключей |
| Nuxt | Используйте модуль Nuxt |
| React SSR | useMediaQuery возвращает false; useResponsive возвращает серверный снапшот |
| Несовпадение при гидратации | Вызовите hydrate() с серверным снапшотом перед первым рендером |
Без подсказки сервер рендерит каждый ключ как false. Опция ssrState задаёт значения, которые используются вместо этого всякий раз, когда matchMedia (вьюпорт) или ResizeObserver (контейнер) недоступны:
const state = createResponsiveState(config, {
ssrState: { desktop: true },
})Ключи, которых нет в конфиге, игнорируются, а не перечисленные ключи остаются false. Как только управление берёт браузер, настоящие значения заменяют засеянные. Та же опция есть у модуля Nuxt. О гидратации без несовпадений и о выборе раскладки по запросу — см. SSR и гидратация.
// Сервер: сериализуем ожидаемое начальное состояние
const initialState = { mobile: false, tablet: false, desktop: true }
// Клиент: гидратируем перед первым рендером
import { responsiveState } from 'responsive-media'
responsiveState.hydrate(initialState)Типы TypeScript
SetConfigOptions
Второй аргумент setResponsiveConfig() / createResponsiveState() / createContainerState().
interface SetConfigOptions {
debounce?: number // задержка debounce (мс) для слушателей subscribe(); on()/onEnter()/onLeave()/once()/waitFor() никогда не дебаунсятся. 0 отключает (по умолчанию)
order?: string[] // явный порядок брейкпоинтов для isAbove()/isBelow()/between(); по умолчанию — порядок ключей в конфиге
ssrState?: Record<string, boolean> // значения вместо `false`, когда matchMedia / ResizeObserver недоступны (SSR)
}BreakpointHelpers
Тип возвращаемого значения useBreakpoints() (Vue и React — одна и та же форма, current — это ComputedRef<K | null> в Vue и обычная K | null в React). K — тип ключа: по умолчанию string, а для хуков из defineResponsive() — ключи вашего конфига.
interface BreakpointHelpers<K extends string = string> {
current: K | null
isAbove(key: K): boolean
isBelow(key: K): boolean
between(from: K, to: K): boolean
}