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

ngx-mq

v3.1.0

Published

Signal-powered breakpoints and media queries for Angular. SSR-safe, zoneless-ready, no RxJS.

Readme


Overview

A responsive value is just a signal: read it in the template, compose it, and never wire up cleanup.

import { Component } from '@angular/core';
import { up } from 'ngx-mq';

@Component({
  selector: 'app-root',
  template: `
    @if (isDesktop()) {
      <app-sidebar />
    }
  `,
})
export class AppComponent {
  readonly isDesktop = up('lg');
}
  • Signal-native so it works anywhere signals do, zoneless apps included.
  • Zero boilerplate: no subscriptions, no unsubscribe, cleanup is automatic.
  • SSR-safe with a value you control on the server.
  • Batteries included: Tailwind, Bootstrap and Material presets, plus and / or / not.
  • Tiny: ~1.9 kB gzipped, and no RxJS.

Install

npm i ngx-mq        # Angular 20-22

Angular 19 -> ngx-mq@2  ·  Angular 16-18 -> ngx-mq@1

Then register your breakpoints once, at bootstrap:

import { provideBreakpoints } from 'ngx-mq';

bootstrapApplication(AppComponent, {
  providers: [provideBreakpoints({ sm: 640, md: 768, lg: 1024 })],
  // or a preset: provideTailwindBreakpoints() / provideBootstrapBreakpoints() / provideMaterialBreakpoints()
});

Call the helpers inside an injection context: a component field, a constructor, or a DI factory.

Examples

Show different layouts per screen size

readonly isMobile = down('md');
readonly isTablet = between('md', 'lg');
readonly isDesktop = up('lg');

Follow the system dark mode

readonly prefersDark = colorScheme('dark');

Drop hover styles on touch devices

// `hover()` has no direct inverse, so compose it
readonly isTouchLike = not(hover());

Combine any conditions

// Large screen, in landscape, with a hover-capable pointer
readonly isLandscapeDesktop = and(up('lg'), orientation('landscape'), hover());

// Small screens OR a reduced-motion preference
readonly prefersSimpleUi = or(down('md'), reducedMotion());

Respect reduced motion

readonly reduceMotion = reducedMotion();

Anything else, with a raw query

readonly isRetina = matchMediaSignal('(min-resolution: 2dppx)');

Why ngx-mq?

Angular's CDK ships BreakpointObserver, which works well but is built around RxJS and raw query strings. ngx-mq is built for the signals era: read a value in the template, subscribe to nothing, clean up automatically.

| | ngx-mq | CDK BreakpointObserver | | --------------------- | ------------------------------------------- | ------------------------------------- | | Reactivity | Signal<boolean> | Observable<BreakpointState> | | Cleanup | Automatic via DestroyRef | Manual (takeUntilDestroyed) | | Named breakpoints | Tailwind / Bootstrap / Material or your own | Material breakpoints or raw strings | | Media-feature helpers | colorScheme, hover, pointer, ... | Raw query strings | | Composition | and / or / not | RxJS operators | | SSR | Configurable static value | Handle it yourself | | Footprint | ~1.9 kB standalone | Part of @angular/cdk |

Documentation

Spin it up in seconds on StackBlitz, no setup required.

Full API reference, guides and recipes live at martsinlabs.github.io/ngx-mq.

Every query helper returns a Signal<boolean> and accepts an optional options argument (CreateMediaQueryOptions).

Breakpoints

| Helper | Arguments | true when | | --------- | ---------------- | ------------------------------------ | | up | bp | viewport width >= bp | | down | bp | viewport width < bp (exclusive) | | between | minBp, maxBp | viewport width is in [minBp, maxBp) |

down and between upper bounds are exclusive: a small epsilon (default 0.02, set via provideBreakpointEpsilon) is subtracted from the max so adjacent ranges never overlap.

Media features

| Helper | Arguments | true when | | --------------- | ------------------------------ | ---------------------------------------- | | orientation | 'portrait' \| 'landscape' | the screen orientation matches | | colorScheme | 'light' \| 'dark' | the system color scheme matches | | displayMode | DisplayModeOption | the display mode matches (PWA detection) | | reducedMotion | none | the user prefers reduced motion | | prefersContrast | 'more' \| 'less' \| 'no-preference' \| 'custom' | the user's contrast preference matches | | hover | none | the primary pointer can hover | | anyHover | none | any available pointer can hover | | pointer | 'fine' \| 'coarse' \| 'none' | the primary pointer matches | | anyPointer | 'fine' \| 'coarse' \| 'none' | any available pointer matches | | colorGamut | 'srgb' \| 'p3' \| 'rec2020' | the display covers the gamut |

Composition

| Helper | Arguments | true when | | ------ | ---------------------------------- | ----------------------------------------- | | and | ...conditions: Signal<boolean>[] | every condition is true (empty: true) | | or | ...conditions: Signal<boolean>[] | any condition is true (empty: false) | | not | condition: Signal<boolean> | the condition is false |

Custom queries

| Helper | Arguments | Description | | ------------------ | --------------- | -------------------------------------------- | | matchMediaSignal | query: string | A signal for any raw CSS media query |

Providers

| Provider | Argument | Description | | ----------------------------- | -------------------- | ------------------------------------------------------- | | provideBreakpoints | bps: MqBreakpoints | Registers a custom breakpoint map | | provideTailwindBreakpoints | none | Registers the Tailwind preset | | provideBootstrapBreakpoints | none | Registers the Bootstrap preset | | provideMaterialBreakpoints | none | Registers the Material 2 preset | | provideBreakpointEpsilon | epsilon: number | Sets the exclusive-bound epsilon (default 0.02) | | provideSsrValue | value: boolean | Sets the value signals report during SSR (default false) |

Options and types

interface CreateMediaQueryOptions {
  ssrValue?: boolean; // value reported during SSR; overrides provideSsrValue
  debugName?: string; // shown for the signal in Angular DevTools
}

type MqBreakpoints = Record<string, number>;

type DisplayModeOption =
  | 'browser' | 'fullscreen' | 'standalone'
  | 'minimal-ui' | 'window-controls-overlay' | 'picture-in-picture';

Server-side rendering

matchMedia does not exist on the server, so each signal returns a static value during SSR and switches to the live result after hydration. Set the default with provideSsrValue(true), or override per call with up('lg', { ssrValue: true }).

Contributing

Contributions are welcome. See CONTRIBUTING.md and ARCHITECTURE.md.

License

MIT © Martsin Labs

Sponsors