npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sandlada/breakpoint

v0.0.1-20260831.a

Published

Breakpoint Observer — Responsive viewport/element breakpoint observer (MD3, RxJS, zero-framework)

Readme

@sandlada/breakpoint

Live Demo Open in GitHub Codespaces

Responsive viewport / element breakpoint observer — zero framework, pure functional, data-last, 100% RxJS.

A production-grade, framework-agnostic breakpoint evaluation and observation library for viewport and arbitrary DOM elements. Built with a pure functional, higher-order function, parameter-last architecture on rxjs@^7.8.x. Supports custom breakpoints, overlapping ranges, precise operator control, AND/OR composition, SSR safety, zero top-level side effects, and runtime element switching with fully symmetric width and height dimensions.

Features

  • Zero framework — pure functional design; no class instances, rxjs is the only runtime dependency
  • 100% reactivestate$ (shareReplay(1)), activeWidthBreakpoints$ / activeHeightBreakpoints$ (distinctUntilChanged + shareReplay), synchronous snapshot
  • Data-last & curryingmatchesBreakpointDefinition(def)(widthPx), evaluateBreakpointMap(map)(widthPx)
  • Viewport & element — observe window by default or any HTMLElement via ResizeObserver + rAF coalescing; switch at runtime with attachElement()
  • MD3 defaults — width (compact <600, medium 600–839, expanded 840–1199, large 1200–1599, extraLarge >=1600) and height (compact <480, medium 480–899, expanded >=900) with aliases xs/sm/md/lg/xl
  • String operators>, >=, <, <=, =, ==, != (e.g. '> 840px', '= 1200px', '!= 960px'), whitespace-tolerant, decimal support, px/rem/em and absolute/viewport units
  • AND / OR composition{ and: [...] } / { or: [...] } explicit composition
  • Overlapping ranges — each breakpoint evaluated independently; multiple active breakpoints simultaneously
  • SSR safe — strict typeof window === 'undefined' short-circuit; initial state configured via defaultWidthMatches / defaultHeightMatches
  • Tree-shakable & zero top-level side-effects ("sideEffects": false), ESM-only with unbundled module structure + d.ts

Install

# npm
npm install @sandlada/breakpoint rxjs

Live Demos (GitHub Pages)

Explore interactive examples directly in your browser without local installation:

| Demo Scene | Description | Live Link | | :--- | :--- | :--- | | 1. Viewport MD3 Basics | Standard Material Design 3 viewport breakpoints (compact, medium, expanded, large, extraLarge) | 🌐 Open Live Demo | | 2. Container Breakpoints | ResizeObserver-driven element container queries with resizable DOM box & runtime switching | 🌐 Open Live Demo | | 3. Custom & Composition | Custom operators (>, <=, !=), rem/em units, and { and: [...] } / { or: [...] } logical composition | 🌐 Open Live Demo | | 4. Height & 2D Observer | Symmetric viewport height breakpoints & 2D dual-dimension responsive metrics | 🌐 Open Live Demo | | 5. RxJS Streams & Lifecycle | Cold observables (observeWidthBreakpoint), RxJS pipe operators, and auto-cleanup | 🌐 Open Live Demo |

Usages

Quick Start

🌐 Live Demo: Try MD3 Viewport Observer on GitHub Pages

import { createBreakpointObserver } from '@sandlada/breakpoint'

const observer = createBreakpointObserver()

// Subscribe to state stream
const subscription = observer.state$.subscribe((state) => {
    console.log(state.activeWidthBreakpoints) // e.g. ['medium', 'sm']
    console.log(state.widthMatches)           // { compact: false, medium: true, ... }
})

// Read synchronous snapshot
console.log(observer.snapshot.activeWidthBreakpoints)

// Clean up when done
subscription.unsubscribe()
observer.dispose()

Standalone Reactive Streams

🌐 Live Demo: Try RxJS Streams & Lifecycle on GitHub Pages

import {
    observeBreakpointState,
    observeWidthBreakpoint,
    observeActiveWidthBreakpoints,
} from '@sandlada/breakpoint'

// Observe a single condition stream (cold observable, auto-cleans on unsubscription)
const isExpanded$ = observeWidthBreakpoint('>= 840px')
const sub1 = isExpanded$.subscribe((isExpanded) => {
    console.log('isExpanded:', isExpanded)
})

// Observe active breakpoint keys stream
const activeKeys$ = observeActiveWidthBreakpoints()
const sub2 = activeKeys$.subscribe((keys) => {
    console.log('active keys:', keys)
})

// Unsubscribe to clean up underlying listeners
sub1.unsubscribe()
sub2.unsubscribe()

Pure Functional Evaluation & Currying (Data-Last)

All evaluation functions support direct invocation, curried (data-last) partial application, and argument reordering:

import {
    matchesBreakpointCondition,
    matchesBreakpointDefinition,
    evaluateBreakpointMap,
    DEFAULT_WIDTH_BREAKPOINTS,
} from '@sandlada/breakpoint'

