Skip to content
Created: March 25, 2025

The JavaScript configuration and plugin snippets here target Tailwind CSS 3. Tailwind CSS 4 uses CSS-first configuration; explicitly load a compatible legacy config with @config rather than assuming automatic discovery. Upgrade guide.

Tailwind's underlying generation mechanism & purge principle is the key to understanding "why writing this way is safe/efficient".

I will dismantle it for you according to the complete link of "From build → JIT → purge". This article is based on the background of Vite / Vue / compilation principle direction.

1. Overview of the overall process (first have the overall picture) ​

The general experience when building Tailwind is:

tailwind.config.js
        ↓
解析 theme / plugins
        ↓
生成“候选类名规则表”
        ↓
扫描 content(源码)
        ↓
JIT 即时生成 CSS
        ↓
输出最终 CSS

📌 Key Points

  • ❌ Not all classes are pre-generated
  • ✅ Yes On-demand generation (JIT)

2. How does Tailwind JIT work? ​

1️⃣ Early days (before v2) ​

  • Pre-generated tens of thousands of CSS
  • It’s useless to purge again
  • Slow build, large CSS

2️⃣ Now (v3+, default JIT) ​

core idea ​

See a class → generate a CSS immediately

For example:

html
<div class="px-4 text-red-500"></div>

JIT will:

  1. split class token
  2. Matching rules
  3. Check theme value
  4. Generate CSS
css
.px-4 { padding-left: 1rem; padding-right: 1rem }
.text-red-500 { color: #ef4444 }

3️⃣ Internal abstract model (important) ​

Each class = a "function call" ​

txt
text-red-500
│    │    │
│    │    └── scale value
│    └────── theme key
└────────── utility name

Equivalent to:

js
text(color = theme.colors.red[500])

📌 So Tailwind is very much like a DSL

3. Content scanning mechanism (the core of purge) ​

1️⃣ What is being scanned? ​

js
content: ['./src/**/*.{vue,js,ts,html}']

Tailwind will:

  • read file
  • Use regular to extract possible class tokens

For example:

vue
<div class="px-4 text-red-500"></div>

will be split into:

txt
px-4
text-red-500

2️⃣ Key facts (many people don’t know) ​

Tailwind doesn’t understand JS/Vue syntax

It just scans "string"

📌 So 👇

❌

js
`bg-${color}-500`

Scan results:

txt
bg-
-color-
-500

👉 Match failed → No CSS generated

4. Why are dynamic classes purged? ​

The reason is not purge, but: ​

JIT "cannot see the full class name" at all

For example:

vue
<div :class="`bg-${type}-500`"></div>

Tailwind scanned only:

txt
bg-
-type-
-500

So:

  • No valid token
  • Does not trigger generation

5. JIT token matching rules (very important) ​

Examples of valid tokens ​

txt
bg-red-500
hover:bg-blue-500
md:hover:text-sm

Internal processing order ​

txt
variants → utility → value

For example:

txt
md:hover:bg-red-500

Equivalent to:

css
@media (min-width: 768px) {
  .md\:hover\:bg-red-500:hover { background: ... }
}

6. How do variants work? ​

Tailwind treats variants as "wrappers" ​

txt
hover:   → &:hover
md:      → @media
dark:    → .dark &

So:

html
<div class="dark:hover:bg-gray-800"></div>

Equivalent to:

css
.dark .dark\:hover\:bg-gray-800:hover {
  background-color: #1f2937;
}

7. Why are the classes generated by the plug-in not purged? ​

Reason: ​

Plugins inject rules before JIT

txt
plugin
  ↓
addUtilities / addComponents
  ↓
规则表
  ↓
content 扫描

📌 Classes in the plug-in:

  • Belongs to "registered rules"
  • No scan hits required

8. safelist (whitelist) mechanism ​

Used for: You know you want to use it, but it can’t be scanned ​

js
safelist: [
  'bg-red-500',
  'bg-blue-500',
  {
    pattern: /bg-(red|blue|green)-(100|500)/
  }
]

📌 Use with caution

  • safelist = forced generation
  • CSS will become larger if you use it too much

9. Why is Tailwind so small? ​

Because:

  • Missed classes → not generated at all
  • Useless variant → will not be included
  • Plug-ins also take effect on demand

A Vue project:

  • Actual CSS: 10~30KB
  • rather than MB

10. Cooperation of Tailwind & Vite (you will resonate with it) ​

Why is Tailwind HMR so fast on Vite? ​

txt
文件变更
  ↓
Vite 通知
  ↓
Tailwind 重新扫描这个文件
  ↓
增量生成 CSS

📌 Not a full rebuild

This is essentially consistent with the Vite virtual module/HMR you studied.

11. The underlying explanation of common “weird bugs” ​

1️⃣ class is written but it has no effect ​

  • Illegal spelling
  • There is no such value in theme
  • dynamically spliced

2️⃣ dev yes, build no ​

  • content path is missing
  • The production environment class is written differently
  • Classes in dependent packages are not scanned

12. Comparison summary with your previous Vue/compilation direction ​

TailwindCompilation principle analogy
utilityAST node
variantwrapper
themesymbol table
JITon-demand codegen
purgedead code elimination

📌 Tailwind Essence = CSS Compiler

13. Correctly using Tailwind’s “instructions” ​

  • 1️⃣ Class name must be a static string
  • 2️⃣ Design variables into theme/plugin
  • 3️⃣ Complex dynamics → Mapping table
  • 4️⃣ Public system → plug-in