How It Works

Deep dive into the Tailwind class obfuscation process with comprehensive examples, step-by-step transformations, and real-world component demonstrations. Understand the complete workflow from file discovery to CSS generation, including class mapping, file rewriting, and debugging techniques.

The Process

The obfuscation process follows these steps:

  1. File Discovery - Find all .tsx, .jsx, .ts, .js files
  2. Class Extraction - Parse className attributes using regex
  3. Class Mapping - Generate random names for each unique class
  4. File Rewriting - Replace original classes with obfuscated names
  5. CSS Generation - Create CSS with @apply rules
  6. Map Export - Save mapping for debugging

Before Obfuscate

TSX
// components/ui/button.tsx
import { cn } from "@/lib/utils";

export function Button({
  children,
  variant = "primary",
  className,
  ...props
}: {
  children: React.ReactNode;
  variant?: "primary" | "secondary";
  className?: string;
}) {
  return (
    <button
      className={cn(
        "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium transition-colors",
        "focus:outline-none focus:ring-2 focus:ring-offset-2",
        variant === "primary"
          ? "bg-primary text-primary-foreground hover:bg-primary/90"
          : "bg-secondary text-secondary-foreground hover:bg-secondary/90",
        className,
      )}
      {...props}
    >
      {children}
    </button>
  );
}

After Obfuscation

TSX
// components/ui/button.tsx (after obfuscation)
import { cn } from "@/lib/utils";

export function Button({
  children,
  variant = "primary",
  className,
  ...props
}: {
  children: React.ReactNode;
  variant?: "primary" | "secondary";
  className?: string;
}) {
  return (
    <button
      className={cn(
        "x7y9z2w1 q3m5n6p8 a1b2c3d4 e5f6g7h8 i9j0k1l2 m3n4o5p6",
        "r7s8t9u0 v1w2x3y4 z5a6b7c8 d9e0f1g2",
        variant === "primary"
          ? "h3i4j5k6 l7m8n9o0 p1q2r3s4 t5u6v7w8"
          : "x9y0z1a2 b3c4d5e6 f7g8h9i0 j1k2l3m4",
        className,
      )}
      {...props}
    >
      {children}
    </button>
  );
}

Generated CSS

CSS
/* app/obfuscated-styles.css */
@import "tailwindcss";
@import "./token.css";

.x7y9z2w1 {
  @apply inline-flex items-center justify-center;
}
.q3m5n6p8 {
  @apply rounded-lg;
}
.a1b2c3d4 {
  @apply px-4 py-2;
}
.e5f6g7h8 {
  @apply text-sm font-medium;
}
.i9j0k1l2 {
  @apply transition-colors;
}
.r7s8t9u0 {
  @apply focus:outline-none;
}
.v1w2x3y4 {
  @apply focus:ring-2;
}
.z5a6b7c8 {
  @apply focus:ring-offset-2;
}
.h3i4j5k6 {
  @apply bg-primary text-primary-foreground;
}
.l7m8n9o0 {
  @apply hover:bg-primary/90;
}
.p1q2r3s4 {
  @apply bg-secondary text-secondary-foreground;
}
.t5u6v7w8 {
  @apply hover:bg-secondary/90;
}

Class Mapping

The script generates a mapping file .obfuscation-map.json:

JSON
{
  "inline-flex": "x7y9z2w1",
  "items-center": "q3m5n6p8",
  "justify-center": "a1b2c3d4",
  "rounded-lg": "e5f6g7h8",
  "px-4": "i9j0k1l2",
  "py-2": "m3n4o5p6",
  "text-sm": "r7s8t9u0",
  "font-medium": "v1w2x3y4",
  "transition-colors": "z5a6b7c8",
  "focus:outline-none": "d9e0f1g2",
  "focus:ring-2": "h3i4j5k6",
  "focus:ring-offset-2": "l7m8n9o0",
  "bg-primary": "h3i4j5k6",
  "text-primary-foreground": "l7m8n9o0",
  "hover:bg-primary/90": "p1q2r3s4",
  "bg-secondary": "x9y0z1a2",
  "text-secondary-foreground": "b3c4d5e6",
  "hover:bg-secondary/90": "t5u6v7w8"
}

This mapping helps with:

  • Debugging - See what each obfuscated class represents
  • Reversibility - Potentially reverse the process
  • Analysis - Understand class usage patterns

File Processing

JAVASCRIPT
function findFiles(dir) {
  const files = [];
  const items = fs.readdirSync(dir);

  for (const item of items) {
    const fullPath = path.join(dir, item);
    const stat = fs.statSync(fullPath);

    if (stat.isDirectory() && !item.startsWith(".")) {
      files.push(...findFiles(fullPath));
    } else if (item.match(/\.(tsx|jsx|ts|js)$/)) {
      files.push(fullPath);
    }
  }
  return files;
}

Build Order

The build process follows this sequence:

  1. Pre-build - node scripts/obfuscate-tailwind.js
  2. Next.js Build - next build
  3. Production Output - Obfuscated HTML + CSS

Development

HTML
<button
  class="inline-flex items-center justify-center rounded-lg bg-primary text-primary-foreground px-4 py-2"
>
  Click me
</button>

Production

HTML
<button class="x7y9z2w1 q3m5n6p8 a1b2c3d4 h3i4j5k6 l7m8n9o0">Click me</button>

Size Reduction:

  • Before: 89 characters
  • After: 47 characters
  • Reduction: 47% smaller

This detailed explanation shows exactly how the obfuscation process works, from input to output, with real examples you can see and understand.