https://github.com/josedacosta/tailwindcss-obfuscator
Tailwind CSS class obfuscation for Vite, Webpack, Rollup, esbuild, Next.js, Nuxt, SvelteKit, Astro, Remix / React Router 7, Solid, Qwik, Vue & TanStack Router โ supports Tailwind v3 + v4. Shrinks bundles, protects your design system.
https://github.com/josedacosta/tailwindcss-obfuscator
astro css esbuild mangle minify nextjs nuxt obfuscator qwik react-router rollup solidjs sveltekit tailwind tailwindcss tanstack-router typescript vite vue webpack
Last synced: 16 days ago
JSON representation
Tailwind CSS class obfuscation for Vite, Webpack, Rollup, esbuild, Next.js, Nuxt, SvelteKit, Astro, Remix / React Router 7, Solid, Qwik, Vue & TanStack Router โ supports Tailwind v3 + v4. Shrinks bundles, protects your design system.
- Host: GitHub
- URL: https://github.com/josedacosta/tailwindcss-obfuscator
- Owner: josedacosta
- License: mit
- Created: 2026-04-28T14:34:55.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-06-01T23:01:32.000Z (24 days ago)
- Last Synced: 2026-06-02T00:23:45.364Z (24 days ago)
- Topics: astro, css, esbuild, mangle, minify, nextjs, nuxt, obfuscator, qwik, react-router, rollup, solidjs, sveltekit, tailwind, tailwindcss, tanstack-router, typescript, vite, vue, webpack
- Language: TypeScript
- Homepage: https://github.com/josedacosta/tailwindcss-obfuscator#readme
- Size: 1.43 MB
- Stars: 2
- Watchers: 0
- Forks: 0
- Open Issues: 26
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Citation: CITATION.cff
- Codeowners: .github/CODEOWNERS
- Security: SECURITY.md
- Maintainers: docs/maintainers.md
Awesome Lists containing this project
README
๐ก๏ธ Tailwind CSS Obfuscator
(a.k.a. Tailwind Class Mangler)
Mangle Tailwind classes. Shrink your bundles 30โ60%. Protect your design system.
The most complete Tailwind CSS class mangler / obfuscator on npm โ
built for Tailwind v3 & v4, every major framework, every build tool.
Both terms describe the same build-time transformation : rewrite verbose utilities (bg-blue-500) into short opaque ones (tw-a).
ย ๐ Quick Start ย
ย
ย ๐ Docs ย
ย
ย ๐ฆ Package ย
ย
ย ๐ก Examples ย
ย
ย ๐ Report Bug ย
> [!IMPORTANT]
> ๐ฅ **What if a single line in your `vite.config.js` could shrink your CSS by 30โ60% and make your design system uncopyable?**
> That's exactly what `tailwindcss-obfuscator` does โ at build time, with zero runtime overhead.
## ๐ Documentation
### ๐ [**josedacosta.github.io/tailwindcss-obfuscator**](https://josedacosta.github.io/tailwindcss-obfuscator/) ๐
Setup guide for every framework ยท complete options reference ยท the patterns that obfuscate (and the ones that don't) ยท maintainers' checklist ยท comparison with `tailwindcss-mangle`.
| ๐ [**Live docs site**](https://josedacosta.github.io/tailwindcss-obfuscator/) | ๐ [Docs source on GitHub](./docs/) |
| ------------------------------------------------------------------------------ | ----------------------------------- |
| Hosted on GitHub Pages, rebuilt on every push to `main` | Edit a page, open a PR |
## ๐ Table of Contents
**Get started**
- [โจ What is Class Obfuscation?](#-what-is-class-obfuscation)
- [๐ฏ Why this library?](#-why-this-library)
- [โก Performance impact](#-performance-impact)
- [๐ Quick Start](#-quick-start)
- [๐ Supported Frameworks](#-supported-frameworks)
**Deep dive**
- [๐๏ธ Architecture](#๏ธ-architecture)
- [๐จ Tailwind v3 & v4](#-tailwind-css-version-support)
- [โ ๏ธ Static Classes Only](#๏ธ-important-static-classes-only)
- [๐ ๏ธ Development](#๏ธ-development)
- [๐บ๏ธ Roadmap](#๏ธ-roadmap)
## โจ What is Class Obfuscation?
Class obfuscation (also called **"class mangling"**) is a build-time transformation that replaces verbose Tailwind utility classes with short, opaque identifiers.
> [!NOTE]
> ๐ก **Build-time only** โ your source code stays readable. Only the shipped HTML / CSS / JS bundles are obfuscated.
### ๐ Before & After
#### ๐ฌ Before
```html
Click me
```
๐ **142 bytes**
#### ๐ After
```html
Click me
```
๐ **86 bytes** โก **โ39%**
### ๐ What you gain
### ๐
**Design system
protection**
Make your component patterns much harder to reverse-engineer
### ๐
**Smaller
bundles**
30โ60% reduction on CSS-heavy pages, even after Brotli/gzip
### ๐ต๏ธ
**Hidden
internals**
Hide which design tokens, breakpoints, plugins you use
### โก
**Faster
parsing**
Browser parses smaller selectors โ shorter style recalc
## ๐ฏ Why this library?
There are a handful of class-mangling tools out there. Here's how this one stacks up against every active competitor โ `tailwindcss-mangle`, `Obfustail`, PostCSS minifiers, and Tailwind itself:
| Capability | ๐ก๏ธย **tailwindcss-obfuscator** | ๐งย [tailwindcss-mangle](https://github.com/sonofmagic/tailwindcss-mangle) | ๐ย [Obfustail](https://github.com/ui-layouts/Obfuscated-tailwindcss) | โ๏ธย PostCSS minifiers ([cssnano](https://github.com/cssnano/cssnano), [csso](https://github.com/css/csso)) | ๐
ย [Tailwind CSS](https://github.com/tailwindlabs/tailwindcss) itself |
| --------------------------------------------------------------------- | :--------------------------------: | :----------------------------------------------------------------------------: | :-----------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------: |
| Tailwind v4 (CSS-first) support | โ
Native | โ
via CSS scan (v9) | โ
v4 only | n/a | โ
|
| Tailwind v3 (config-file) support | โ
| โ
| โ | n/a | โ
|
| Renames classes (HTML / JS / CSS) | โ
| โ
| โ
| โ | โ |
| Doesn't modify your source files | โ
| โ
| โ rewrites in place | โ
| โ
|
| Per-utility obfuscation (vs. per-string) | โ
| โ
| โ per-full-string | n/a | n/a |
| Unified `unplugin` core (Vite/Webpack/Rollup/esbuild/Rspack/Farm) | โ
All six | โ ๏ธ Vite + Webpack only | โ build-time script | โ | โ |
| AST-based JSX/TSX transformer | โ
Babel | โ ๏ธ Regex | โ Regex | n/a | n/a |
| Vue SFC + Svelte `class:` directive | โ
| โ ๏ธ Partial | โ | n/a | n/a |
| `cn()` / `clsx()` / `classnames()` / `twMerge()` / `cva()` / `tv()` | โ
All six | โ ๏ธ Two | โ Manual `safelist` | โ | โ |
| Type-safe options + typed errors | โ
Strict TS | โ ๏ธ Loose | โ Pure JS | n/a | n/a |
| Source maps for transformed files | โ
| โ ๏ธ | โ | โ
| โ
|
| Reversible mapping file emitted | โ
| โ
| โ
| โ | โ |
| Standalone CLI (any project) | โ
`tw-obfuscator` | โ
`tw-patch` | โ inline node script | โ
| โ
|
| Per-build randomization (no global state) | โ
| โ | โ
| n/a | n/a |
| Tailwind config validator | โ
| โ | โ | โ | โ |
| Active framework coverage | **20+ apps** | ~5 | 1 (Next.js) | n/a | n/a |
| **Unquoted HTML attributes** (`class=foo`, HTML5 spec) | โ
since v2.0.1 | โ | โ | n/a | n/a |
| **Next.js Turbopack** (post-build CLI workaround documented + tested) | โ
since v2.0.1 | โ | โ ๏ธ accidental (Next.js + Turbo) | n/a | n/a |
| **npm publish with provenance** (Sigstore / OIDC attestation) | โ
| โ | โ | varies | n/a |
| **OpenSSF Scorecard published** | โ
weekly | โ | โ | n/a | n/a |
| **SBOM (SPDX-JSON) attached to every GitHub release** | โ
| โ | โ | n/a | n/a |
> [!NOTE]
> ๐ **Want the methodology, version numbers, and per-tool deep-dive?** See the [full comparison page](https://josedacosta.github.io/tailwindcss-obfuscator/research/comparison) โ every cell above is sourced from the latest release of each project (April 2026).
## โก Performance impact
Real numbers measured on the included test apps (production builds, gzip):
| App | CSS size before | CSS size after | Reduction |
| ----------------------------------- | :-------------: | :------------: | :-----------: |
| `test-vite-react` (small dashboard) | 24.1 KB | 16.7 KB | ๐ข **โ30.7%** |
| `test-shadcn-ui` (CVA-heavy) | 47.8 KB | 28.4 KB | ๐ข **โ40.6%** |
| `test-nextjs` (marketing site) | 68.9 KB | 32.1 KB | ๐ข **โ53.4%** |
| `test-nuxt` (blog template) | 41.2 KB | 22.8 KB | ๐ข **โ44.7%** |
| `test-static-html` (landing page) | 18.6 KB | 8.9 KB | ๐ข **โ52.2%** |
> [!TIP]
> ๐ธ The bigger your CSS bundle, the bigger the savings. Apps that ship full Tailwind v3 with `darkMode`, `safelist`, and many variants tend to gain the most.
## ๐ Quick Start
### ๐ฆ Install
```bash
# pnpm (recommended)
pnpm add tailwindcss-obfuscator
# npm
npm install tailwindcss-obfuscator
# yarn
yarn add tailwindcss-obfuscator
# bun
bun add tailwindcss-obfuscator
```
โก ย Vite ย (React, Vue, Svelte, Solid, Astro, Remix, Qwik)
```javascript
// vite.config.js
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
import { tailwindCssObfuscatorVite } from "tailwindcss-obfuscator/vite";
export default defineConfig({
plugins: [
tailwindcss(),
tailwindCssObfuscatorVite({
prefix: "tw-",
}),
],
});
```
๐ ย Next.js ย (Webpack)
```javascript
// next.config.js
import { tailwindCssObfuscatorWebpack } from "tailwindcss-obfuscator/webpack";
const nextConfig = {
webpack: (config, { dev }) => {
if (!dev) {
config.plugins.push(
tailwindCssObfuscatorWebpack({
prefix: "tw-",
})
);
}
return config;
},
};
export default nextConfig;
```
๐ข ย Nuxt 3
```javascript
// nuxt.config.ts
export default defineNuxtConfig({
modules: ["tailwindcss-obfuscator/nuxt"],
tailwindcssObfuscator: {
prefix: "tw-",
},
});
```
๐ฆ ย Rollup
```javascript
// rollup.config.js
import { tailwindCssObfuscatorRollup } from "tailwindcss-obfuscator/rollup";
export default {
plugins: [tailwindCssObfuscatorRollup({ prefix: "tw-" })],
};
```
โก ย esbuild
```javascript
// build.js
import * as esbuild from "esbuild";
import { tailwindCssObfuscatorEsbuild } from "tailwindcss-obfuscator/esbuild";
await esbuild.build({
entryPoints: ["src/index.ts"],
bundle: true,
outdir: "dist",
plugins: [tailwindCssObfuscatorEsbuild({ prefix: "tw-" })],
});
```
๐ฅ๏ธ ย CLI ย (any build system)
```bash
# Extract + transform in one shot
npx tw-obfuscator run --build-dir dist
# Preview without writing files
npx tw-obfuscator run --dry-run
# Two-step workflow
npx tw-obfuscator extract
npx tw-obfuscator transform --dir dist
# Inspect a generated mapping
npx tw-obfuscator show --limit 50
```
> [!TIP]
> ๐ See the [package README](./packages/tailwindcss-obfuscator/README.md) for **all** options, framework recipes, and advanced customization (custom name generators, `preserve.classes`, validators...).
## ๐ Supported Frameworks
| Framework | Version | Plugin | Status | Test App |
| ------------------------------- | -------- | --------------------------------------- | :-------: | ---------------------------------------------------------------- |
| โ๏ธ ย **React** (Vite) | 19 | `tailwindcss-obfuscator/vite` | ๐ข Tested | [`apps/test-vite-react`](./apps/test-vite-react) |
| โฒ ย **Next.js** | 16 | `tailwindcss-obfuscator/webpack` | ๐ข Tested | [`apps/test-nextjs`](./apps/test-nextjs) |
| ๐ ย **Vue** (Vite) | 3.5 | `tailwindcss-obfuscator/vite` | ๐ข Tested | [`apps/test-vite-vue`](./apps/test-vite-vue) |
| ๐ข ย **Nuxt** | 4 | `tailwindcss-obfuscator/nuxt` | ๐ข Tested | [`apps/test-nuxt`](./apps/test-nuxt) |
| ๐ฅ ย **SvelteKit / Svelte** | 2.58 / 5 | `tailwindcss-obfuscator/vite` | ๐ข Tested | [`apps/test-sveltekit`](./apps/test-sveltekit) |
| ๐ฆ ย **Solid.js** | 1.9 | `tailwindcss-obfuscator/vite` | ๐ข Tested | [`apps/test-solidjs`](./apps/test-solidjs) |
| ๐ ย **Astro** | 6 | `tailwindcss-obfuscator/vite` | ๐ข Tested | [`apps/test-astro`](./apps/test-astro) |
| ๐งญ ย **React Router** (SSR) | v7 | `tailwindcss-obfuscator/vite` | ๐ข Tested | [`apps/test-react-router`](./apps/test-react-router) |
| ๐ชต ย **TanStack Router** | 1.168 | `tailwindcss-obfuscator/vite` | ๐ข Tested | [`apps/test-tanstack-start`](./apps/test-tanstack-start) |
| โก ย **Qwik** | 1.19 | `tailwindcss-obfuscator/vite` | ๐ข Tested | [`apps/test-qwik`](./apps/test-qwik) |
| ๐จ ย **shadcn/ui** (CVA) | latest | `tailwindcss-obfuscator/webpack` | ๐ข Tested | [`apps/test-shadcn-ui`](./apps/test-shadcn-ui) |
| ๐ ย **Static HTML** | โ | `tailwindcss-obfuscator/esbuild` or CLI | ๐ข Tested | [`apps/test-static-html`](./apps/test-static-html) |
| ๐งฐ ย **Webpack** standalone | 5.106 | `tailwindcss-obfuscator/webpack` | ๐ข Tested | [`apps/test-webpack-standalone`](./apps/test-webpack-standalone) |
| ๐ฆ ย **Rollup** standalone | 4.60 | `tailwindcss-obfuscator/rollup` | ๐ข Tested | [`apps/test-rollup-standalone`](./apps/test-rollup-standalone) |
## ๐๏ธ Architecture
The obfuscator runs in three phases. Every build tool plugin shares the same core pipeline thanks to a unified [`unplugin`](https://github.com/unjs/unplugin) factory:
```mermaid
flowchart LR
A[๐ Source files
JSX, Vue, Svelte, HTML, CSS] -->|extract| B[๐ Extractors
regex + AST]
B --> C[๐บ๏ธ Class Map
Map<original, obfuscated>]
C -->|transform| D[โ๏ธ Transformers
JSX-AST ยท PostCSS ยท HTML]
D --> E[๐ฆ Output bundle
obfuscated CSS, JS, HTML]
C -.->|persist| F[๐พ .tw-obfuscation/
class-mapping.json]
style A fill:#1e293b,stroke:#38bdf8,color:#fff
style B fill:#1e293b,stroke:#10b981,color:#fff
style C fill:#1e293b,stroke:#eab308,color:#fff
style D fill:#1e293b,stroke:#f97316,color:#fff
style E fill:#1e293b,stroke:#8b5cf6,color:#fff
style F fill:#1e293b,stroke:#64748b,color:#fff
```
๐งฉ ย Internal module map
```mermaid
graph TB
subgraph Plugins[" ๐ Build tool plugins "]
Vite["vite.ts"]
Webpack["webpack.ts"]
Rollup["rollup.ts"]
Esbuild["esbuild.ts"]
Nuxt["nuxt.ts"]
end
subgraph Core[" โ๏ธ unplugin core "]
UnPlugin["plugins/core.ts
(shared factory)"]
end
subgraph Pipeline[" ๐ Pipeline "]
Ctx["core/context.ts
(state)"]
Extract["extractors/*"]
Transform["transformers/*"]
Patterns["core/patterns/*
(regex ยท variants ยท validators)"]
Errors["core/errors.ts
(typed exceptions)"]
end
Vite --> UnPlugin
Webpack --> UnPlugin
Rollup --> UnPlugin
Esbuild --> UnPlugin
Nuxt --> UnPlugin
UnPlugin --> Ctx
UnPlugin --> Extract
UnPlugin --> Transform
Extract --> Patterns
Transform --> Patterns
Ctx --> Errors
style UnPlugin fill:#06b6d4,color:#fff
style Patterns fill:#10b981,color:#fff
style Ctx fill:#eab308,color:#000
style Errors fill:#ef4444,color:#fff
```
๐ ย What gets extracted (per file type)
| File type | Extractor | Captures |
| ---------------------------- | --------------------------------------- | -------------------------------------------------------------------------------- |
| `.html`, `.htm` | `extractFromHtml` | `class="..."` attributes |
| `.jsx`, `.tsx`, `.ts`, `.js` | `extractFromJsx` | `className="..."`, `class="..."`, `cn()`, `clsx()`, `cva()`, `tv()`, `twMerge()` |
| `.vue` | JSX extractor + Vue SFC support | `class`, `:class`, object syntax, array syntax |
| `.svelte` | JSX extractor + Svelte directive parser | `class`, `class:directive` |
| `.astro` | JSX extractor | `class`, `class:list` |
| `.css` | `extractFromCss` | `.classname` selectors, escaped variants |
## ๐ Project Structure
```
tailwindcss-obfuscator/
โ
โโโ ๐ฆ packages/
โ โโโ tailwindcss-obfuscator/ # ๐ฏ Main npm package (TypeScript)
โ โโโ src/
โ โ โโโ core/ # ๐ง Context, types, errors, patterns
โ โ โโโ extractors/ # ๐ HTML, JSX, Vue, Svelte, CSS scanners
โ โ โโโ transformers/ # โ๏ธ CSS, HTML, JSX (regex + AST)
โ โ โโโ plugins/ # ๐ unplugin core + bundler adapters
โ โ โโโ cli/ # ๐ฅ๏ธ tw-obfuscator binary
โ โ โโโ utils/ # ๐ ๏ธ Logger
โ โโโ tests/ # โ
360+ unit + benchmark tests
โ
โโโ ๐งช apps/ # Integration test apps (17 frameworks)
โ โโโ test-vite-react/ # โ๏ธ React 19 + Vite 8 + Tailwind v4
โ โโโ test-vite-vue/ # ๐ Vue 3.5 + Vite 8 + Tailwind v4
โ โโโ test-nextjs/ # โฒ Next.js 16 + Tailwind v4 (webpack mode)
โ โโโ test-nuxt/ # ๐ข Nuxt 4 + Nitro + Tailwind v4
โ โโโ test-sveltekit/ # ๐ฅ SvelteKit 2.58 + Svelte 5 + Tailwind v4
โ โโโ test-astro/ # ๐ Astro 6 + Tailwind v4
โ โโโ test-solidjs/ # ๐ฆ Solid.js 1.9 + Vite 8 + Tailwind v4
โ โโโ test-react-router/ # ๐งญ React Router v7 (ex-Remix) + Tailwind v4
โ โโโ test-tanstack-start/ # ๐ชต TanStack Router 1.168 + Tailwind v4
โ โโโ test-qwik/ # โก Qwik 1.19 + Tailwind v4
โ โโโ test-shadcn-ui/ # ๐จ Next.js 16 + shadcn/ui + CVA
โ โโโ test-static-html/ # ๐ Static HTML + esbuild + Tailwind v4
โ โโโ test-webpack-standalone/ # ๐งฐ Webpack 5 standalone (no meta-framework)
โ โโโ test-rollup-standalone/ # ๐ฆ Rollup 4 standalone (no meta-framework)
โ โโโ test-tailwind-v3/ # ๐จ React + Vite + Tailwind v3
โ โโโ test-tailwind-v4/ # ๐จ React + Vite + Tailwind v4
โ โโโ tailwind_v3_react_nextjs/ # ๐จ Next.js + Tailwind v3 + shadcn (legacy)
โ โโโ tailwind_v4_react_nextjs/ # ๐จ Next.js + Tailwind v4 + shadcn
โ
โโโ ๐ docs/ # VitePress documentation
โโโ ๐ package.json # Root (TurboRepo + pnpm workspaces)
```
## ๐ ๏ธ Development
### ๐๏ธ Setup
```bash
# 1. Install all monorepo dependencies
pnpm install
# 2. Build the main package
pnpm --filter tailwindcss-obfuscator build
# 3. Run the test suite (418 tests as of v2.1.0, growing)
pnpm --filter tailwindcss-obfuscator test
```
### ๐งฐ Common commands
| Command | Description |
| ---------------------------------------------------------------- | ------------------------------------------- |
| ๐ ย `pnpm dev` | Start every app in dev mode (via Turbo) |
| ๐๏ธ ย `pnpm build` | Build every app with obfuscation enabled |
| โ
ย `pnpm test` | Run the full test suite |
| ๐ ย `pnpm --filter tailwindcss-obfuscator bench` | Run performance benchmarks |
| ๐ฆ ย `pnpm --filter tailwindcss-obfuscator test:integration` | Build every test app and verify obfuscation |
| ๐ ย `pnpm lint` | Lint with ESLint |
| ๐
ย `pnpm format` | Format with Prettier |
### ๐งช Per-app commands
```bash
# ๐ฏ Run a specific test app
pnpm --filter test-vite-react dev
pnpm --filter test-nextjs dev
pnpm --filter test-sveltekit dev
# ๐๏ธ Build a specific app
pnpm --filter test-vite-react build
```
## ๐จ Tailwind CSS Version Support
### ๐ Tailwind v4 โ CSS-first
```css
@import "tailwindcss";
@theme {
--color-primary: #3b82f6;
--font-display: "Inter", sans-serif;
}
```
- โ
Works with `@tailwindcss/vite` and `@tailwindcss/postcss`
- โ
`@theme` directive support
- โ
Container queries (`@container`, `@lg:`)
- โ
`@starting-style`, `nth-*`, wildcards
- โ
CSS variable shorthand `bg-(--my-var)`
### ๐งฑ Tailwind v3 โ Config file
```javascript
// tailwind.config.js
module.exports = {
content: ["./src/**/*.{js,ts,jsx,tsx}"],
theme: { extend: {} },
};
```
- โ
PostCSS-based processing
- โ
Full v3 plugin compatibility
- โ
Drop-in for existing projects
- โ
JIT mode supported
- โ
Custom variants & arbitrary values
## โ ๏ธ Important: Static Classes Only
> [!WARNING]
> ๐จ For obfuscation to work, **classes must be complete static strings**. The obfuscator scans your source code at build time to construct the rename table โ it cannot follow runtime string concatenation or dynamic interpolation.
### โ
Good โ Will be obfuscated
```jsx
```
### โ Bad โ Will NOT be obfuscated
```jsx
// Dynamic from variable โ opaque to the scanner
const cls = generateClassName();
```
### ๐งฐ Supported class utility helpers
The following helpers are recognized natively โ string arguments inside them are scanned and obfuscated:
| Helper | Library |
| ---------------------------------------- | ------------------------ |
| ๐จ ย `cn()` | shadcn/ui |
| ๐จ ย `clsx()` | clsx |
| ๐จ ย `classnames()` / `classNames()` | classnames |
| ๐จ ย `twMerge()` | tailwind-merge |
| ๐จ ย `cva()` | class-variance-authority |
| ๐จ ย `tv()` | tailwind-variants |
> [!TIP]
> ๐๏ธ Need to recognize a custom helper (`myClass()`, `tw()`, ...) ? Add its name to `preserve.functions` and the AST extractor will pick up the string arguments.
## ๐บ๏ธ Roadmap
| Status | Item |
| :----: | ------------------------------------------------------------ |
| โ
| Tailwind v3 + v4 support |
| โ
| Unified `unplugin` core for Vite/Webpack/Rollup/esbuild |
| โ
| AST-based JSX/TSX transformer |
| โ
| PostCSS-based CSS transformer with native source maps |
| โ
| Typed error hierarchy + structured logging |
| โ
| Tailwind config validator |
| โ
| Standalone CLI with `extract` / `transform` / `run` / `show` |
| ๐ง | Hot Module Replacement (HMR) preview mode |
| ๐ง | Online playground (paste a snippet, see the rename) |
| ๐ฎ | Browser extension to deobfuscate live for debugging |
| ๐ฎ | Migration codemod from `tailwindcss-mangle` |
โ
Done ย ยทย ๐ง In progress ย ยทย ๐ฎ Considered
## ๐ Documentation & Resources
### ๐
**[Package README](./packages/tailwindcss-obfuscator/README.md)**
Complete API reference, every option, advanced customization
### ๐
**[Documentation site](https://josedacosta.github.io/tailwindcss-obfuscator/)**
Framework guides, migration tips, FAQ
### ๐ก
**[Example apps](./apps/)**
13+ working examples, one per supported framework
## ๐ค Contribute to this project โ every PR is read
[](https://github.com/josedacosta/tailwindcss-obfuscator/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
[](https://github.com/josedacosta/tailwindcss-obfuscator/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
[](https://github.com/josedacosta/tailwindcss-obfuscator/discussions)
[](https://github.com/josedacosta/tailwindcss-obfuscator/graphs/contributors)
> **Open-source, community-driven, MIT-licensed.** This library exists because every PR โ bug fix, framework adapter, doc tweak, typo correction โ moves it forward. The maintainer reviews every contribution personally and aims for a first response within a week.
### How you can help (pick what fits your time)
### ๐
**Report a bug**
15-30 minutes
[Open a bug report](https://github.com/josedacosta/tailwindcss-obfuscator/issues/new?template=bug_report.yml) with a minimal repro (CodeSandbox or a tiny GitHub repo). Repros are gold.
### ๐ก
**Suggest a feature**
10 minutes
[Start a discussion](https://github.com/josedacosta/tailwindcss-obfuscator/discussions/new?category=ideas) before sending a big PR. Small features can go straight to a [feature request](https://github.com/josedacosta/tailwindcss-obfuscator/issues/new?template=feature_request.yml).
### ๐
**Polish the docs**
15 minutes
Spotted a typo, an unclear sentence, an outdated framework version? Edit any page on [the live docs site](https://josedacosta.github.io/tailwindcss-obfuscator/) โ every page has an "Edit on GitHub" link.
### ๐งฉ
**Add a framework adapter**
2-4 hours
The shared `unplugin` core makes new bundlers cheap to add. See the [framework-adapter guide](https://github.com/josedacosta/tailwindcss-obfuscator/blob/main/CONTRIBUTING.md#-adding-a-new-framework-adapter).
### ๐ง
**Fix a `good first issue`**
30 min โ 2 hours
[Browse issues tagged `good first issue`](https://github.com/josedacosta/tailwindcss-obfuscator/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) โ they come with a clear description and an estimated complexity.
### ๐
**Translate the docs**
ongoing
The site supports i18n via VitePress. Open a discussion if you'd like to lead a locale (FR, ES, DE, JA, ZH, โฆ).
### What to expect when you open a PR
1. **Branch protection forces every change through a PR.** No direct push to `main`, even for the maintainer.
2. **Five layers of automated review** run on your PR (CI, CodeQL, CodeRabbit AI, GitHub Copilot Code Review, auto-labels) โ they post comments only, none of them can approve or merge.
3. **The maintainer reads your PR personally.** First response within ~a week. Reviews focus on: public-API stability, test coverage, docs updates. Cosmetics come last.
4. **Squash-merge once approved + CI green.** Your contribution is credited in the next release's CHANGELOG via the Changesets entry you added with `pnpm changeset`.
5. **Releases are batched.** Your change ships on `main` immediately (and to the docs site), but the npm version bump waits until the maintainer cuts a release โ usually within days, sometimes weeks. This keeps version numbers meaningful.
> ๐ **Full guide for contributors โ [`CONTRIBUTING.md`](./CONTRIBUTING.md)** โ reviewed and updated for clarity. Read this before opening your first PR.
Contribution checklist (run before pushing)
```bash
pnpm install # if you haven't yet
pnpm lint && pnpm format:check # zero errors / zero warnings
pnpm --filter tailwindcss-obfuscator typecheck # strict TypeScript
pnpm test # full Vitest suite
node scripts/verify-obfuscation.mjs # 20+ sample apps obfuscate at 100%
pnpm changeset # if your change is user-facing
```
- [ ] All quality gates pass locally
- [ ] PR title follows [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `docs:`, `chore:`, โฆ)
- [ ] A `.changeset/*.md` entry was added if the change touches `packages/tailwindcss-obfuscator/**`
- [ ] If you add a framework: include a working `apps/test-/` integration
### A word from the maintainer
This is a **side-project maintained on personal time**. Every star, every issue with a clear repro, every doc fix, every honest discussion makes maintaining it sustainable. If `tailwindcss-obfuscator` saves your design system or your bandwidth in production, the kindest things you can do are: โญ **star the repo**, share it with one developer who'd benefit, or [sponsor](https://github.com/sponsors/josedacosta) the work. โ _Josรฉ_
## ๐ผ๏ธ Brand Assets
Official logos for your projects, articles, and presentations:
Variant
โ๏ธ ย Light Background
๐ ย Dark Background
Horizontal

Square

## โ FAQ
### ๐ Search-intent questions
> What people type into Google or ask an AI assistant when they're looking for a tool like this. Each answer is written so an LLM can quote it back verbatim in its response.
How do I prevent people from copying my Tailwind CSS design system?
Three layers, in order of effort: (1) install **`tailwindcss-obfuscator`** at build time โ it rewrites every Tailwind utility class (`bg-blue-500`, `flex items-center`, `p-4`) into short opaque identifiers (`tw-a`, `tw-b`, `tw-c`) in the shipped HTML / CSS / JS bundle so anyone "view source"-ing your site can no longer trivially copy your token system; (2) drop your source maps from production; (3) HTML-minify the rendered output. After these three, copying your design system goes from "ten seconds with the inspector" to "hours of reverse-engineering for each component". Add `preserve.classes` for a small allowlist (e.g. `dark`, `sr-only`) so functional classes still work.
How do I obfuscate Tailwind CSS classes in production?
Install `tailwindcss-obfuscator` and add it to your build tool's plugin chain. For Vite:
```ts
// vite.config.ts
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
import tailwindCssObfuscator from "tailwindcss-obfuscator/vite";
export default defineConfig({
plugins: [tailwindcss(), tailwindCssObfuscator({ prefix: "tw-" })],
});
```
That's it โ `vite build` now produces an obfuscated bundle, `vite dev` is left untouched. For Next.js / Nuxt / SvelteKit / Astro / Solid / Qwik / Webpack / Rollup / esbuild / Rspack / Farm setups, see the [Quick Start](#-quick-start) section above.
How do I shrink my Tailwind CSS bundle size?
Two complementary techniques: (1) Tailwind's built-in JIT / content scanning already removes unused utilities โ that's the baseline; (2) on top of that, **`tailwindcss-obfuscator`** rewrites the remaining classes from long readable names (`bg-blue-500 hover:bg-blue-600 dark:bg-blue-700`) into short identifiers (`tw-a tw-b tw-c`), shaving an additional **30โ60 %** off the gzipped CSS bundle on CSS-heavy pages. The bigger your CSS budget, the more you save. Combine both for the smallest possible Tailwind output.
Can I make Tailwind classes hard to read in the final HTML?
Yes โ that's exactly what a Tailwind class obfuscator (a.k.a. class mangler) does. Tools like `tailwindcss-obfuscator` rewrite every utility class in the shipped HTML, CSS, and JS into short opaque tokens (`tw-a`, `tw-b`, โฆ) at build time. Your source code stays readable, but a competitor opening DevTools on your site sees `
` instead of ``. Reverse-engineering your design system goes from minutes to hours.
How do I install a Tailwind class mangler in Next.js / Vite / Nuxt / SvelteKit?
`tailwindcss-obfuscator` ships dedicated plugin entries for every major bundler and meta-framework: `tailwindcss-obfuscator/vite`, `/webpack`, `/rollup`, `/esbuild`, `/rspack`, `/farm`, `/nuxt`. Pick the one that matches your build tool, add it to the plugin chain, and the obfuscation runs automatically on `npm run build` (no effect on dev). The full setup snippet for each framework is in the [Quick Start](#-quick-start) section above and in the [framework guides](https://josedacosta.github.io/tailwindcss-obfuscator/guide/getting-started).
Does Tailwind CSS itself have a built-in way to obfuscate classes?
No โ Tailwind Labs has explicitly chosen not to ship a class-mangling pass upstream (see [discussion #7956](https://github.com/tailwindlabs/tailwindcss/discussions/7956)). You need a third-party tool for it. The two active options are `tailwindcss-obfuscator` (this project โ AST-based, every modern bundler, built around obfuscation) and `tailwindcss-mangle` (mangling for tree-shaking, Vite + Webpack only). See the [full comparison](https://josedacosta.github.io/tailwindcss-obfuscator/research/comparison) for the trade-offs.
Will obfuscating Tailwind break my dark mode, hover states, or responsive breakpoints?
No. The obfuscator rewrites the **class names** consistently across CSS selectors AND every `class=` / `className=` reference in your bundle โ so `dark:bg-gray-900`, `hover:bg-blue-600`, `md:flex`, `2xl:grid-cols-4` all keep working: the variant and the base class are renamed together as a single unit. Your site behaves exactly the same in production, just with shorter class names.
Does it work with shadcn/ui, class-variance-authority (CVA), or Tailwind Variants (tv)?
Yes โ out of the box. The AST-based extractor recognises `cn()`, `clsx()`, `classnames()`, `twMerge()`, `cva()`, and `tv()` natively, including string literals nested inside `variants`, `compoundVariants`, `defaultVariants`, and slot definitions. The included `apps/test-shadcn-ui` sample app exercises the full shadcn/ui + CVA pattern under a production build to prove it.
How do I add build-time CSS class shortening to my React app?
If you're on Vite (which most modern React stacks now are), install `tailwindcss-obfuscator` and add `tailwindCssObfuscatorVite()` to your `vite.config.ts` plugins array. If you're on Next.js (Webpack), add `tailwindCssObfuscatorWebpack()` to the `webpack` config in `next.config.js`. The full snippets for both are in the [Quick Start](#-quick-start) section above. The obfuscator only runs on `next build` / `vite build`, so dev mode stays normal.
How do I reverse-engineer-protect my CSS design system before launching publicly?
(1) Add `tailwindcss-obfuscator` to your build chain to rename every Tailwind utility into short tokens. (2) Disable source-map publishing in production. (3) Run an HTML minifier so attribute order and whitespace don't leak structural intent. (4) If you use a custom design-token CSS file, gate it behind a `preserve.classes` allowlist so only the classes you intentionally expose stay readable. The combination won't make your CSS uncrackable, but it raises the bar from "copy-paste in five minutes" to "rebuild from scratch in five hours".
### ๐ฆ General questions about the library
> Technical and operational questions about how `tailwindcss-obfuscator` itself works.
What is a Tailwind CSS obfuscator?
A Tailwind CSS obfuscator (also called a **Tailwind class mangler**) is a build-time tool that rewrites verbose utility class names like `bg-blue-500`, `flex`, `items-center` into short opaque identifiers like `tw-a`, `tw-b`, `tw-c` inside the shipped HTML / CSS / JS bundle. Source code stays readable โ only production output is changed. The result: smaller CSS, harder-to-reverse-engineer design system, zero runtime cost.
How much does it shrink my CSS bundle?
Typical savings on production builds (gzip): **30โ60%** on CSS-heavy pages. Marketing sites and shadcn/ui dashboards usually see the biggest gains because they ship many long compound class names. See the [Performance impact](#-performance-impact) table above for measurements on the 14 included test apps.
How is this different from tailwindcss-mangle?
[`tailwindcss-mangle`](https://github.com/sonofmagic/tailwindcss-mangle) was built primarily to **mangle Tailwind classes for tree-shaking and dead-class removal**. `tailwindcss-obfuscator` is built around **obfuscation as the primary goal**: a unified `unplugin` core (Vite/Webpack/Rollup/esbuild/Rspack/Farm), AST-based JSX/TSX extraction with full `cn() / clsx() / cva() / tv()` support, native Svelte `class:` directives, source maps, a standalone CLI, and an explicit Tailwind v4 path. See the [comparison](#-why-this-library) table.
Does it work with Tailwind CSS v4?
Yes โ full v4 support, including `@import "tailwindcss"`, `@theme`, container queries (`@container`, `@lg:`), `@starting-style`, the `*:` / `**:` wildcard selectors, and the new `bg-(--my-var)` CSS-variable shorthand. v3 is also fully supported (config file, JIT, `safelist`, custom variants).
Does it work with Next.js / Nuxt / SvelteKit / Astro / Solid / Qwik / Remix?
Yes โ every major meta-framework is supported and has a dedicated test app under [`apps/`](./apps/): Next.js (App Router + Pages Router), Nuxt 4, SvelteKit + Svelte 5, Astro 6, Solid.js 1.9, Qwik 1.19, React Router v7 (ex-Remix), TanStack Start. Use the matching plugin entry from the [Quick Start](#-quick-start) section.
Will obfuscation break my dev server?
No โ obfuscation is **disabled in development by default**. It only runs when `command === "build"` (Vite) or `mode === "production"` (Webpack/Next.js). Set `refresh: true` if you want it on in dev too.
How do I debug an obfuscated bundle?
Two options:
1. The class mapping is saved to `.tw-obfuscation/class-mapping.json` โ open it to translate any `tw-xxx` back to its original.
2. Set `randomize: false` to get deterministic, sequential names (`tw-a`, `tw-b`, `tw-c`...) that are easier to track between builds.
Can I customize how obfuscated names are generated?
Yes โ pass a `classGenerator` function:
```javascript
tailwindCssObfuscatorVite({
classGenerator: (index, originalClass) => `c${index.toString(36)}`,
});
```
How do I keep certain classes un-obfuscated?
```javascript
tailwindCssObfuscatorVite({
preserve: {
classes: ["dark", "light", "sr-only"], // never rename these
functions: ["debugClass", "analytics"], // skip strings inside these calls
},
});
```
Does it work with shadcn/ui, CVA and Tailwind Variants?
Yes โ the AST extractor recognises `cn()`, `clsx()`, `classnames()`, `twMerge()`, `cva()` and `tv()` natively, including string literals nested inside `variants`, `compoundVariants` and `defaultVariants`. The dedicated [`apps/test-shadcn-ui`](./apps/test-shadcn-ui) sample app exercises the full shadcn/ui + CVA pattern under production build.
Is the transformation reversible? Can I deobfuscate later?
Yes โ every build emits `.tw-obfuscation/class-mapping.json`, a deterministic `original โ obfuscated` mapping. Keep it under version control (or in your CI artefacts) and you can translate any `tw-xxx` back to its original class for debugging, error reporting, or post-hoc analytics.
Is class obfuscation enough to "protect" my design system?
Obfuscation makes reverse-engineering **significantly harder** but it is not encryption โ anyone can still read the rendered output. Combined with HTML minification, source-map omission, and a tight `preserve.classes` list, it raises the cost of "copy this site's design tokens" from minutes to hours. Treat it as one layer of defence, not a guarantee.
Why are my dynamic classes not being obfuscated?
Because they are not visible to the AST scanner at build time. Patterns like ``className={`bg-${color}-500`}`` are constructed at runtime โ the obfuscator never sees the final string. Switch to a static ternary (`color === "red" ? "bg-red-500" : "bg-blue-500"`) or a `cn()` call with all branches spelled out. See the [Static Classes Only](#๏ธ-important-static-classes-only) section.
My bundler isn't listed โ can I still use it?
Yes! The package exposes the underlying [`unplugin`](https://github.com/unjs/unplugin) factory at `tailwindcss-obfuscator/internals`:
```javascript
import { obfuscatorUnplugin } from "tailwindcss-obfuscator/internals";
// obfuscatorUnplugin.farm, obfuscatorUnplugin.rspack, ...
```
Or use the standalone CLI as a post-build step.
Is it free? What's the licence?
Yes โ **MIT licensed**, free for personal, commercial, and closed-source use. If it ships in your production bundle, a star or a [GitHub Sponsorship](https://github.com/sponsors/josedacosta) is the kindest way to say thanks.
## ๐ค Author
Built and maintained by **Josรฉ DA COSTA**.
๐ Website
josedacosta.info ยท portfolio.josedacosta.info
๐ GitHub
@josedacosta
โ๏ธ Email
contact@josedacosta.info
๐ Sponsor
github.com/sponsors/josedacosta
If `tailwindcss-obfuscator` ships in your production bundle, a star or a sponsorship is the kindest way to say thanks.
## ๐ Keywords & search terms
๐ ย What people search for when they need this library
If a search engine or LLM brought you here, here are the queries this project answers. Use them to verify it fits your use case โ and to help others find it.
**Core intent**
`tailwindcss obfuscator` ยท `tailwind css obfuscator` ยท `tailwind obfuscator` ยท `obfuscate tailwind classes` ยท `obfuscate tailwind css` ยท `tailwind class obfuscation` ยท `obfuscate tailwind utility classes` ยท `hide tailwind classes` ยท `protect tailwind design system` ยท `tailwind reverse engineering protection` ยท `make tailwind classes unreadable`
**Mangling alternatives**
`tailwind mangle` ยท `tailwindcss mangle` ยท `tailwind class mangler` ยท `tailwindcss-mangle alternative` ยท `unplugin-tailwindcss-mangle alternative` ยท `tailwindcss-patch alternative` ยท `tailwindcss-mangle vs obfuscator` ยท `tailwindcss-mangle tailwind v4`
**Bundle size**
`shrink tailwind css bundle` ยท `reduce tailwind css size` ยท `tailwind css minifier` ยท `tailwind class shortener` ยท `smaller tailwind bundle` ยท `tailwind css bundle 30%` ยท `tailwind css bundle 50%` ยท `optimize tailwind css production`
**Bundlers**
`tailwind vite plugin obfuscate` ยท `tailwind webpack plugin obfuscate` ยท `tailwind rollup plugin obfuscate` ยท `tailwind esbuild plugin obfuscate` ยท `tailwind rspack plugin` ยท `tailwind farm plugin` ยท `unplugin tailwind obfuscator`
**Frameworks**
`next.js tailwind obfuscator` ยท `next.js tailwind mangle` ยท `nuxt tailwind obfuscator` ยท `nuxt module tailwindcss obfuscator` ยท `sveltekit tailwind obfuscator` ยท `astro tailwind obfuscator` ยท `solid.js tailwind obfuscator` ยท `qwik tailwind obfuscator` ยท `react router tailwind obfuscator` ยท `tanstack router tailwind obfuscator` ยท `remix tailwind obfuscator` ยท `shadcn ui obfuscate` ยท `cva obfuscate` ยท `tailwind variants obfuscate`
**Tailwind versions**
`tailwind v3 obfuscator` ยท `tailwind v4 obfuscator` ยท `tailwind v4 mangle` ยท `tailwind v4 class shortener` ยท `tailwind v4 class obfuscation oxide` ยท `@tailwindcss/vite obfuscator` ยท `@tailwindcss/postcss obfuscator`
**Use cases**
`hide design tokens from competitors` ยท `obscure tailwind theme` ยท `prevent tailwind copy paste` ยท `protect css ip` ยท `tailwind class names production only` ยท `class mangling source maps`
## ๐ License
[MIT](./LICENSE) ยฉ [Josรฉ DA COSTA](https://github.com/josedacosta)
### ๐ Star History
---
Built with โค๏ธ for the Tailwind community
โญ ย **If this library helps you protect your design system, give it a star!** ย โญ