From tailwindcss-master
Optimizes Tailwind CSS performance covering v4 Rust engine improvements, JIT compilation, content detection, tree shaking, dynamic class name solutions, and transition best practices for faster builds and smaller bundles.
npx claudepluginhub josiahsiegel/claude-plugin-marketplace --plugin tailwindcss-masterThis skill uses the workspace's default tool permissions.
Tailwind CSS v4 features a completely rewritten engine in Rust:
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Tailwind CSS v4 features a completely rewritten engine in Rust:
| Metric | v3 | v4 |
|---|---|---|
| Full builds | Baseline | Up to 5x faster |
| Incremental builds | Milliseconds | Microseconds (100x+) |
| Engine | JavaScript | Rust |
JIT generates styles on-demand as classes are discovered in your files:
Unlike v3, JIT is always enabled in v4—no configuration needed:
@import "tailwindcss";
/* JIT is automatic */
v4 automatically detects template files—no content configuration required:
/* v4 - Works automatically */
@import "tailwindcss";
If automatic detection fails, specify sources explicitly:
@import "tailwindcss";
@source "./src/**/*.{html,js,jsx,ts,tsx,vue,svelte}";
@source "./components/**/*.{js,jsx,ts,tsx}";
@source not "./src/legacy/**";
Tailwind's build process removes unused CSS:
Source: All possible utilities (~15MB+)
↓
Scan: Find used class names
↓
Output: Only used styles (~10-50KB typical)
# Vite - automatically optimized for production
npm run build
# PostCSS - ensure NODE_ENV is set
NODE_ENV=production npx postcss input.css -o output.css
Tailwind can't detect dynamically constructed class names:
// BAD - Classes won't be generated
const color = 'blue'
className={`text-${color}-500`} // ❌ Not detected
const size = 'lg'
className={`text-${size}`} // ❌ Not detected
// GOOD - Full class names
const colorClasses = {
blue: 'text-blue-500',
red: 'text-red-500',
green: 'text-green-500',
}
className={colorClasses[color]} // ✓ Detected
// GOOD - Style based on data attributes
<div data-color={color} className="data-[color=blue]:text-blue-500 data-[color=red]:text-red-500">
/* In your CSS for v4 */
@source inline("text-blue-500 text-red-500 text-green-500");
@theme {
--color-dynamic: oklch(0.6 0.2 250);
}
<div class="text-[var(--color-dynamic)]">Dynamic color</div>
<!-- SLOW - Transitions all properties -->
<button class="transition-all duration-200">
<!-- FAST - Only transitions specific properties -->
<button class="transition-colors duration-200">
<button class="transition-transform duration-200">
<button class="transition-opacity duration-200">
Prefer transform and opacity for smooth animations:
<!-- GOOD - GPU accelerated -->
<div class="transform hover:scale-105 transition-transform">
<!-- GOOD - GPU accelerated -->
<div class="opacity-100 hover:opacity-80 transition-opacity">
<!-- SLOW - May cause repaints -->
<div class="left-0 hover:left-4 transition-all">
In v4, use CSS variables directly instead of theme():
/* v3 - Uses theme() function */
.element {
color: theme(colors.blue.500);
}
/* v4 - Use CSS variables (faster) */
.element {
color: var(--color-blue-500);
}
For performance-critical paths:
@import "tailwindcss/theme.css" theme(static);
This inlines theme values instead of using CSS variables.
// vite.config.js
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [tailwindcss()],
build: {
// Minify CSS
cssMinify: 'lightningcss',
// Optimize chunks
rollupOptions: {
output: {
manualChunks: {
// Split vendor CSS if needed
}
}
}
}
})
// postcss.config.mjs
export default {
plugins: {
'@tailwindcss/postcss': {},
cssnano: process.env.NODE_ENV === 'production' ? {} : false
}
}
/* Only load what you need */
@plugin "@tailwindcss/typography";
/* Don't load unused plugins */
@theme {
/* Disable default colors */
--color-*: initial;
/* Define only needed colors */
--color-primary: oklch(0.6 0.2 250);
--color-secondary: oklch(0.7 0.15 180);
--color-gray-100: oklch(0.95 0 0);
--color-gray-900: oklch(0.15 0 0);
}
@theme {
/* Remove unused breakpoints */
--breakpoint-2xl: initial;
/* Keep only what you use */
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
}
# GitHub Actions example
- name: Cache node_modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
- name: Build
run: npm run build
# Time your build
time npm run build
# Verbose output
DEBUG=tailwindcss:* npm run build
# Install analyzer
npm install -D vite-bundle-analyzer
# Analyze bundle
npm run build -- --analyze
# Check output CSS size
ls -lh dist/assets/*.css
# Gzipped size
gzip -c dist/assets/main.css | wc -c
NODE_ENV=production is set| Issue | Solution |
|---|---|
| Large CSS output | Check for dynamic classes, safelist issues |
| Slow builds | Ensure v4, check file globs |
| Missing styles | Check content detection, class names |
| Slow animations | Use GPU-accelerated properties |
For very large apps, consider code-splitting CSS:
// Dynamically import CSS for routes
const AdminPage = lazy(() =>
import('./admin.css').then(() => import('./AdminPage'))
)
transition-alltransform and opacity