JSPM

postcss-composer

0.0.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 4
  • Score
    100M100P100Q69322F
  • License MIT

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.

Package Exports

  • postcss-composer
  • postcss-composer/package.json

Readme

postcss-composer logo

postcss-composer

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.


✨ Features

  • Convert rem(), em() to scalable expressions
  • Use themes() for conditional styling (e.g. dark/light/class attribute)
  • Built-in support for rgb(), hsl(), hwb(), oklch() with alpha manipulation
  • Mixins for hover, rtl, theme switching, and more
  • Clean and scalable configuration
  • Supports custom design systems with minimal setup
  • Simple CSS abstraction for scalable design systems

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: {
    'postcss-composer': {
      'themes-attr': 'class' // Change to match your theme attribute (e.g. 'data-theme', 'color-scheme')
    }
  }
};

Options

  • themes-attr: The attribute name for theme switching (e.g. class, data-theme, color-scheme)
  • 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.
const customHoverMixin = {
  '@media (hover: hover)': {
    '&:hover': {
      color: 'red',
      '@mixin-content': {} // Use the mixin content
    }
  }
};

export default {
  plugins: {
    'postcss-composer': {
      'themes-attr': 'class',
      plugins: {
        'postcss-import': {},
        'postcss-custom-media': { preserve: false }
      },
      // plugins: ['postcss-import', ['postcss-custom-media', { preserve: false }]] // Alternative syntax
      mixins: {
        customHover: customHoverMixin // Register custom mixin
      }
    }
  }
};

💡 Usage

Once configured, you can use dynamic functions like scale(), rem(), em(), themes(), and hls() in your 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)) */
}

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

  • scale(32) → calc(2rem * var(--scale, 1))
  • scale(32px, 2) → calc(2rem * var(--scale, 2))
  • scale(16px, 4) → calc(1rem * var(--scale, 4))
/* Input */
.selector {
  font-size: rem(32px);
  margin: em(16px);
  padding: scale(32px); /* default scale = 1 */
  gap: scale(24px, 3); /* custom scale = 3 */
}

/* Output */
.selector {
  font-size: 2rem;
  margin: 1em;
  padding: calc(2rem * var(--scale, 1));
  gap: calc(1.5rem * var(--scale, 3));
}

Examples scale()

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))

TailwindCSS Compatibility

Works seamlessly inside Tailwind CSS with arbitrary value support:

<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([ *]));
}
// components/MyComponent.tsx
<span className="color-[#1a1b1e,#fff] mixin-ltr:mr-auto" />

🧩 Mixins

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

Supported Mixins

  • 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"]

Example

.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;
    margin-right: unset;
  }
  @mixin rtl {
    margin-right: auto;
    margin-left: unset;
  }

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

Technical Notes

  • @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
  • @mixin-content value is used dynamically, allowing nested rules without repetition

🤝 Contributing

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


📄 License

MIT © Ilham Khoeri