Browser state

Reactive window, viewport, network and device state: media queries, window size, orientation, online status, media devices.

Signals over page and device state. Singletons (windowSize, online, orientation) are shared app-wide; factories (createMediaQuery, createMediaDevices) clean up with their scope.

createMediaQuery

Creates a Computed<boolean> that tracks a CSS media query. Returns true when the query matches, false otherwise.

import {  } from "elements-kit/utilities/media-query";

const  = ("(prefers-color-scheme: dark)");

(); // true or false — live, updates on OS change

Signature

function createMediaQuery(
  query: string,
  defaultState?: boolean,
): Computed<boolean>
ParameterDescription
queryAny valid CSS media query string
defaultStateValue returned during SSR (where window is unavailable). Defaults to false.

Examples

Dark mode toggle

import { effect } from "elements-kit/signals";
import { createMediaQuery } from "elements-kit/utilities/media-query";

const isDark = createMediaQuery("(prefers-color-scheme: dark)");

effect(() => {
  document.documentElement.classList.toggle("dark", isDark());
});

Responsive layout

const isMobile  = createMediaQuery("(max-width: 640px)");
const isTablet  = createMediaQuery("(max-width: 1024px)");
const prefersReducedMotion = createMediaQuery("(prefers-reduced-motion: reduce)");

effect(() => {
  if (isMobile()) {
    // render compact layout
  } else if (isTablet()) {
    // render medium layout
  }
});

SSR / hydration

Pass defaultState to control the value returned on the server before the browser environment is available:

// Server renders as if dark mode is off
const isDark = createMediaQuery("(prefers-color-scheme: dark)", false);

Cleanup

The underlying MediaQueryList event listener is automatically removed when the signal goes out of scope — via onCleanup if created inside an effect, or Symbol.dispose for explicit resource management:

// Inside an effect or effectScope — cleanup is automatic
effectScope(() => {
  const isDark = createMediaQuery("(prefers-color-scheme: dark)");
  effect(() => document.body.classList.toggle("dark", isDark()));
});
// ↑ stop() cleans up the MediaQueryList listener too

// Explicit disposal
using isDark = createMediaQuery("(prefers-color-scheme: dark)");
// ↑ listener removed when `isDark` goes out of scope (TC39 `using` keyword)

windowSize

Module-level singleton. Reactive innerWidth and innerHeight of the browser window.

import { windowSize } from "elements-kit/utilities/window-size";

effect(() => console.log(windowSize.width(), windowSize.height()));

orientation

Module-level singleton. Reactive screen.orientation angle and type.

import { orientation } from "elements-kit/utilities/orientation";

effect(() => console.log(orientation.type(), orientation.angle()));

online

Module-level singleton. Computed<boolean> — true when navigator.onLine is true. Reacts to online / offline window events.

import { online } from "elements-kit/utilities/network";

effect(() => {
  if (!online()) showOfflineBanner();
});

createMediaDevices

Returns a reactive list of available media devices, refreshed when devices are added or removed.

import { createMediaDevices } from "elements-kit/utilities/media-devices";

const devices = createMediaDevices();

effect(() => console.log(devices().map((d) => d.label)));
function createMediaDevices(): Computed<MediaDeviceInfo[]>

See also