Skip to content

Responsive Media

v2.2.2УтилитыVanilla JSVueNuxtReact

Реактивное булево состояние на основе CSS media queries и размеров элементов для Vanilla JS, Vue 3, Nuxt и React 19+ — без обязательных peer-зависимостей.

Responsive Media
Начать знакомство →
npm install responsive-media@latest
01 — Назначение

Когда это пригодится

CSS media query решает, как выглядит вёрстка, но ничего не сообщает JavaScript — responsive-media превращает тот же самый media query в обычное реактивное булево значение, которое можно читать прямо в логике компонента, без отдельного resize-listener и ручного дебаунса.

Компоненту нужно знать брейкпоинт, не только CSS

На мобильном показывается пять карточек в ленте, на десктопе — таблица на двадцать колонок. Это не вопрос стилей, а разный набор данных и разная логика рендера — компонент читает текущий брейкпоинт как обычное реактивное значение и сам решает, что рендерить.

Виджет меняет вид по размеру своего блока, а не экрана

Один и тот же дашборд-виджет в узкой боковой панели должен схлопываться в компактный вид, а в широкой центральной колонке — раскрываться полностью. Виджет отслеживает размер собственного контейнера, а не всего экрана, и переключается сам.

Пользователь попросил браузер не показывать анимации

Системная настройка «уменьшить движение» должна реально отключать переходы и параллакс на сайте — она доступна как то же самое реактивное значение, что и обычные брейкпоинты, и на неё можно так же просто подписаться.

Страница не должна на долю секунды показать не ту вёрстку

На сервере ещё не известен реальный размер экрана пользователя — состояние безопасно переносится с сервера на клиент без рассинхронизации, и вёрстка не дёргается, переключаясь с одной версии на другую сразу после загрузки.

02 — Фичи

Коротко о главном

Реактивное состояние от медиа- и контейнерных запросов

Реактивное состояние от медиа- и контейнерных запросов

Отслеживайте изменения вьюпорта и размеров элементов через единый API. ReactiveResponsiveState использует window.matchMedia для брейкпоинтов, а ContainerState — ResizeObserver для оценки размеров и ориентации элемента. Состояние всегда актуально и обновляется без лишних ререндеров.

Гибкая конфигурация с AND/OR и множеством условий

Гибкая конфигурация с AND/OR и множеством условий

Описывайте брейкпоинты как массив условий (AND) или вложенный массив групп (OR). Поддерживаются min/max-width/height, orientation, aspect-ratio, prefers-color-scheme, hover, pointer, resolution, display-mode и raw-строки. Это даёт полный контроль над логикой адаптивности.

Богатый API подписки и упорядоченные брейкпоинты

Богатый API подписки и упорядоченные брейкпоинты

Подписывайтесь на общие изменения (subscribe), конкретные ключи (on), переходы (onEnter/onLeave), однократные события (once) и ожидайте выполнения условия (waitFor). Упорядоченные брейкпоинты дают методы current, isAbove, isBelow и between для семантических сравнений.

Утилиты, пресеты и интеграция с сигналами

Утилиты, пресеты и интеграция с сигналами

Синхронизируйте состояние с CSS-переменными (syncCSSVars), генерируйте DOM-события (emitDOMEvents), преобразуйте ключи в сигналы (toSignal) и выбирайте значения по первому активному брейкпоинту (match). Используйте готовые пресеты для Tailwind, Bootstrap и доступности.

Готовая интеграция с Vue 3, React 19+ и Nuxt

Готовая интеграция с Vue 3, React 19+ и Nuxt

Vue-композаблы (useResponsive, useBreakpoints, useMediaQuery, useContainerState) и React-хуки на useSyncExternalStore обеспечивают реактивность в шаблонах и компонентах. Модуль Nuxt берёт брейкпоинты из nuxt.config.ts, генерирует типы по ним, гидратируется без предупреждений о несовпадении и умеет рендерить сервер под устройство посетителя; defineResponsive выводит ключи состояния из вашего конфига. Все API SSR-безопасны, поддерживают гидратацию (в том числе ssrState для серверной раскладки) и не требуют обязательных peer-зависимостей.

Независимое ядро и безопасная гидратация

Независимое ядро и безопасная гидратация

Ядро библиотеки не зависит ни от одного фреймворка — Vue- и React-интеграции лишь тонко оборачивают один и тот же runtime, так что логику можно переиспользовать и в vanilla-коде. createResponsiveState() создаёт изолированные экземпляры состояния, а hydrate() безопасно переносит серверное состояние на клиент.

03 — Быстрый пример

Как это работает

Один настроенный синглтон на всё приложение

responsiveState уже настроен под mobile/tablet/desktop из коробки — getState() отдаёт стабильный снапшот, proxy — живой доступ без дебаунса, а getResponsiveMediaQueries() возвращает те же условия готовыми CSS-строками.

singleton.ts
import { responsiveState, setResponsiveConfig, getResponsiveMediaQueries } from 'responsive-media'

// Re-configure the singleton
setResponsiveConfig(
  {
    sm: [{ type: 'max-width', value: 767 }],
    lg: [{ type: 'min-width', value: 1024 }],
  },
  {
    order: ['sm', 'lg'], // for isAbove / isBelow / between
    debounce: 50, // ms — throttle subscribe() listeners
  },
)

// Read a stable snapshot
const { sm, lg } = responsiveState.getState()

// Live proxy access (never debounced)
console.log(responsiveState.proxy.sm)

// Get the generated CSS strings
const mq = getResponsiveMediaQueries()
// { sm: '(max-width: 767px)', lg: '(min-width: 1024px)' }

Контейнерные запросы без фреймворка

createContainerState следит за размером конкретного DOM-элемента через ResizeObserver и сам переключает классы/CSS-переменные — та же идея, что CSS Container Queries, но с поддержкой браузеров без них.

container-vanilla.ts
import { createContainerState } from 'responsive-media/container'
// or: 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'],
  },
)

// Reactive class toggling
cardState.on('compact', (v) => card.classList.toggle('card--compact', v))

// Sync CSS custom properties: --card-compact: 1; --card-wide: 0; …
cardState.syncCSSVars({ prefix: '--card-' })

// Get @container-compatible query strings
const strings = cardState.getMediaQueries()
// { compact: '(max-width: 300px)', wide: '(min-width: 600px)' }

// Cleanup — call once you're done watching this element (e.g. before
// removing it from the DOM), not right after setup
// cardState.destroy()