Feature request
@swc/plugin-styled-components already accepts a namespace option. As implemented today (12.9.0), namespace prefixes the styled-components componentId hash to avoid cross-instance collisions in micro-frontend setups. That's useful, but it is a different problem from what the babel ecosystem covers with @quickbaseoss/babel-plugin-styled-components-css-namespace, which emits a parent CSS selector before every styled-component class — .foo becomes .<ns> .foo — increasing specificity and isolating styles from legacy CSS that lives in the host page.
I'd like an additional option (or a separate plugin) that produces the parent-selector behavior at the swc layer. Sketch:
Use case
We run 11 micro-frontend React apps mounted into a single portal host page that also serves legacy non-styled-components CSS. Three of the apps depend on the babel parent-selector plugin today to isolate their styles from the host stylesheet. Migrating all 11 apps off babel-loader to swc-loader is held up by exactly these three apps — the other 8 already run on swc successfully (~3.8× faster cold build, -44% main bundle size in our pilot).
The plugin's parent-selector behavior is small and well-specified:
- For every
styled.X and styled(X) invocation, wrap the runtime-injected CSS so that all selectors are prefixed by .<cssNamespace> (note the trailing space — descendant combinator, not concatenation).
- No componentId mutation — that's covered by the existing
namespace option.
- No effect on
styled-components global styles or createGlobalStyle (existing babel plugin scopes only component-level styles).
Why a separate option (not overloading namespace)
- The two behaviors are independent and frequently want different values per app.
- The existing
namespace value affects bundle output bytes deterministically; layering parent-selector emission on top of the same key would silently change behavior for current users.
- Easy to deprecate the babel plugin once a 1:1 swc analog exists, since adopters can flip with a config change.
What I tried
- The current
namespace option (does not emit parent selectors — verified by grep on production bundle: zero matches for .<ns> pattern in swc output, 97+ matches in babel-built equivalent app).
- Routing affected files to a separate babel-loader rule via webpack — works but defeats the speed win and keeps
babel-loader + babel-plugin-styled-components + @quickbaseoss/babel-plugin-styled-components-css-namespace in the dep tree for the whole monorepo.
Versions
@swc/core: 1.15.33
@swc/plugin-styled-components: 12.9.0
swc-loader: 0.2.7
styled-components: 5.3.3
- Node: 22.x
- OS: macOS / Linux CI runners
Happy to contribute
If the maintainers point at where in the Rust plugin source the per-component CSS emission lives, I can attempt a draft PR. Reference implementation (babel): https://github.com/QuickBase/babel-plugin-styled-components-css-namespace/blob/master/index.js (small file, ~60 LOC).
Feature request
@swc/plugin-styled-componentsalready accepts anamespaceoption. As implemented today (12.9.0),namespaceprefixes the styled-components componentId hash to avoid cross-instance collisions in micro-frontend setups. That's useful, but it is a different problem from what the babel ecosystem covers with@quickbaseoss/babel-plugin-styled-components-css-namespace, which emits a parent CSS selector before every styled-component class —.foobecomes.<ns> .foo— increasing specificity and isolating styles from legacy CSS that lives in the host page.I'd like an additional option (or a separate plugin) that produces the parent-selector behavior at the swc layer. Sketch:
{ "experimental": { "plugins": [ ["@swc/plugin-styled-components", { "displayName": false, "ssr": true, "minify": true, "namespace": "myapp", // existing: componentId prefix "cssNamespace": "myapp" // proposed: emit `.myapp .foo` }] ] } }Use case
We run 11 micro-frontend React apps mounted into a single portal host page that also serves legacy non-styled-components CSS. Three of the apps depend on the babel parent-selector plugin today to isolate their styles from the host stylesheet. Migrating all 11 apps off
babel-loadertoswc-loaderis held up by exactly these three apps — the other 8 already run on swc successfully (~3.8× faster cold build, -44% main bundle size in our pilot).The plugin's parent-selector behavior is small and well-specified:
styled.Xandstyled(X)invocation, wrap the runtime-injected CSS so that all selectors are prefixed by.<cssNamespace>(note the trailing space — descendant combinator, not concatenation).namespaceoption.styled-componentsglobal styles orcreateGlobalStyle(existing babel plugin scopes only component-level styles).Why a separate option (not overloading
namespace)namespacevalue affects bundle output bytes deterministically; layering parent-selector emission on top of the same key would silently change behavior for current users.What I tried
namespaceoption (does not emit parent selectors — verified by grep on production bundle: zero matches for.<ns>pattern in swc output, 97+ matches in babel-built equivalent app).babel-loader+babel-plugin-styled-components+@quickbaseoss/babel-plugin-styled-components-css-namespacein the dep tree for the whole monorepo.Versions
@swc/core: 1.15.33@swc/plugin-styled-components: 12.9.0swc-loader: 0.2.7styled-components: 5.3.3Happy to contribute
If the maintainers point at where in the Rust plugin source the per-component CSS emission lives, I can attempt a draft PR. Reference implementation (babel): https://github.com/QuickBase/babel-plugin-styled-components-css-namespace/blob/master/index.js (small file, ~60 LOC).