// 1. Curried condition evaluation (data-last)
const isLargeWidth = matchesBreakpointCondition('>= 1200px')
console.log(isLargeWidth(1440)) // true
console.log(isLargeWidth(800))  // false

// 2. Direct condition evaluation
console.log(matchesBreakpointCondition('>= 1200px', 1440)) // true
console.log(matchesBreakpointCondition(1440, '>= 1200px')) // true

// 3. Definition evaluation with AND/OR logic
const isTablet = matchesBreakpointDefinition({ and: ['>= 600px', '< 1024px'] })
console.log(isTablet(768)) // true

// 4. Map evaluation
const evaluateMd3 = evaluateBreakpointMap(DEFAULT_WIDTH_BREAKPOINTS)
const result = evaluateMd3(1024)
console.log(result.activeBreakpoints) // ['expanded', 'md']
console.log(result.matchesTable)     // { compact: false, medium: false, expanded: true, ... }

Custom Breakpoints & Logical Composition

🌐 Live Demo: Try Custom Conditions & Composition on GitHub Pages

import { createBreakpointObserver, Breakpoint } from '@sandlada/breakpoint'

const observer = createBreakpointObserver({
    widthBreakpoints: {
        mobile: '< 600px',
        tablet: Breakpoint.interval(600, 1024),
        desktop: '>= 1024px',
        customOr: { or: ['< 480px', '>= 1920px'] },
        customAnd: { and: ['>= 768px', '<= 1440px'] },
        customRange: { min: 600, max: 960, minInclusive: true, maxInclusive: false },
    },
})

Element Observation & Runtime Switching

🌐 Live Demo: Try Element Container Queries on GitHub Pages

import { createBreakpointObserver } from '@sandlada/breakpoint'

const containerElement = document.querySelector('#container') as HTMLElement

const containerObserver = createBreakpointObserver({
    element: containerElement,
    dimension: 'both', // 'width' | 'height' | 'both'
})

containerObserver.state$.subscribe((state) => {
    console.log(state.width, state.height)
    console.log(state.activeWidthBreakpoints, state.activeHeightBreakpoints)
})

// Switch observed element at runtime
const otherElement = document.querySelector('#sidebar') as HTMLElement
containerObserver.attachElement(otherElement)

// Detach element to fall back to viewport observation
containerObserver.detachElement()

// Destroy observer and disconnect ResizeObserver
containerObserver.dispose()

Server-Side Rendering (SSR)

The library safely evaluates on Node.js/SSR environments without window errors:

import { createBreakpointObserver, isServer } from '@sandlada/breakpoint'

console.log(isServer()) // true on server

const ssrObserver = createBreakpointObserver({
    defaultWidthMatches: {
        compact: true,
        medium: false,
        expanded: false,
    },
})

// Synchronous snapshot and initial stream values use defaults
console.log(ssrObserver.snapshot.activeWidthBreakpoints) // ['compact']

API Reference

Observer & Stream Creators

  • createBreakpointObserver(configuration?: BreakpointConfiguration): BreakpointObserverInstance
  • observeBreakpointState(configuration?: BreakpointConfiguration): Observable<BreakpointState>
  • observeBreakpoint(definition: BreakpointDefinition, dimension?: BreakpointDimension, configuration?: BreakpointConfiguration): Observable<boolean>
  • observeWidthBreakpoint(definition: BreakpointDefinition, configuration?: BreakpointConfiguration): Observable<boolean>
  • observeHeightBreakpoint(definition: BreakpointDefinition, configuration?: BreakpointConfiguration): Observable<boolean>
  • observeActiveBreakpoints(dimension?: BreakpointDimension, configuration?: BreakpointConfiguration): Observable<string[]>
  • observeActiveWidthBreakpoints(configuration?: BreakpointConfiguration): Observable<string[]>
  • observeActiveHeightBreakpoints(configuration?: BreakpointConfiguration): Observable<string[]>
  • getDefaultViewportObserver(): BreakpointObserverInstance

Pure Evaluation & Transformation Functions

  • parseBreakpointCondition(conditionString: BreakpointCondition): ParsedBreakpointCondition
  • matchesBreakpointCondition(condition: BreakpointCondition, options?: BreakpointEvaluationOptions): (value: number) => boolean
  • matchesBreakpointCondition(condition: BreakpointCondition, value: number, options?: BreakpointEvaluationOptions): boolean
  • matchesBreakpointCondition(value: number, condition: BreakpointCondition, options?: BreakpointEvaluationOptions): boolean
  • matchesBreakpointDefinition(definition: BreakpointDefinition, options?: BreakpointEvaluationOptions): (value: number) => boolean
  • matchesBreakpointDefinition(definition: BreakpointDefinition, value: number, options?: BreakpointEvaluationOptions): boolean
  • matchesBreakpointDefinition(value: number, definition: BreakpointDefinition, options?: BreakpointEvaluationOptions): boolean
  • evaluateBreakpointMap(map: BreakpointMap, options?: BreakpointEvaluationOptions): (value: number) => BreakpointEvaluationResult
  • evaluateBreakpointMap(map: BreakpointMap, value: number, options?: BreakpointEvaluationOptions): BreakpointEvaluationResult
  • evaluateBreakpointMap(value: number, map: BreakpointMap, options?: BreakpointEvaluationOptions): BreakpointEvaluationResult
  • computeBreakpointState(targetWidthPx: number, targetHeightPx: number, configuration?: BreakpointConfiguration): BreakpointState
  • convertConditionToMediaQuery(condition: BreakpointCondition, dimension: 'width' | 'height', mediaQueryExclusiveStep: number): string | null
  • convertDefinitionToMediaQuery(definition: BreakpointDefinition, dimension: 'width' | 'height', mediaQueryExclusiveStep: number): string | null

