Skip to content

koga73/dynamic-css-plugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dynamic CSS Plugin

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:

  1. Patch into the framework's DOM rendering setAttribute method to intercept class names as they are applied to elements
  2. Inject a custom setAttribute method into the bundle that runs the transformation logic
  3. Transform class names at run-time and cooresponding CSS at build-time

Getting Started

Install

npm install dynamic-css-plugin -D

Vite

vite.config.js

Import into Vite config

import dynamicCssPlugin from "dynamic-css-plugin/vite";

Add to Vite plugins

plugins: [
	dynamicCssPlugin({
		transform: "app_[md4:hash:base64:5]"
	})
];

Full Vite example


Webpack

webpack.config.js

Import into Webpack config

import DynamicCssPlugin from "dynamic-css-plugin/webpack";

Add to Webpack plugins

plugins: [
	new DynamicCssPlugin({
		transform: "app_[md4:hash:base64:5]"
	})
];

Full Webpack example

Options

  • 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

  • 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

  • inject: {} - The injection config

    • inject.entryPoint: undefined The 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

  • patch: PatchReactDom - The patch function

    import 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,`
    	};
    }

Notes

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

Limitations

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.

About

Vite / Webpack - plugin to transform dynamic css class names on-the-fly

Topics

Resources

License

Stars

Watchers

Forks