postcss-prefixwrap

A PostCSS plugin that wraps every CSS rule in a container selector to scope styles safely.

Library
npm
v1.58.0
83stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
48/100Fair
Development Activity32
Maintenance44
Community44
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
79/100Good
Architecture85
Code Quality90
Innovation80
Learning Curve60

postcss-prefixwrap is a PostCSS plugin that prepends a chosen CSS selector to every rule in a stylesheet, effectively wrapping the entire file’s styles so they only apply inside a specific container element. This is the common fix for embedding one site’s or widget’s CSS inside another page without the two style sheets bleeding into each other.

Beyond simple prefixing, the plugin handles the edge cases that make CSS scoping hard in practice: html/body/:root selectors are rewritten into the container class instead of being left dangling, @keyframes percentage selectors are left untouched, and nested rules produced by plugins like postcss-nested are skipped via a configurable nested marker. Whitelist/blacklist options let you scope prefixing to specific files by path, ignoredSelectors exempts specific selectors (strings or regex) from being wrapped, and a prefixTransform callback gives full custom control over how each selector is rewritten.

It supports both the PostCSS v7 and v8 plugin APIs transparently (detecting which one it’s given at runtime) and ships prebuilt for Node.js, Bun, and Deno, with TypeScript types included.

What You Get

  • A single-purpose PostCSS plugin: pass a container selector and it wraps every rule in the processed stylesheet under that selector.
  • Automatic handling of html, body, and :root selectors, which are rewritten into the container rather than left broken.
  • Whitelist and blacklist options (regex-matched file paths) to scope prefixing to specific stylesheets in a build.
  • ignoredSelectors (string or RegExp) to exempt selectors like :root or third-party IDs from being wrapped.
  • A nested option to skip selectors already nested under & when combined with plugins like postcss-nested.
  • A prefixTransform callback for full custom control over how each selector is rewritten, instead of simple string concatenation.
  • Dual support for the PostCSS v7 and v8 plugin APIs, detected automatically at runtime from the postcss instance passed in.

Common Use Cases

  • Embedding a widget’s or third-party component’s stylesheet into a host page without its rules affecting the rest of the page.
  • Publishing a design system or admin panel’s CSS for reuse inside a client’s existing site under a wrapping container class.
  • Isolating legacy CSS so it can be maintained without editing every selector by hand, per the maintainer’s own ‘Maintainable Legacy CSS’ use case.
  • Building multi-tenant or white-label front ends where each tenant’s theme CSS needs to be scoped to a specific container per page.

Under The Hood

Architecture The package is a thin, well-separated pipeline: PostCSSPlugin.ts inspects the postcss instance handed to it (via isPostCSSv8, which checks for a Root export) and dispatches to either PostCSS7Plugin.ts (wrapping the legacy postcss.plugin() factory) or PostCSS8Plugin.ts (returning a plugin object with an Once hook), both of which delegate the actual work to a shared PostCSSPrefixWrap class. That class walks the stylesheet’s rules and hands each one to internal/domain/CSSRuleWrapper.ts, which in turn defers selector-level decisions (root tags, keyframes, ignored selectors, custom transforms) to small pure functions in internal/domain/CSSSelector.ts and file-scoping decisions to FileIncludeList.ts. The domain layer has no dependency on the plugin-adapter layer, so the core rewriting logic is fully decoupled from which PostCSS API version is in use.

Tech Stack Written in TypeScript against the postcss peer dependency (accepting any version via peerDependencies: { postcss: "*" }), built with tsc into a build/ output with bundled .d.ts types, and tested with Jest via ts-jest. Tooling is modern and cross-runtime: the project explicitly targets Node.js, Bun, and Deno, uses pnpm for local development, husky for git hooks, and prettier/sort-package-json for formatting and lint checks enforced in CI.

Code Quality The test suite is extensive relative to the package’s scope: sixteen dedicated spec files cover standard prefixing, blacklist/whitelist file scoping, nested selectors, keyframe percentages, media queries, HTML/body tag replacement, ignored and exact-match selectors, custom prefix transforms, and selectors with special characters, each backed by fixture CSS files comparing raw input against expected output. Types are used throughout the public and internal APIs (PostCSSPrefixWrapOptions, PostcssV7, PostCSSAcceptedPlugin), and CI runs lint, test, and build steps across three separate runtimes (Bun, Deno, and two Node.js versions) on every push and pull request.

What Makes It Unique Rather than treating PostCSS API compatibility as an afterthought, the plugin detects the installed PostCSS major version at runtime and transparently adapts its own plugin shape, so a single published package supports both v7 and v8 consumers without requiring separate installs. Its handling of edge cases most naive selector-prefixing scripts miss — keyframe percentages, root-tag rewriting, nested-selector awareness, and a fully custom transform hook — reflects a library that has been hardened against real-world stylesheet embedding problems over many small releases rather than built once and left alone.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search