Patch. Inject. Transform.
"btn-primary btn-primary-disabled" ==> .app_Xscyf.app_LfRuA
Supported frameworks:
Advantages:
- 👌 Prevent conflicts with third party CSS
- ❤️🔥 Smaller bundle size
- 💥 Obfuscate class names
How it works:
- Patch into the framework's DOM rendering setAttribute method to intercept class names as they are applied to elements
- Inject a custom setAttribute method into the bundle that runs the transformation logic
- Transform class names at run-time and cooresponding CSS at build-time
npm install dynamic-css-plugin -Dvite.config.js
import dynamicCssPlugin from "dynamic-css-plugin/vite";plugins: [
dynamicCssPlugin({
transform: "app_[md4:hash:base64:5]"
})
];webpack.config.js
import DynamicCssPlugin from "dynamic-css-plugin/webpack";plugins: [
new DynamicCssPlugin({
transform: "app_[md4:hash:base64:5]"
})
];-
enabled: true- If false, the plugin will not run -
scope: ""- Applies a scope to the injected method. Useful if multiple instances are on the same page such as with micro-frontends -
debug: false- Enables debug logging
-
transform: "[md4:hash:base64:5]"- The transform template or config object -
transform: {}- The transform config object-
transform.template: "[md4:hash:base64:5]"- The format of transformed class names- "prefix_[{algorithm}:hash:{encoding}:{length}]"
-
transform.attributes: /^(class)$/- Regex for HTML attributes to transform -
transform.ignoreTags: /(path)/i- Regex for HTML tags to ignore -
transform.ignoreValues: /^(css|sc|icon)-/i- Regex for HTML attribute values to ignore -
transform.ignoreFiles: /\.notransform\.(css|scss)$/i- Regex for files to ignore
-
-
inject: {}- The injection config-
inject.entryPoint: undefinedThe entry point for the injected method in the bundle, if undefined the first entry is used -
inject.src: "./src/inject/index.js"The code to apply options and generate the file to inject -
inject.file: "./src/inject/index_generated.js"The code to inject
-
-
patch: PatchReactDom- The patch functionimport PatchReactDom from "dynamic-css-plugin/patch/react-dom";
Patch method signature:
function PatchReactDom(scope = "") { return { test: /react-dom/, search: /(\w+)\.setAttribute\(/g, replace: `globalThis['${scope}setAttributeDynamic'].call($1,` }; }
ignoreValues default excludes class names with the following prefixes that are commonly generated by other libraries and should not be transformed
- css- which is the emotion default
- sc- which is the styled-components default
- icon- which is the the standard for icon classes
Since we are only patching calls to "setAttribute" inside react-dom that means if a class is applied a different way such as with vanilla JS or using classList the transform will not apply. This is largely ok since these patterns don’t necessarily follow the "react way of doing things". However if you do need to support these patterns you can use the injected method directly.
For example:
import DynamicCss from "dynamic-css-plugin";
document.body.classList.add(DynamicCss("theme-light"));To avoid issues, the plugin supports options to ignore specific tags and values. Additionally the plugin does not transform ids, this is done purposely so that tests and analytics can still reliably target elements.