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

postcss-composer

v0.1.3

Published

A PostCSS plugin that provides composable utilities for styling components with custom themes, responsive units, and safe mixin-based variants — Made for scalable UI design.

Readme

postcss-composer

A PostCSS plugin that provides composable utilities for scalable, theme-aware styling. — Made for scalable UI design. Includes color mode conversion, dynamic theming, mixins, and scale-based units — AST-aware & Tailwind-friendly.


✨ Features

  • ✅ Convert rem(), em(), and scale() units to responsive expressions
  • 🎨 Parse rgb(), hsl(), lab(), oklch(), etc. with contextual var() resolution
  • 🌗 Theme switching with themes() and @mixin dark, @mixin light, etc.
  • 🧩 Mixins for hover, ltr/rtl, breakpoints, and more
  • ⚙️ Fully configurable via postcss.config.js
  • 🎯 Tailwind-compatible with support for arbitrary values and custom variants
  • 🔍 AST-aware — resolves nested and inline variables properly

Required peer dependencies:

  • postcss
  • postcss-mixins
  • postcss-nested
  • postcss-values-parser

🚀 Installation

Install postcss-composer and required PostCSS plugins:

npm install postcss-composer postcss postcss-mixins postcss-nested postcss-values-parser -D

⚙️ Configuration

Add postcss-composer to your postcss.config.mjs:

export default {
  plugins: ['@tailwindcss/postcss', 'autoprefixer', 'postcss-composer']
};

With Option

export default {
  plugins: {
    '@tailwindcss/postcss': {},
    autoprefixer: {},
    'postcss-composer': {
      'themes-attr': 'class', // Change to match your theme attribute (e.g. 'data-theme', 'color-scheme')
      // Optional: register custom plugins or mixins
      plugins: {
        'postcss-import': {},
        'postcss-custom-media': { preserve: false }
      },
      // plugins: ['postcss-import', ['postcss-custom-media', { preserve: false }]] // Alternative syntax
      mixins: {
        // Register custom mixin
        customHover: {
          '@media (hover: hover)': {
            '&:hover': {
              '--color': '#f00',
              '@mixin-content': {} // Use the mixin content
            }
          }
        }
      }
    }
  }
};

Note:

  • themes-attr: The attribute name for theme switching (e.g. class, data-theme, color-scheme), default value is 'class'.
  • plugins: An array of PostCSS plugins to be used for theme switching. You can us the same syntax as in the postcss.config.mjs file.
  • mixins: An object with custom mixin names as keys and mixin functions as values. These mixins will be available in your CSS files.

💡 Usage

Once configured, you can use dynamic functions like scale(), rem(), em(), and themes() in your CSS:

/* css */
:root {
  --muted: #151619;
  --foreground: #18191d;
  --emphasis: #171717;
}

.btn {
  color: var(--emphasis); /* #171717 */
  border-color: hls(var(--muted) / 0.5);
  /* Converts to: hsl(225deg 10% 9% / 0.5) — currently supports rgb(), hsl(), hwb(), oklch() */
  background-color: themes(rgb(26 27 30), var(--foreground));
  /* Resolves value based on theme context (e.g. dark/light/class) */
  font-size: rem(32); /* → calc(2rem * var(--scale, 1)) */
  letter-spacing: em(4); /* → calc(0.25em * var(--scale, 1)) */
}

🎨 Color conversion

Convert contextual CSS variables (even nested or fallback!) using any culori mode:

/* css */
:root {
  --brand: #1a1b1e;
}

