Use when picking a Tailwind CSS version for a new project, deciding whether to upgrade an existing v3 codebase to v4, or debugging visual regressions and build errors that appear right after a v3 to v4 bump. Prevents the silent visual breakages (border-color, ring-width, shadow scale rename) and the removed-option traps (corePlugins, safelist, separator) that bite every v3 to v4 migration. Covers the JIT vs Oxide engine model, JS config vs CSS-first @theme configuration, default behaviour shifts, removed APIs, browser baseline requirements, and the upgrade-or-stay decision tree. Keywords: tailwind v3, tailwind v4, tailwindcss v4, oxide engine, JIT, tailwind upgrade, tailwind migration, @theme, @tailwind directive removed, corePlugins removed, safelist removed, separator removed, ring-width changed, border-color changed, shadow-sm changed, should I upgrade tailwind, tailwind 4 vs 3, breaking changes tailwind, lightning css, @import tailwindcss
Use when picking a Tailwind CSS version for a new project, deciding whether to upgrade an existing v3 codebase to v4, or debugging visual regressions and build errors that appear right after a v3 to v4 bump. Prevents the silent visual breakages (border-color, ring-width, shadow scale rename) and the removed-option traps (corePlugins, safelist, separator) that bite every v3 to v4 migration. Covers the JIT vs Oxide engine model, JS config vs CSS-first @theme configuration, default behaviour shifts, removed APIs, browser baseline requirements, and the upgrade-or-stay decision tree. Keywords: tailwind v3, tailwind v4, tailwindcss v4, oxide engine, JIT, tailwind upgrade, tailwind migration, @theme, @tailwind directive removed, corePlugins removed, safelist removed, separator removed, ring-width changed, border-color changed, shadow-sm changed, should I upgrade tailwind, tailwind 4 vs 3, breaking changes tailwind, lightning css, @import tailwindcss
license
MIT
compatibility
Designed for Claude Code. Requires Tailwind CSS v3.4 or v4.0+.
metadata
{"author":"OpenAEC-Foundation","version":"1.0"}
Tailwind CSS v3 vs v4
This skill is the version-comparison reference for Tailwind CSS. Every section
presents v3 and v4 side-by-side. Pair with companion skills:
tailwind-errors-v4-migration : trap catalog when an upgrade breaks builds
Quick Decision Tree
Do you control the browser baseline (Safari 16.4+, Chrome 111+, Firefox 128+)?
├── NO → STAY on v3.4 (v4 requires @property and color-mix() support)
└── YES → Is the project new (greenfield)?
├── YES → USE v4 (CSS-first config, faster builds, native @import + autoprefix)
└── NO → Does the project use ANY of: corePlugins, safelist, separator,
custom prefix with `tw-` pattern, JS resolveConfig at runtime,
or Sass/Less/Stylus in the pipeline?
├── YES → STAY on v3.4 until alternatives are in place
│ (corePlugins and separator have NO v4 replacement)
└── NO → UPGRADE with `npx @tailwindcss/upgrade` (requires Node 20+)
then expect visual diffs from border-color, ring-width, and
shadow/blur/radius scale renames.
Engine Model
Aspect
v3 (JIT)
v4 (Oxide)
Implementation
JavaScript
Rust + Lightning CSS
Default since
v3.0 (Dec 2021)
v4.0 (Jan 22 2025)
Full build (official benchmark)
378ms
100ms
Incremental with CSS changes
44ms
5ms
Incremental without CSS changes
35ms
192µs
Marketing claim
n/a
over 3.5x full, over 8x incremental, over 100x no-CSS-change
v4 ALWAYS requires Safari 16.4+, Chrome 111+, Firefox 128+ because Oxide
emits @property and color-mix(). NEVER ship v4 to a project that must
support older browsers. Stay on v3.4 instead.
Configuration
Topic
v3
v4
Config file
tailwind.config.js (auto-detected)
None auto-detected ; legacy via @config "./..."
Tokens
JS object under theme.extend
CSS variables in @theme { ... }
Plugins
plugins: [require('...')]
@plugin "..." in CSS
Content scanning
content: [...] glob array
@source "..." directive (auto-detects by default)
Custom variants
addVariant() in plugin
@custom-variant name (...) in CSS
Dark mode
darkMode: 'class' | 'media' | 'selector'
@custom-variant dark (&:where(.dark, .dark *))
Prefix
prefix: 'tw-' → tw-flex
CSS-first prefix → tw:flex (variant-style)
Safelist
safelist: [...] array/regex
@source inline("...") with brace expansion
Disable utilities
corePlugins: { float: false }
REMOVED, no replacement
Custom separator
separator: '_'
REMOVED, no replacement
JS access to theme
resolveConfig()
REMOVED, use getComputedStyle() on CSS vars
See references/examples.md for a complete v3 config rewritten as a v4 stylesheet.
/* v4 : single @import replaces all three */@import"tailwindcss";
The v3 @tailwind directives ALWAYS error in v4 with "Cannot apply unknown
utility class". This is the most common upgrade failure. Always replace ALL
three before first build.
Default Behaviour Shifts (visual breakage)
These v4 changes affect EVERY existing v3 project on upgrade. Verified against
the official upgrade guide.
@layer base { button, [role="button"] { cursor: pointer; } }
Hover variant
always applies
@media (hover: hover) gated
@custom-variant hover (&:hover);
outline-none
works
renamed to outline-hidden
Run upgrade tool to rename
ALWAYS apply the shims OR refactor markup. NEVER ignore these on upgrade :
unstyled borders and 1px rings on cards/inputs are the most reported visual
regressions in v4 upgrade threads.
EVERY size-suffixed scale shifted down by one in v4 because a new -xs step
was inserted. Existing shadow-sm becomes visually smaller after upgrade.
Scale
v3 smallest
v3 next
v4 smallest
v4 next
shadow-*
shadow-sm
shadow
shadow-xs
shadow-sm
drop-shadow-*
drop-shadow-sm
drop-shadow
drop-shadow-xs
drop-shadow-sm
blur-*
blur-sm
blur
blur-xs
blur-sm
backdrop-blur-*
backdrop-blur-sm
backdrop-blur
backdrop-blur-xs
backdrop-blur-sm
rounded-*
rounded-sm
rounded
rounded-xs
rounded-sm
ALWAYS run npx @tailwindcss/upgrade first ; it renames the call sites.
NEVER rely on visual parity after upgrade without checking each shadowed
card, input ring, and rounded badge.
Removed Without Replacement
Feature
v3 syntax
v4 status
Workaround
Disable specific utilities
corePlugins: { float: false }
REMOVED
Use @source not "path/to/file" to scope content scan, or post-process
Custom separator
separator: '_'
REMOVED
None ; refactor templates to use :
JS theme access at runtime
import resolveConfig from 'tailwindcss/resolveConfig'
Tailwind v4 is the preprocessor ; remove the other pipeline
If a v3 project depends on any of these, STAY on v3.4 until the dependency is
removed. Migrating then failing to compile is the most common v4 upgrade trap.
<!-- v3 : "first child of any direct child", reading right-to-left --><ulclass="first:*:pt-0"><!-- v4 equivalent : "any direct child, first one", reading left-to-right --><ulclass="*:first:pt-0">
The upgrade tool rewrites common cases. ALWAYS manually review bespoke
arbitrary-variant stacks like [&>div]:hover:bg-red-500 combined with
structural selectors. See tailwind-syntax-variants for the full rule set.
v3 scoped stylesheets in Vue SFC <style>, Svelte component <style>, and
CSS modules ALWAYS had implicit theme context. v4 compiles each scoped block
in isolation, so @apply text-2xl fails with "Cannot apply unknown utility
class".
<!-- v4 : requires @reference at the top of the scoped block -->
<style scoped>
@reference "../app.css";
.btn { @apply px-4 py-2 bg-blue-500 text-white; }
</style>
ALWAYS add @reference to every scoped <style> block in v4 projects.
NEVER assume @apply works without it. See tailwind-errors-v4-migration
for the full failure mode catalogue.
START
│
├── Browser support requires IE11, Safari <16.4, Chrome <111, or Firefox <128 ?
│ └── YES → STAY v3.4 (hard blocker, v4 cannot work)
│
├── Project uses corePlugins to disable utilities ?
│ └── YES → STAY v3.4 until the disabled utilities are removed
│ from source instead (no v4 replacement exists)
│
├── Project uses Sass, Less, or Stylus preprocessor ?
│ └── YES → STAY v3.4 until preprocessor is removed
│
├── Project uses resolveConfig() in runtime JS (e.g. a charting lib that
│ needs tailwind theme colours at runtime) ?
│ └── YES → Refactor to read `getComputedStyle(...).getPropertyValue('--color-*')`,
│ THEN upgrade
│
├── Project uses safelist with regex patterns ?
│ └── YES → Rewrite as `@source inline("...")` with brace expansion,
│ THEN upgrade
│
├── Project is Vue / Svelte / CSS-modules with scoped <style> blocks ?
│ └── YES → UPGRADE possible, BUT add `@reference "../app.css";` to
│ every scoped block first or post-upgrade
│
└── None of the above → UPGRADE
├── Run `npx @tailwindcss/upgrade` on a clean branch (Node 20+)
├── Apply the v3-default shims for border-color and ring (if visual
│ parity matters more than clean v4 code)
└── Verify shadow / blur / rounded / drop-shadow / backdrop-blur
call sites visually (sizes shifted by one step)
Partial Upgrade Strategy
v4 ALWAYS supports the v3 JS config via @config "./tailwind.config.js",
so a partial upgrade path is :
Move to the v4 engine (replace @tailwind with @import "tailwindcss").
Keep @config "./tailwind.config.js"; at the top of the stylesheet.
Migrate theme.extend keys to @theme block one category at a time.
Remove @config when nothing remains in the JS config.
NEVER mix @theme and theme.extend for the SAME token category : the v4
@theme wins but the duplication causes silent drift. Migrate per category
and delete the JS-config entry in the same commit.
Cross-References
Topic
Skill
Upgrade execution (codemod, validation, rollback)
tailwind-impl-migration-v3-v4
Trap catalogue when an upgrade breaks builds
tailwind-errors-v4-migration
Variant stacking rules (with v3 vs v4 examples)
tailwind-syntax-variants
Dark mode setup (v3 darkMode vs v4 @custom-variant dark)
tailwind-syntax-dark-mode
Plugin authoring (v3 JS vs v4 CSS-first)
tailwind-impl-plugins-custom
Build integration per framework
tailwind-impl-build-*
Reference Files
references/methods.md : complete API breaking-changes table (removed, renamed, new)
references/examples.md : side-by-side v3 vs v4 code for install, config, dark mode, plugins, scoped styles
references/anti-patterns.md : upgrade traps with root cause and fix