Breakpoint Builders & Helpers

  • Breakpoint.gt(value: number, unit?: BreakpointUnit): BreakpointCondition
  • Breakpoint.gte(value: number, unit?: BreakpointUnit): BreakpointCondition
  • Breakpoint.lt(value: number, unit?: BreakpointUnit): BreakpointCondition
  • Breakpoint.lte(value: number, unit?: BreakpointUnit): BreakpointCondition
  • Breakpoint.eq(value: number, unit?: BreakpointUnit): BreakpointCondition
  • Breakpoint.ne(value: number, unit?: BreakpointUnit): BreakpointCondition
  • Breakpoint.interval(min: number, max: number, options?: BreakpointIntervalOptions): BreakpointDefinition
  • Breakpoint.between(min: number, max: number, options?: BreakpointIntervalOptions): BreakpointDefinition
  • greaterThan(value: number, unit?: BreakpointUnit): BreakpointCondition
  • greaterThanOrEqual(value: number, unit?: BreakpointUnit): BreakpointCondition
  • lessThan(value: number, unit?: BreakpointUnit): BreakpointCondition
  • lessThanOrEqual(value: number, unit?: BreakpointUnit): BreakpointCondition
  • equals(value: number, unit?: BreakpointUnit): BreakpointCondition
  • notEquals(value: number, unit?: BreakpointUnit): BreakpointCondition
  • createBreakpointInterval(min: number, max: number, options?: BreakpointIntervalOptions): BreakpointDefinition

Environment & SSR Utilities

  • isServer(): boolean
  • isBrowser(): boolean
  • canUseDOM(): boolean
  • canUseMatchMedia(): boolean
  • canUseResizeObserver(): boolean
  • canUseRequestAnimationFrame(): boolean
  • getWindow(): (Window & typeof globalThis) | undefined
  • getDocument(): Document | undefined

Reactive & Comparison Utilities

  • Subject (re-exported from rxjs)
  • isShallowEqualArray<T>(firstArray: readonly T[], secondArray: readonly T[]): boolean
  • isShallowEqualRecord(firstRecord: Readonly<Record<string, boolean>>, secondRecord: Readonly<Record<string, boolean>>): boolean

Constants

  • DEFAULT_WIDTH_BREAKPOINTS: Readonly<WidthBreakpointMap>
  • DEFAULT_HEIGHT_BREAKPOINTS: Readonly<HeightBreakpointMap>
  • ABSOLUTE_PX: Readonly<Record<AbsoluteBreakpointUnit, number>>
  • REM_BASE: number (16)
  • EM_BASE: number (16)

Types & Interfaces

  • BreakpointObserverInstance
  • BreakpointState
  • BreakpointConfiguration
  • BreakpointDefinition
  • BreakpointCondition
  • BreakpointRange
  • BreakpointDimension ('width' | 'height' | 'both')
  • BreakpointLogic ('and' | 'or')
  • BreakpointUnit / ParsedBreakpointUnit
  • AbsoluteBreakpointUnit
  • WidthBreakpointMap / HeightBreakpointMap / BreakpointMap
  • BreakpointEvaluationOptions
  • BreakpointEvaluationResult
  • BreakpointIntervalOptions
  • ParsedBreakpointCondition

Testing & Quality Assurance

This repository employs a Spec-First / Black-Box testing methodology. Tests verify interface signatures, mathematical boundary constraints, and lifecycle contracts without coupling to internal implementations.

Each test suite organizes specifications with structured nested describe blocks:

  • Happy Path: Standard valid inputs, mathematical interval correctness, state calculations, and currying equivalence.
  • Boundary & Error Handling: Critical boundary transitions (±1px, 0px, float precision), malformed syntax exceptions (TypeError), and empty or unsupported unit fallbacks.
  • RxJS Streams & Teardown: Cold observable verification, initial synchronous emissions, multi-subscriber multicast, distinctUntilChanged deduplication, and complete listener release upon unsubscribe() / dispose().
  • Environment Isolation: SSR safety (isServer: true), custom defaultWidthMatches / defaultHeightMatches fallback handling, and graceful degradation when browser APIs are unavailable.
npm run lint:types    # Typecheck with tsc --noEmit
npm test              # Run vitest test suite
npm run build         # Build dist via tsdown

License

MIT