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

responsive-sass

v3.0.0

Published

Responsive sass mixins

Readme

responsive-sass

npm version npm downloads License

A library of mixins for creating responsive styles in SASS.

Features

  • 🎯 Non-contiguous ranges - (1,0,1,0,1) targets three separate zones in a single media query. Other libraries need three separate calls and emit three blocks.
  • 👨‍🎨 Multiple grids - You can create multiple grids and use them in the same project.
  • 👩‍🔬 Smart rendering - breakpoints are parsed smart under the hood, (1,1,1,1,1) will just insert value as is without any media query.
  • 📐 No overlapping boundaries - adjacent zones never both match at the exact breakpoint value, so source order can't silently decide the winner.
  • 🕰️ No browser floor - plain min-width/max-width by default; opt into range syntax when your targets allow.
  • ♾️ Unlimited breakpoints - You can create your own mixin from constructor responsive with any number of breakpoints.
  • 📦 Container queries - the same zone syntax targets a container's width via @container, not just the viewport.
  • 🧊 Nothing in your namespace - the package exports two mixins. You name your own.

Requirements

Dart Sass >= 1.23.0 (the release that introduced the module system). LibSass / node-sass are not supported, since they never implemented @use.

Output is plain min-width/max-width, so there is no browser floor beyond media query support itself. If you only target modern browsers, $ranges switches to the shorter Level 4 syntax.

Setup

npm install -D responsive-sass
pnpm add -D responsive-sass
yarn add -D responsive-sass
bun add -d responsive-sass

The package is stylesheets only. Nothing of it reaches runtime, so it belongs in devDependencies.

Usage

The package exports two constructors and nothing else: responsive for @media, and container for @container. Build your own mixin on top. Breakpoints live in one place, so changing the grid is a one-line edit:

@use "responsive-sass" as *;

$breakpoints: 30rem, 45rem, 75rem, 100rem; // 480px, 720px, 1200px, 1600px

@mixin media ($values...) {
    @include responsive ("all", $breakpoints, $values...) {
        @content;
    }
}
div {
    @include media (1,0,0,0,0) {
        background-color: blue;
    }
    @include media (0,0,1,0,1) {
        background-color: yellow;
    }
}

N breakpoints create N + 1 zones, so you pass N + 1 flags. 1 means "apply here", 0 means "skip". The flags above map to: <30rem, 30-45rem, 45-75rem, 75-100rem, >=100rem.

Output

@media (max-width: 29.999rem) {
    div {
        background-color: blue;
    }
}
@media (min-width: 45rem) and (max-width: 74.999rem), (min-width: 100rem) {
    div {
        background-color: yellow;
    }
}

Two details in that output:

  • The all media type is omitted because it is the default.
  • Upper bounds step just below the breakpoint. max-width is inclusive, so an unadjusted max-width: 30rem would match at the same width as the next zone's min-width: 30rem and source order would silently decide the winner. The step is 0.001 for font-relative units and 0.02 for px, both below one device pixel.

Media types

The first argument is the media type, so screen and print variants are the same three lines. They can share the same $breakpoints:

@mixin mediaScreen ($values...) {
    @include responsive ("screen", $breakpoints, $values...) {
        @content;
    }
}

@mixin mediaPrint ($values...) {
    @include responsive ("print", $breakpoints, $values...) {
        @content;
    }
}
div {
    @include mediaScreen (1,1,0,1,1) {
        background-color: blue;
    }
    @include mediaPrint (0,0,1,0,0) {
        background-color: yellow;
    }
}
@media screen and (max-width: 44.999rem), screen and (min-width: 75rem) {
    div {
        background-color: blue;
    }
}
@media print and (min-width: 45rem) and (max-width: 74.999rem) {
    div {
        background-color: yellow;
    }
}

Multiple grids

Nothing is global, so independent grids coexist. A component can have its own breakpoints without touching the page grid:

@mixin mediaSidebar ($values...) {
    @include responsive ("all", (20rem, 60rem), $values...) {
        @content;
    }
}

.sidebar {
    @include mediaSidebar (0,1,0) {
        display: none;
    }
}
@media (min-width: 20rem) and (max-width: 59.999rem) {
    .sidebar {
        display: none;
    }
}

Container queries

container is the same constructor against a container's width instead of the viewport's, so a component can carry its own breakpoints and behave correctly in a wide column and a narrow sidebar alike:

@use "responsive-sass" as *;

.card-wrap { container-type: inline-size; container-name: card; }

@mixin mediaCard ($values...) {
    @include container ("card", (20rem, 40rem, 60rem), $values...) {
        @content;
    }
}

.card {
    @include mediaCard (0,1,0,1) {
        display: grid;
    }
}
@container card ((min-width: 20rem) and (max-width: 39.999rem)) or (min-width: 60rem) {
    .card {
        display: grid;
    }
}

Pass null as the name to match the nearest ancestor container.

Two differences from responsive, both dictated by the CSS grammar:

  • Alternatives are joined with or, not commas. @container does accept comma-separated condition lists per spec, but browsers only started shipping them recently, so or is the portable choice.
  • Two-sided clauses get wrapped in parentheses, because and and or cannot be mixed at the same level.

Container names are <custom-ident>, so none, and, or, not, default and at are rejected by the parser. Passing one of them emits a warning.

Range syntax

If your support target allows it, $ranges: true switches to the Media Queries Level 4 range syntax:

@use "responsive-sass" as * with ($ranges: true);
@media (max-width: 29.999rem)                                    /* default  */
@media (width < 30rem)                                           /* $ranges  */

@media (min-width: 45rem) and (max-width: 74.999rem)             /* default  */
@media (width >= 45rem) and (width < 75rem)                      /* $ranges  */

It is shorter and exact, because the upper bound is genuinely exclusive and needs no epsilon step. The cost is a browser floor: Chrome/Edge 104+, Firefox 63+, Safari 16.4+. A browser that cannot parse the condition discards the entire block, so every rule inside it is lost rather than degraded. Off by default for that reason.