.card {
  background: hsl(var(--brand) / 0.4); /* ✅ context-aware */
  border-color: oklch(var(--brand, #333) / 0.2); /* ✅ fallback supported */
  color: lab(var(--brand, #111, #fff) / 0.5); /* ✅ multi-level fallback */
}

Supported color formats (culori Mode):

'a98' | 'cubehelix' | 'dlab' | 'dlch' | 'hsi' | 'hsl' | 'hsv' | 'hwb' | 'itp' | 'jab' | 'jch' | 'lab' | 'lab65' | 'lch' | 'lch65' | 'lchuv' | 'lrgb' | 'luv' | 'okhsl' | 'okhsv' | 'oklab' | 'oklch' | 'p3' | 'prophoto' | 'rec2020' | 'rgb' | 'xyb' | 'xyz50' | 'xyz65' | 'yiq';

🧩 Mixins

postcss-composer includes helpful mixins to handle interaction state and layout direction.

  • hover: Uses :hover or :active depending on device
  • ltr: Styles for LTR mode only
  • rtl: Styles for [dir="rtl"]
  • light: Styles scoped to [themes-attr="light"]
  • dark: Styles scoped to [themes-attr="dark"]

CSS variables

/* variables */

:root,
:host {
  @mixin light-root {
    --muted: #f0f0f0;
    --foreground: #202020;
    --emphasis: #000000;
    /* ...others */
  }
  @mixin dark-root {
    --muted: #1a1a1a;
    --foreground: #e0e0e0;
    --emphasis: #ffffff;
    /* ...others */
  }
}

CSS class

/* css */

.btn {
  font-size: rem(16);

  @mixin hover {
    &:not([data-loading]):not(:disabled):not([data-disabled]) {
      --muted: #cccccc;
      --foreground: #eeeeee;
      --emphasis: #ffffff;
    }
  }

  @mixin light {
    --muted: #f0f0f0;
    --foreground: #202020;
    --emphasis: #000000;
  }
  @mixin dark {
    --muted: #1a1a1a;
    --foreground: #e0e0e0;
    --emphasis: #ffffff;
  }

  @mixin ltr {
    margin-left: auto;
  }
  @mixin rtl {
    margin-right: auto;
  }

  @mixin max 768px {
    text-align: center;
  }
  @mixin min 768px {
    text-align: start;
  }
}

📐 Scale & Unit Helpers

scale(...) function can support formats like:

/* css */
.selector {
  font-size: rem(32px); /* → calc(2rem * var(--scale, 1)) */
  letter-spacing: em(4px); /* → calc(0.25em * var(--scale, 1)) */
  padding: scale(32px); /* → calc(2rem * 1) */
  gap: scale(24px, 3); /* → calc(1.5rem * 3) */
  width: scale(10rem, screen); /* → calc(10rem * var(--screen-scale, 1)) */
}

Examples scale function

| Input CSS | Output CSS | | --------------------------------- | -------------------------------------------- | | scale(32) | calc(2rem * 1) | | scale(32, 3) | calc(2rem * 3) | | scale(32, large) | calc(2rem * var(--large-scale, 1)) | | scale(32, --large) | calc(2rem * var(--large-scale, 1)) | | scale(32, -large--scale) | calc(2rem * var(--large-scale, 1)) | | scale(32, large-screen) | calc(2rem * var(--large-screen-scale, 1)) | | scale(32, --large-screen) | calc(2rem * var(--large-screen-scale, 1)) | | scale(32, -large-screen--scale) | calc(2rem * var (--large-screen-scale, 1)) |

🌙 Theme Context

Resolve theme-aware values based on attribute context:

/* css */
.card {
  background: themes(#1a1b1e, var(--fg)); /* → themes(light, dark) */
  /* → resolves to --fg based on .dark or [data-theme="dark"] etc. */
}

Customizable via themes-attr config (e.g. class, data-theme, etc.)

🌀 TailwindCSS Compatibility

Works seamlessly inside Tailwind CSS with arbitrary value support:

// file.tsx
<span className="text-[themes(#1a1b1e,#fff)] [@mixin_ltr]:mr-auto" />

🧪 TailwindCSS v4

/* globals.css */
@import 'tailwindcss';

@custom-variant mixin-light (@mixin light);
@custom-variant mixin-dark (@mixin dark);
@custom-variant mixin-ltr (@mixin ltr);
@custom-variant mixin-rtl (@mixin rtl);

@utility color-* {
  color: themes(--value([ *]));
}
@utility background-* {
  background: themes(--value([ *]));
}
// file.tsx
<span className="color-[#1a1b1e,#fff] mixin-ltr:mr-auto" />

🛠️ Technical Notes

  • 🎯 var() resolution:
    • Supports deeply nested values (e.g. var(--a, var(--b, #fff)))
    • Context-aware: resolves from closest rule → parent → root
  • 🎨 Color handling:
    • Uses culori under the hood — all formats supported
    • Auto-formats based on target function (hsl(), rgb(), hwb(), oklch(), etc.)
  • 🧩 @mixin support:
    • The @mixin directive is used to define a mixin, which is a reusable block of CSS code.
    • @mixin hover: will adjust between hover and active depending on the device (@media (hover: hover) vs none)
    • @mixin light & @mixin dark: take the themes-attr value you configured, so it's flexible and can be used with any theme.
  • 📌 No caching to preserve dynamicity (e.g. :hover, @media, inline overrides)
    • The current approach avoids caching to allow for dynamic contexts like :hover, :active, @media, and inline styles ([--var:...]) that can produce different results depending on the DOM state. Caching is premature and can limit user experience and developer flexibility — the first thought was to keep the plugin AST-aware and context-sensitive. So yes, keep it uncached for now.

🤝 Contributing

Want to help improve postcss-composer? Check out the contribution guide


📄 License

MIT © Ilham Khoeri