Skip to content

Основные концепции ​

Формат конфига ​

Каждый брейкпоинт описывается через MediaQueryConfig — массив условий, объединённых через AND, либо вложенный массив групп, объединённых через OR.

AND (плоский массив) ​

ts
// (min-width: 768px) and (max-width: 1023px)
;[
  { type: 'min-width', value: 768 },
  { type: 'max-width', value: 1023 },
]

OR (вложенный массив) ​

ts
// (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:

ts
// Совпадает с типом медиа 'print'
;[{ type: 'raw', value: 'print' }][
  // screen and (max-width: 600px)
  ({ type: 'raw', value: 'screen' }, { type: 'max-width', value: 600 })
]

Поддерживаемые типы условий ​

typeПример значенияСгенерированный запрос
min-width768(min-width: 768px)
max-width1023(max-width: 1023px)
min-height600(min-height: 600px)
max-height900(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> выводит тип булева состояния из объекта конфига, так что использующему коду не нужно вручную описывать форму итогового состояния:

ts
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).

ts
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
tablet601 – 960px
desktop≥ 961px

Создание независимых экземпляров ​

createResponsiveState()

Создаёт независимые экземпляры — полезно для SSR по-запросу, нескольких независимых контекстов или тестирования:

ts
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 подписки и Утилиты).

ts
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 SSRuseMediaQuery возвращает false; useResponsive возвращает серверный снапшот
Несовпадение при гидратацииВызовите hydrate() с серверным снапшотом перед первым рендером

Без подсказки сервер рендерит каждый ключ как false. Опция ssrState задаёт значения, которые используются вместо этого всякий раз, когда matchMedia (вьюпорт) или ResizeObserver (контейнер) недоступны:

ts
const state = createResponsiveState(config, {
  ssrState: { desktop: true },
})

Ключи, которых нет в конфиге, игнорируются, а не перечисленные ключи остаются false. Как только управление берёт браузер, настоящие значения заменяют засеянные. Та же опция есть у модуля Nuxt. О гидратации без несовпадений и о выборе раскладки по запросу — см. SSR и гидратация.

ts
// Сервер: сериализуем ожидаемое начальное состояние
const initialState = { mobile: false, tablet: false, desktop: true }

// Клиент: гидратируем перед первым рендером
import { responsiveState } from 'responsive-media'
responsiveState.hydrate(initialState)

Типы TypeScript ​

SetConfigOptions ​

Второй аргумент setResponsiveConfig() / createResponsiveState() / createContainerState().

ts
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() — ключи вашего конфига.

ts
interface BreakpointHelpers<K extends string = string> {
  current: K | null
  isAbove(key: K): boolean
  isBelow(key: K): boolean
  between(from: K, to: K): boolean
}