tailwindcss correction pack · for projects on tailwindcss@^4
What we actually measured
4 Claude models were asked for idiomatic tailwindcss code with no tools, purely from training knowledge. 7 reproduced failures across 11 runs, each verified against the release that broke the belief.
| Model | Stated cutoff | tailwindcss version attribution stops | Lag inside the window |
|---|---|---|---|
| Claude Fable 5.1 | 2026-06 | 4.1.0 · 2025-04-01 | ~14 months |
| Claude Opus 5 | 2026-05 | 4.1.0 · 2025-04-01 | ~13 months |
| Claude Fable 5 | 2026-01 | 4.1.0 · 2025-04-01 | ~9 months |
| Claude Sonnet 5 | 2026-01 | 4.1.0 · 2025-04-01 | ~9 months |
A recent cutoff is not a defence. Every model here loses track of this library's release history well before the date it states as its own cutoff, and the stopping points cluster far tighter than the cutoffs do.
Read that column precisely. It is the newest tailwindcss release whose contents the model can correctly attribute to that release — not the newest tailwindcss feature it knows. Past that point a model will often write working code with a newer API while naming the wrong release for it, and that guess runs early — it names a release older than the one that shipped the feature. So the question this pack answers is not "does the model know this API" but "can it be trusted about which version the API arrived in" — which is the question that matters when you are pinned to a version.
Latest tailwindcss is 4.3.3 (verified 2026-09-06). Separately from the corrections below, 1 of 11 runs recorded a version fact — the model named a current tailwindcss version from memory and was behind. If a model states a version without checking, assume it is behind and check the registry.
How to read an entry
Every entry ends with a Reproduced against line. Where it names models, we have the generated code that got it wrong, dated, with the model's own words in the run write-up. Where it says no model yet, the correction is verified from the release notes but nothing has been probed for it — it is a fix, not a measurement, and the pack says so rather than blurring the two.
The section an entry sits in is the worst case if you act on the stale belief. The severity in brackets after a model's name is what that particular model's output actually did, which can be milder — a model can hold the wrong belief and still, on the day, write code that runs.
The corrections
Breaks the build, or throws at runtime
Act on the stale belief here and the code does not run. Fix these first.
@apply inside Vue/Svelte/Astro <style> blocks and CSS modules
New requirement in tailwindcss 4.0.0 (2025-01-21)
Stylesheets bundled separately from the main CSS file — single-file-component <style> blocks, CSS module files — have no access to your theme variables, custom utilities or custom variants, so @apply fails there. Pull the definitions in first with @reference, pointing at your CSS entry file so custom @theme tokens resolve. @reference adds no CSS to the bundle.
The stale belief: That @apply works in any stylesheet the build touches, as it did in v3.
<style scoped>
h1 { @apply text-2xl font-bold text-brand; }
</style>
<style scoped>
@reference "../assets/main.css"; /* your entry file, so @theme tokens resolve */
h1 { @apply text-2xl font-bold text-brand; }
</style>
@reference "tailwindcss";is sufficient only if you use no custom theme tokens. Using the generated CSS variables directly (color: var(--color-brand)) avoids the problem and is faster.
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Using @apply with Vue, Svelte, or CSS modules"
@layer utilities / @layer components (for defining custom classes)
Removed in tailwindcss 4.0.0 (2025-01-21)
v4 uses native CSS cascade layers and no longer hijacks the @layer at-rule, so a class defined inside @layer utilities is no longer registered as a utility and gets no variant support (hover:, md:). Custom utilities are declared with the @utility API, which also accepts functional forms.
The stale belief: That wrapping a class in @layer utilities is how you add a custom utility that works with variants.
/* Stale */
@layer utilities {
.tab-4 { tab-size: 4; }
}
/* Current */
@utility tab-4 {
tab-size: 4;
}
/* functional form, matching tab-2, tab-4, tab-[13] */
@utility tab-* {
tab-size: --value(integer);
}
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Adding custom utilities" · Tailwind CSS docs — Adding custom styles, functional utilities
@tailwind base / @tailwind components / @tailwind utilities
Removed in tailwindcss 4.0.0 (2025-01-21)
The three @tailwind directives were removed. A v4 CSS entry file pulls the framework in with a plain CSS import: @import "tailwindcss";.
The stale belief: That a Tailwind entry stylesheet starts with @tailwind base; @tailwind components; @tailwind utilities;.
/* Stale */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Current */
@import "tailwindcss";
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Removed @tailwind directives"
bg-[--var] (CSS variable shorthand in arbitrary values)
Renamed in tailwindcss 4.0.0 (2025-01-21)
The bare-variable shorthand inside arbitrary values moved from square brackets to parentheses: bg-[--brand] becomes bg-(--brand). The explicit longhand bg-[var(--brand)] is unaffected and still works.
The stale belief: That bg-[--brand] resolves the custom property.
<div class="bg-[--brand]"></div>
<div class="bg-(--brand)"></div>
<div class="bg-[var(--brand)]"></div>
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Variables in arbitrary values"
bg-opacity-* / text-opacity-* / border-opacity-* / ring-opacity-* / placeholder-opacity-* / divide-opacity-*
Removed in tailwindcss 4.0.0 (2025-01-21)
The separate opacity utilities were removed. Use the slash opacity modifier on the colour utility itself.
The stale belief: That translucency is expressed as a second class, bg-black bg-opacity-50.
<div class="bg-black bg-opacity-50"></div>
<div class="bg-black/50"></div>
No CSS is emitted for the removed class, so the element is simply opaque. Nothing errors — the failure is visual.
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Removed deprecated utilities"
commas in grid-cols-[…] / grid-rows-[…] / object-[…] arbitrary values
Stricter in tailwindcss 4.0.0 (2025-01-21)
v3 replaced commas with spaces inside these particular arbitrary values, a compatibility hack carried over from v2. That is gone: use underscores for spaces, as everywhere else in arbitrary values.
The stale belief: That grid-cols-[max-content,auto] means two columns.
<div class="grid-cols-[max-content,auto]"></div>
<div class="grid-cols-[max-content_auto]"></div>
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Arbitrary values in grid and object-position utilities"
container (center / padding configuration)
Removed in tailwindcss 4.0.0 (2025-01-21)
The container utility's center and padding configuration options no longer exist. Extend the utility itself instead.
The stale belief: That container centring and padding are set under theme.container in the JS config.
/* Stale */
// tailwind.config.js
theme: { container: { center: true, padding: '2rem' } }
/* Current */
@utility container {
margin-inline: auto;
padding-inline: 2rem;
}
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Container configuration"
prefix
Renamed in tailwindcss 4.0.0 (2025-01-21)
Prefixes now look like variants and sit at the front of the whole class: tw:flex, tw:hover:bg-red-600. The prefix is declared on the import, and theme variables are still authored unprefixed.
The stale belief: That the prefix is glued to the utility name after any variants, hover:tw-bg-red-600.
<div class="tw-flex tw-bg-red-500 hover:tw-bg-red-600"></div>
/* CSS */
@import "tailwindcss" prefix(tw);
<div class="tw:flex tw:bg-red-500 tw:hover:bg-red-600"></div>
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Using a prefix"
resolveConfig
Removed in tailwindcss 4.0.0 (2025-01-21)
The resolveConfig export was removed. Theme values are real CSS custom properties at runtime, so reference them directly (var(--color-blue-500)), or read one in JS with getComputedStyle(document.documentElement).getPropertyValue("--shadow-xl").
The stale belief: That you import resolveConfig from tailwindcss/resolveConfig to get theme values into JavaScript.
// Stale
import resolveConfig from 'tailwindcss/resolveConfig';
import config from './tailwind.config.js';
const { theme } = resolveConfig(config);
// Current
const styles = getComputedStyle(document.documentElement);
const shadow = styles.getPropertyValue("--shadow-xl");
// or just use the variable in CSS/JS values:
<motion.div animate={{ backgroundColor: "var(--color-blue-500)" }} />
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Theme values in JavaScript"
safelist / corePlugins / separator
Removed in tailwindcss 4.0.0 (2025-01-21)
The safelist, corePlugins and separator options are not supported in v4, from a JS config or anywhere else. Their replacement did not ship with the removal: @source inline(...) — whose argument is brace-expanded — arrived in 4.1.0 (2025-04-01), ten weeks after 4.0.0 removed safelist. On 4.0.x there is no safelisting mechanism at all, and @source inline("...") is a build error there: the 4.0 parser accepts a quoted path only and throws `@source paths must be quoted.`
The stale belief: That runtime-assembled class names are rescued by a safelist array in tailwind.config.js.
The code below needs tailwindcss 4.1.0 or later. Between 4.0.0 and 4.1.0 this correction does not apply — see the note.
/* Stale */
// tailwind.config.js
module.exports = {
safelist: ['bg-red-500', { pattern: /bg-(red|green)-(100|500)/, variants: ['hover'] }],
};
/* Current */
@import "tailwindcss";
@source inline("{hover:,}bg-{red,green}-{100,500}");
@source inline("{hover:,}bg-red-{50,{100..900..100},950}"); /* ranges work too */
/* on 4.0.x there is no replacement: write the complete class names
into a lookup map so the scanner sees them literally. */
Bisected in the published packages 2026-09-02: the
@sourceparser handlesinline((andnot) from 4.1.0; the string is absent from the dist of 4.0.0, 4.0.9, 4.0.12, 4.0.15 and 4.0.17 — the last 4.0.x — where the parser takes a quoted path only. The upgrade-guide sentence quoted below is on the current docs page and describes 4.1+, not the 4.0.0 release it appears under; this fact repeated that gap until it was re-verified. The better fix where you control the code is still to write complete class names into a lookup map so the scanner sees them;@source inline()is for strings that genuinely cannot be static.
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Using a JavaScript config file" · Tailwind CSS docs — Detecting classes in source files, "Safelisting specific utilities" · tailwindcss 4.1.0 — shipped package (dist), @source inline( parser present; absent through 4.0.17 · 2025-04-01
Sass / Less / Stylus
New requirement in tailwindcss 4.0.0 (2025-01-21)
v4 is not designed to be used with a CSS preprocessor. Tailwind is the preprocessor: you cannot use Sass, Less or Stylus for your stylesheets or for <style lang="scss"> blocks in Vue, Svelte or Astro.
The stale belief: That Tailwind composes with Sass the way v3 tolerated.
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Using Sass, Less, and Stylus"
tailwind.config.js
Removed in tailwindcss 4.0.0 (2025-01-21)
JavaScript config files are still supported but are no longer detected automatically. A tailwind.config.js sitting in the project root is simply ignored, so every utility derived from it (bg-brand, font-display) silently emits nothing. Configure the theme in CSS with @theme, or load the legacy file explicitly with @config.
The stale belief: That dropping a tailwind.config.js in the project root configures the build, and that theme.extend is where custom colours and fonts go.
/* Stale */
// tailwind.config.js — not detected in v4
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: { extend: { colors: { brand: '#4f46e5' } } },
};
/* Current */
/* src/index.css */
@import "tailwindcss";
@theme {
--color-brand: #4f46e5; /* generates bg-brand, text-brand, border-brand, ... */
--font-display: "Satoshi", sans-serif; /* generates font-display */
}
/* or, to keep a legacy JS config: */
@config "../../tailwind.config.js";
corePlugins,safelistandseparatorare not supported from a JS config in v4 at all — see LF12.
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Using a JavaScript config file"
tailwindcss as a PostCSS plugin / npx tailwindcss
Removed in tailwindcss 4.0.0 (2025-01-21)
The tailwindcss package is no longer itself the PostCSS plugin or the CLI. The PostCSS plugin moved to @tailwindcss/postcss, the CLI to @tailwindcss/cli, and Vite projects have a dedicated @tailwindcss/vite plugin, which is the documented install path. Import handling and vendor prefixing are built in, so postcss-import and autoprefixer can be removed.
The stale belief: That you install tailwindcss postcss autoprefixer, run npx tailwindcss init -p, and list tailwindcss: {} among your PostCSS plugins.
// Stale
// postcss.config.mjs (v3)
export default { plugins: { "postcss-import": {}, tailwindcss: {}, autoprefixer: {} } };
// Current
// vite.config.ts — the documented v4 path for Vite
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({ plugins: [tailwindcss()] });
// or PostCSS, for non-Vite toolchains:
// postcss.config.mjs
export default { plugins: { "@tailwindcss/postcss": {} } };
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Using PostCSS" / "Using Tailwind CLI" · Tailwind CSS docs — Installation, Using Vite
Runs, but is silently wrong
Nothing errors. The behaviour is simply not what a model trained earlier will tell you.
node_modules scanning
Default changed in tailwindcss 4.1.0 (2025-04-01)
As of 4.1.0 node_modules is ignored by default when detecting classes. A component library shipping Tailwind classes in its published files needs an explicit @source pointing into it.
The stale belief: That classes inside an installed package are picked up automatically.
@import "tailwindcss";
@source "../node_modules/@acmecorp/ui-lib";
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: tailwindcss CHANGELOG — 4.1.0 (2025-04-01), Changed · 2025-04-01 · Tailwind CSS docs — Detecting classes in source files
border / divide (default colour)
Default changed in tailwindcss 4.0.0 (2025-01-21)
The default colour of border-* and divide-* changed from the configured gray-200 to currentColor, matching browser defaults. An uncoloured border now renders in the element's text colour — usually near-black — where v3 rendered a light grey.
The stale belief: That class="border" draws a subtle grey hairline.
<div class="border rounded-lg p-4">…</div>
<div class="border border-gray-200 rounded-lg p-4">…</div>
<style>
@layer base {
*, ::after, ::before, ::backdrop, ::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}
</style>
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Default border color"
hover variant
Behaviour changed in tailwindcss 4.0.0 (2025-01-21)
hover: is now wrapped in @media (hover: hover), so it does not fire on touch devices that previously triggered hover on tap. Treat hover as an enhancement; if you genuinely depend on the old behaviour, override the variant.
The stale belief: That hover: styles apply on touch devices when the user taps.
/* Stale */
/* v3 */ .hover\:underline:hover { text-decoration: underline; }
/* Current */
/* v4 */ @media (hover: hover) { .hover\:underline:hover { text-decoration: underline; } }
/* to opt out: */
@custom-variant hover (&:hover);
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Hover styles on mobile"
outline-none
Renamed in tailwindcss 4.0.0 (2025-01-21)
In v3, outline-none did not set outline-style: none — it set an invisible outline that still appeared in forced-colors mode for accessibility. That behaviour is now called outline-hidden. The v4 outline-none really does remove the outline. Code carrying focus:outline-none from v3 therefore loses its forced-colors-mode focus indicator.
The stale belief: That focus:outline-none is the safe way to suppress the browser's focus ring before drawing your own.
<input class="focus:outline-none" />
<input class="focus:outline-hidden" />
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Renamed outline utility"
Preflight base styles
Default changed in tailwindcss 4.0.0 (2025-01-21)
Three Preflight defaults changed: placeholder text uses the current text colour at 50% opacity rather than gray-400; buttons use cursor: default rather than cursor: pointer; and margins are reset on <dialog>, so dialogs are no longer centred by default. Each is restorable with a small @layer base block.
The stale belief: That buttons get a pointer cursor and dialogs are centred out of the box.
@layer base {
button:not(:disabled), [role="button"]:not(:disabled) { cursor: pointer; }
input::placeholder, textarea::placeholder { color: var(--color-gray-400); }
dialog { margin: auto; }
}
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Preflight changes"
ring
Default changed in tailwindcss 4.0.0 (2025-01-21)
The bare ring utility changed from a 3px blue-500 ring to a 1px currentColor ring. A v3 focus style written as focus:ring is therefore now a thin ring in the element's own text colour — on a coloured button with white text, effectively invisible. Use ring-3 plus an explicit ring-<color> to restore the v3 appearance.
The stale belief: That focus:ring alone produces the familiar 3px blue focus ring.
<button class="focus:ring">Save</button>
<button class="focus-visible:ring-3 focus-visible:ring-blue-500">Save</button>
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Default ring width and color"
shadow-sm / shadow / rounded-sm / rounded / blur-sm / blur
Renamed in tailwindcss 4.0.0 (2025-01-21)
The shadow, radius and blur scales were shifted one step so every utility has a named value. What v3 called shadow is now shadow-sm, and v3's shadow-sm is now shadow-xs. The same applies to rounded/rounded-sm, blur/blur-sm and drop-shadow/backdrop-blur. The old bare names still resolve, so nothing errors — they just render one step larger than the author intended.
The stale belief: That shadow-sm is the subtlest shadow in the scale.
<div class="shadow-sm rounded-sm blur-sm"></div>
<div class="shadow-xs rounded-xs blur-xs"></div>
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Updated shadow, radius, and blur scales"
space-x- / space-y- / divide-x- / divide-y- (selector)
Behaviour changed in tailwindcss 4.0.0 (2025-01-21)
The selector behind these utilities changed from > :not([hidden]) ~ :not([hidden]) to > :not(:last-child), for performance, and the spacing now hangs off the bottom of each child rather than the top of each sibling. Layouts using these with inline elements, or with per-child margin tweaks, can shift. Prefer flex/grid with gap.
The stale belief: That space-y-4 adds top margin to every child after the first.
/* Stale */
/* v3 */ .space-y-4 > :not([hidden]) ~ :not([hidden]) { margin-top: 1rem; }
/* Current */
/* v4 */ .space-y-4 > :not(:last-child) { margin-bottom: 1rem; }
/* recommended instead: */
/* <div class="flex flex-col gap-4"> */
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Space-between selector"
stacked variant order
Behaviour changed in tailwindcss 4.0.0 (2025-01-21)
Stacked variants now apply left to right instead of right to left, to read more like CSS. Order-sensitive stacks must be reversed: v3's first:*:pt-0 is v4's *:first:pt-0. In practice this bites the direct-child variant * and plugin variants like prose-headings.
The stale belief: That first:*:pt-0 targets the first child.
<ul class="py-4 first:*:pt-0 last:*:pb-0">…</ul>
<ul class="py-4 *:first:pt-0 *:last:pb-0">…</ul>
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Variant stacking order"
transform-none / rotate-* / scale-* / translate-*
Behaviour changed in tailwindcss 4.0.0 (2025-01-21)
These utilities are now built on the individual rotate, scale and translate CSS properties rather than the transform shorthand. Two consequences: transform-none no longer resets them (reset the individual property, e.g. scale-none), and a custom transition list containing transform no longer transitions them — list the individual properties, transition-[opacity,scale].
The stale belief: That transform-none clears a scale or rotation, and that transition-[opacity,transform] animates scale-*.
<button class="scale-150 focus:transform-none"></button>
<button class="transition-[opacity,transform] hover:scale-150"></button>
<button class="scale-150 focus:scale-none"></button>
<button class="transition-[opacity,scale] hover:scale-150"></button>
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Individual transform properties"
Deprecated, or a better API now exists
Works today. It is the older idiom, and some of it is scheduled for removal.
@container-size
Added in tailwindcss 4.3.0 (2026-05-08)
Since 4.3.0 the @container-size utility sets container-type: size, alongside the long-standing @container utility which sets container-type: inline-size. Use @container-size where a container query needs to read the container's block size as well as its inline size; @container is unchanged and is still the right default.
The stale belief: That @container is the only container-type utility, so container-type: size needs [container-type:size] or a hand-written rule.
<div class="[container-type:size]"></div>
<div class="@container-size">
<div class="@md:flex @lg:gap-4">…</div>
</div>
S3 under the workaround rule:
[container-type:size]compiles at 4.2.4 and at 4.3.0 and emits the same declaration. VERIFIED BY COMPILATION 2026-09-06 against installed 4.2.4, 4.3.0 and 4.3.3.@container-sizeemits nothing at 4.2.4 andcontainer-type: sizeat 4.3.0 and 4.3.3;@containeremitscontainer-type: inline-sizeat all three, so the older utility did not change.
Reproduced against: Claude Fable 5.1 (S2) — tailwindcss/v3-a, 2026-09-06.
Source: tailwindcss v4.3.0 release notes — Added · 2026-05-08 · tailwindcss 4.3.0, shipped package — @container-size compiles to container-type: size; @container still compiles to container-type: inline-size. · tailwindcss 4.2.4, shipped package — @container-size compiles to nothing; @container is already present.
@variant (stacked and compound variants)
Added in tailwindcss 4.3.0 (2026-05-08)
Since 4.3.0 the @variant at-rule accepts a stacked variant (@variant hover:focus { … }, applying when both conditions hold) and a comma-separated compound list (@variant hover, focus { … }, applying when any of them holds). Both spellings work with modifier-carrying variants too, so @variant group-hover:focus { … } is legal. Before 4.3.0 @variant took exactly one variant name and anything else was a BUILD ERROR: Cannot use @variant with unknown variant: hover:focus.
The stale belief: That @variant takes a single variant only, so combining two conditions means nesting one @variant block inside another, and a comma-separated list has no equivalent at all — a model holding this belief will tell the author of a working @variant hover:focus { … } block that Tailwind cannot parse it.
/* Stale */
@import "tailwindcss";
.btn {
@variant hover {
@variant focus {
outline: 2px solid magenta;
}
}
}
/* Current */
@import "tailwindcss";
.btn {
@variant hover:focus {
outline: 2px solid magenta;
}
/* and the compound form, which nesting cannot express at all */
@variant hover, focus, active {
outline-offset: 2px;
}
}
SEVERITY IS ASYMMETRIC ACROSS THE TWO HALVES AND THE FACT DOES NOT AVERAGE THEM. The stacked half is S3: the nested workaround compiles at 4.2.4 and at 4.3.0 and, verified by compilation, produces BYTE-IDENTICAL output to the stacked form at 4.3.0, so a reader who nests loses nothing. The compound half has no nested equivalent — nesting means AND, the comma means OR — so a reader who believes the comma is illegal writes the block out once per variant instead. That is still working CSS, which is why the fact as a whole stays S3 rather than S2; the S2 direction is review, where a draw tells the author of a working
@variant hover:focusblock that it is a build error. VERIFIED BY COMPILATION 2026-09-06 with the real engine. At 4.2.4@variant hover:focus,@variant group-hover:focusand@variant hover, focus, activeeach THROWCannot use @variant with unknown variant: …— a hard build failure, not a silent no-op — while@variant hoverand the nested form compile. At 4.3.0 and 4.3.3 all five compile.
Reproduced against: Claude Fable 5.1 (S3) — tailwindcss/v3-a, 2026-09-06.
Source: tailwindcss v4.3.0 release notes — Added · 2026-05-08 · tailwindcss v4.3.0 release notes — Added · 2026-05-08 · tailwindcss 4.3.0, shipped package — @variant hover:focus { outline: 2px solid magenta } compiles to &:hover { @media (hover: hover) { &:focus { outline: 2px solid magenta } } }, identical to the nested form; @variant hover, focus { … } compiles to two separate rules. · tailwindcss 4.2.4, shipped package — the same stacked and compound sources throw Cannot use @variant with unknown variant: hover:focus / : hover, focus; the single-variant and nested forms compile.
scrollbar-auto / scrollbar-thin / scrollbar-none / scrollbar-thumb-* / scrollbar-track-* / scrollbar-gutter-*
Added in tailwindcss 4.3.0 (2026-05-08)
Since 4.3.0 Tailwind ships first-class scrollbar utilities and no plugin is needed. scrollbar-auto, scrollbar-thin and scrollbar-none set scrollbar-width. scrollbar-thumb-* and scrollbar-track-* set scrollbar-color — each writes one half into a CSS variable (--tw-scrollbar-thumb / --tw-scrollbar-track) and both emit the same scrollbar-color declaration, so use them as a pair or the unset half falls back to the variable's default. scrollbar-gutter-auto and scrollbar-gutter-stable set scrollbar-gutter. Colour utilities take the theme palette and arbitrary values alike: scrollbar-thumb-slate-500 and scrollbar-thumb-[#123456] both compile. Do not install tailwind-scrollbar and do not hand-write a ::-webkit-scrollbar block for the standard properties.
The stale belief: That Tailwind has no scrollbar utilities, so styling a scrollbar means installing the third-party tailwind-scrollbar plugin, writing ::-webkit-scrollbar rules in your own CSS, or reaching for arbitrary properties. A model holding this belief will also reject working scrollbar-thin scrollbar-thumb-slate-500 markup in review as classes that generate nothing.
<div class="overflow-y-auto scrollbar-thin"></div>
<style>
.panel::-webkit-scrollbar { width: 8px }
.panel::-webkit-scrollbar-thumb { background: #64748b }
</style>
<div class="overflow-y-auto scrollbar-thin scrollbar-thumb-slate-500 scrollbar-track-slate-100 scrollbar-gutter-stable">
…
</div>
SEVERITY IS S3 AND THE REASON MATTERS, exactly as on LF30. The arbitrary-property workaround a stale model offers (
[scrollbar-width:thin],[scrollbar-color:red_blue]) compiles at 4.2.4 AND at 4.3.0 and emits the identical declaration, so acting on the stale belief costs verbosity, not correctness, and the workaround rule (HARNESS.md, JOURNAL/030) forbids charging it as a defect. Two directions are worse and are S2 where a run shows them: telling the author of workingscrollbar-thinmarkup that it generates nothing, and sending the reader to a third-party plugin or to::-webkit-scrollbar— the latter is not equivalent, because::-webkit-scrollbaris unimplemented in Firefox whilescrollbar-widthandscrollbar-colorare the standard properties this family emits. VERIFIED BY COMPILATION 2026-09-06: every class incorrect_codebuilt one at a time with the real engine against installed 4.1.0, 4.2.4, 4.3.0 and 4.3.3. All of them emit nothing at 4.1.0 and 4.2.4 and the expected declaration at 4.3.0 and 4.3.3 — a clean bisection onto the target release, with the whole 4.2.x line ruled out rather than just 4.2.0. THE SCHEME DOES NOT GENERALISE, and this is the family's guessing trap:scrollbar-corner-*,scrollbar-hidden,scrollbar-width-thinandscrollbar-thumb-roundedare absent from 4.2.4, 4.3.0 and 4.3.3 alike and compile to nothing at all three. There is no corner utility; CSSscrollbar-colortakes exactly two colours and the utilities cover exactly those two.
Reproduced against: Claude Fable 5.1 (S2) — tailwindcss/v3-a, 2026-09-06.
Source: tailwindcss v4.3.0 release notes — Added · 2026-05-08 · tailwindcss v4.3.0 release notes — Added · 2026-05-08 · tailwindcss 4.3.0, shipped package — the engine compiles scrollbar-thin to scrollbar-width: thin, scrollbar-thumb-red-500 to --tw-scrollbar-thumb: var(--color-red-500); scrollbar-color: var(--tw-scrollbar-thumb) var(--tw-scrollbar-track), and scrollbar-gutter-stable to scrollbar-gutter: stable. · tailwindcss 4.2.4, shipped package — every one of those class names compiles to nothing, at the last release of the 4.2 line. The family begins at 4.3.0, not earlier in 4.2.x. · tailwindcss 4.3.3, shipped package — scrollbar-corner-red-500, scrollbar-hidden, scrollbar-width-thin and scrollbar-thumb-rounded are absent and compile to nothing at the newest release that exists. · Tailwind CSS docs — scrollbar-width (Interactivity), documented at v4.3
zoom-* / tab-*
Added in tailwindcss 4.3.0 (2026-05-08)
Since 4.3.0 Tailwind ships zoom-* utilities for the CSS zoom property and tab-* utilities for tab-size. zoom-* takes a percentage-style number — zoom-50 emits zoom: 50%, zoom-150 emits zoom: 150% — and tab-* takes a plain integer, so tab-4 emits tab-size: 4. Both accept arbitrary values: zoom-[1.5] emits zoom: 1.5 (a bare ratio, not a percentage) and tab-[3] emits tab-size: 3.
The stale belief: That neither CSS zoom nor tab-size has a Tailwind utility, so both need arbitrary properties or hand-written CSS.
<pre class="[tab-size:4]"></pre>
<div class="[zoom:0.5]"></div>
<pre class="tab-4"></pre>
<div class="zoom-50"></div>
S3 for the same reason as LF31:
[tab-size:4]and[zoom:1.5]compile at 4.2.4 and at 4.3.0 and emit the identical declaration, so the stale belief costs verbosity rather than correctness and cannot be charged as a defect under the workaround rule. Note the unit asymmetry, which is the one place a reader can go wrong translating between the two spellings: the named scale is a percentage (zoom-50->zoom: 50%) while the arbitrary form is passed through untouched (zoom-[1.5]->zoom: 1.5, which is 150%, not 1.5%).zoom-normalandzoom-resetdo not exist. VERIFIED BY COMPILATION 2026-09-06 against installed 4.1.0, 4.2.4, 4.3.0 and 4.3.3:zoom-50,zoom-25,zoom-150,tab-1,tab-4andtab-8emit nothing at 4.1.0 and 4.2.4 and the expected declaration at 4.3.0 and 4.3.3.
Reproduced against: Claude Fable 5.1 (S2) — tailwindcss/v3-a, 2026-09-06.
Source: tailwindcss v4.3.0 release notes — Added · 2026-05-08 · tailwindcss v4.3.0 release notes — Added · 2026-05-08 · tailwindcss 4.3.0, shipped package — zoom-50 compiles to zoom: 50%, zoom-[1.5] to zoom: 1.5, tab-4 to tab-size: 4. · tailwindcss 4.2.4, shipped package — zoom-50, zoom-[1.5], tab-4 all compile to nothing, while [zoom:1.5] and [tab-size:4] compile there and at 4.3.0 alike. · Tailwind CSS docs — zoom, documented at v4.3 · Tailwind CSS docs — tab-size, documented at v4.3
pbs-* / pbe-* / mbs-* / mbe-* / border-bs-* / inline-* / block-* / min-inline-* / max-block-* / inset-bs-*
Added in tailwindcss 4.2.0 (2026-02-18)
Since 4.2.0 Tailwind ships first-class utilities for the CSS block axis and for logical sizing. Spacing: pbs-*, pbe-*, mbs-*, mbe-*, and the scroll variants scroll-pbs-*, scroll-pbe-*, scroll-mbs-*, scroll-mbe-*. Borders: border-bs-*, border-be-*. Sizing: inline-*, min-inline-*, max-inline-* for inline-size and its bounds, and block-*, min-block-*, max-block-* for block-size. Insets: inset-s-*, inset-e-*, inset-bs-*, inset-be-*. Do not reach for [padding-block-start:...] arbitrary values and do not substitute the physical pt-* / mb-* / h-* utilities, which are not equivalent in a vertical writing mode. The INLINE axis is the older half of the family and keeps its v3 spelling: ps-*, pe-*, ms-*, me-*. There is no pis-*, pie-*, mis-*, mie-* or border-is-* at any release, 4.3.3 included.
The stale belief: That Tailwind covers the inline axis only (ps-*, ms-*, start-*, end-*) and offers nothing for padding-block-start, border-block-start, inline-size or block-size, so a writing-mode-agnostic component needs arbitrary values or hand-written CSS. A model holding this belief will also reject working pbs-6 / inline-full markup in review as classes that generate nothing.
<article class="pt-6 pb-6 mb-4 border-t-2 w-full max-h-96">
<article class="[padding-block-start:1.5rem] [inline-size:100%]">
<article class="pbs-6 pbe-6 mbe-4 border-bs-2 inline-full max-block-96">
<div class="ps-4 me-4"></div>
</article>
SEVERITY IS S3 AND THE REASON MATTERS. The arbitrary-value workaround a stale model offers (
[padding-block-start:1.5rem]) compiles and emits the same declaration, so acting on the stale belief costs verbosity, not correctness - the workaround rule (HARNESS.md, JOURNAL/030) forbids charging that as a defect. The S2 direction is REVIEW: a model that tells the author of workingpbs-6markup that the class does not exist and emits nothing is asserting something the compiler contradicts, and a finding charged in that direction states S2 on the run. Substitutingpt-*forpbs-*is also S2, but only in a non-horizontal writing mode - inwriting-mode: horizontal-tbthe two are identical, which is why this fact does not claim the substitution is broken in general. VERIFIED BY COMPILATION 2026-09-03, every class incorrect_codebuilt one at a time against installed 4.1.0, 4.2.0 and 4.3.3 with the real engine. Every 4.2.0 name emits nothing at 4.1.0 and the expected declaration at 4.2.0;ps-4andme-4emit at both;pis-4,pie-4,mis-4,mie-4andborder-is-2emit at none of the three. That last check is the point of thepis-*sentence in the statement: the family has a naming scheme a reader (or a model) will over-generalise, and four of the names it suggests do not exist. Not to be confused with the display utilities.inline,block,inline-blockandinline-flexare unchanged and still setdisplay;inline-4andblock-fullare the new sizing utilities and do not collide with them.
Reproduced against: Claude Opus 5 (S2) — tailwindcss/v2-a, 2026-09-03.
Source: tailwindcss CHANGELOG - 4.2.0 (2026-02-18), Added · 2026-02-18 · tailwindcss CHANGELOG - 4.2.0 (2026-02-18), Added · 2026-02-18 · tailwindcss CHANGELOG - 4.2.0 (2026-02-18), Added · 2026-02-18 · tailwindcss 4.2.0, shipped package - dist/ registers pbs, pbe, mbs, mbe, border-bs, border-be, inset-s, inset-e, inset-bs, inset-be, scroll-pbs and scroll-mbe, and the engine compiles pbs-6 to padding-block-start: calc(var(--spacing) * 6) and inline-full to inline-size: 100%. · tailwindcss 4.1.0, shipped package - none of those names is registered and every one of them compiles to nothing, while ps-4 and me-4 compile normally. The family begins at 4.2.0 and the inline axis predates it. · tailwindcss 4.3.3, shipped package - pis-*, pie-*, mis-*, mie-* and border-is-* are absent from dist/ and compile to nothing at the newest release that exists. The scheme does not generalise to the inline axis.
start-* / end-*
Deprecated in tailwindcss 4.2.0 (2026-02-18)
As of 4.2.0 the logical inset utilities start-* and end-* are deprecated in favour of inset-s-* and inset-e-*, alongside a new family of logical-property utilities (pbs-*, mbe-*, border-bs-*, inline-*, block-*).
The stale belief: That start-0 / end-0 are the current logical inset utilities.
<div class="absolute start-0 end-0"></div>
<div class="absolute inset-s-0 inset-e-0"></div>
Published 2026-02-18, after the stated cutoff of two of the three models in this Index. Not chargeable against them - a scheduled retest, not a pass. VERIFIED AGAINST THE COMPILER 2026-09-03, because JOURNAL/041 had just found two tailwindcss facts that read a status word out of a release note and were wrong about the artifact. This one is right, and deprecated really does mean deprecated: at 4.2.0
start-0andinset-s-0compile to the SAME declaration (inset-inline-start: calc(var(--spacing) * 0)), so nobody's working markup broke. Do not restate this fact asremoved. The only difference the compiler shows is cosmetic and appears later: at 4.3.3inset-s-0emits the constant-foldedinset-inline-start: 0pxwhilestart-0still emits thecalc()form. Same computed value.
Reproduced against: Claude Opus 5 (S3) — tailwindcss/v2-a, 2026-09-03.
Source: tailwindcss CHANGELOG — 4.2.0 (2026-02-18), Deprecated · 2026-02-18 · tailwindcss 4.2.0, shipped package - both spellings compile, and to the same declaration. start-0 -> inset-inline-start: calc(var(--spacing) * 0); inset-s-0 -> the same string. · tailwindcss 4.1.0, shipped package - inset-s-0 and inset-e-0 generate no CSS at all, and dist/ registers only the start and end utility names. The deprecation and its replacement both begin at 4.2.0.
bg-gradient-*
Deprecated in tailwindcss 4.0.0 (2025-01-21)
Linear gradient utilities were renamed from bg-gradient-* to bg-linear-*, making room for the new bg-radial-* and bg-conic-* families and for angle values like bg-linear-45. The old spelling was kept as an alias, not removed: 4.x rewrites a bg-gradient-to-* candidate to bg-linear-to-* before compiling it, so v3 markup still renders a gradient. Adopt the new name for the angle and radial/conic families, not because the old one stops working. Gradients also keep their other stops when a variant overrides one — use via-none to drop back to a two-stop gradient in a given state.
The stale belief: That bg-gradient-to-r is the current spelling of the linear gradient utility. It is the v3 spelling, retained as an alias.
<div class="bg-gradient-to-r from-indigo-500 to-pink-500"></div>
<div class="bg-linear-to-r from-indigo-500 to-pink-500"></div>
<div class="bg-linear-45 from-indigo-500 via-purple-500 to-pink-500"></div>
VERIFIED 2026-09-03 (JOURNAL/041), against the shipped packages, and the statement was sharpened as a result. It previously said only that the utilities were "renamed", which reads as the old name being gone; the note under it hedged that the old name had not been verified to stop working. It has now been verified to keep working.
tools/audit/css-audit.mjsbuiltbg-gradient-to-ragainst tailwindcss 4.0.0 and 4.3.3: it generates at both, and at 4.0.0 its declarations are identical tobg-linear-to-r's. The alias is explicit in the shipped bundle — the candidate parser rewrites a root beginningbg-gradient-to-tobg-linear-to-— which is why the two directional families cannot diverge. Onlybg-linear-45and thebg-radial-*/bg-conic-*families are genuinely new surface.
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS v4.0 announcement — Expanded gradient APIs · 2025-01-22 · tailwindcss CHANGELOG — bg-linear- alias · tailwindcss 4.3.3, shipped package — dist/lib.mjs rewrites a bg-gradient-to- root to bg-linear-to-*
flex-shrink-* / flex-grow-* / overflow-ellipsis / decoration-slice / decoration-clone
Deprecated in tailwindcss 4.0.0 (2025-01-21)
These v3 aliases were dropped from the documentation, not from the compiler. flex-shrink-*, flex-grow-*, overflow-ellipsis, decoration-slice and decoration-clone are still registered utilities in every 4.x release and emit exactly the same declarations as shrink-*, grow-*, text-ellipsis, box-decoration-slice and box-decoration-clone. Markup carrying the old names keeps working and renders identically. Adopt the new spellings because the old ones are undocumented and no longer suggested by tooling — not because anything breaks.
The stale belief: That flex-shrink-0 is the current, documented spelling. It is the v3 spelling: it still compiles to flex-shrink: 0, and it is no longer in the docs.
<img class="flex-shrink-0" /> <div class="flex-grow">…</div>
<img class="shrink-0" /> <div class="grow">…</div>
CORRECTED 2026-09-03 (JOURNAL/041), against the shipped packages. This entry previously filed the five aliases as
removedat S1 breaks-build, on the upgrade guide's line "We've removed any utilities that were deprecated in v3". That line is about the documentation. The compiler never dropped them:tools/audit/css-audit.mjsbuilt each stale class against tailwindcss 4.0.0, 4.1.0, 4.2.0 and 4.3.3 and every one generated CSS byte-identical to the replacement this fact prescribes —.flex-shrink-0 { flex-shrink: 0; }beside.shrink-0 { flex-shrink: 0; }, and the same for the other four. The mechanism is visible in the shipped bundle: each name is registered withutilities.functional(...)orutilities.static(...)and paired withutilities.suggest(<name>, () => []), an empty suggestion list — registered, and hidden from tooling. A reader following the old entry was told their working markup broke the build. It does not. Same defect class as valibot LF3 (JOURNAL/040): a release note that is accurate about the intent and wrong about the artifact.
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Removed deprecated utilities" (accurate about the docs, not about the compiler) · tailwindcss 4.0.0, shipped package — dist/lib.mjs still registers flex-shrink, flex-grow, overflow-ellipsis, decoration-slice and decoration-clone · tailwindcss 4.3.3, shipped package — the same five, each beside an empty utilities.suggest() list
the important modifier (!flex)
Deprecated in tailwindcss 4.0.0 (2025-01-21)
The ! that marks a utility important moved from the start of the class name to the end: !flex becomes flex!, hover:!bg-red-600 becomes hover:bg-red-600!. The leading form is still supported for compatibility but is deprecated.
The stale belief: That the important marker goes after the variants and before the utility.
<div class="!flex hover:!bg-red-600"></div>
<div class="flex! hover:bg-red-600!"></div>
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "The important modifier"
theme() function
Deprecated in tailwindcss 4.0.0 (2025-01-21)
Prefer the generated CSS variables over theme(). The dot path is deprecated, not removed: theme(colors.red.500) and theme(screens.xl) still resolve in v4 and compile to exactly the same declarations as theme(--color-red-500) and theme(--breakpoint-xl). Write the CSS-variable form in new code — var(--color-red-500) outside media queries, theme(--breakpoint-xl) inside them, where CSS variables are not supported — but existing dot-path stylesheets are not broken and do not need an urgent migration.
The stale belief: That theme values are addressed with dot notation, theme(colors.red.500), and that this is the current idiom rather than a deprecated one.
/* Stale */
.my-class { background-color: theme(colors.red.500); }
@media (width >= theme(screens.xl)) { }
/* Current */
.my-class { background-color: var(--color-red-500); }
@media (width >= theme(--breakpoint-xl)) { }
Do not tell readers the dot path stopped working. Verified by compiling the four forms with the installed Tailwind engine at both 4.0.0 and 4.3.3:
theme(colors.red.500)andtheme(--color-red-500)both emitcolor: oklch(0.637 0.237 25.331), andtheme(screens.xl)andtheme(--breakpoint-xl)both emit@media (min-width: 80rem). The upgrade guide says you should use the variable name; it does not say the dot path is rejected, and the v4 line has gone on fixing dot-path resolution in JS plugins and config files (4.3.3 fixestheme('colors.foo')lookups). Fourth fact in this pack found asserting a break that the shipped artifact does not perform — see LF5 and LF27.
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Using the theme() function" (advice, not a removal) · tailwindcss CHANGELOG — 4.3.3 still fixing dot-path theme() resolution · 2026-07-16
Wrong facts about the library
Not code — versions, minimums and metadata that models state confidently and get wrong.
browser support baseline
New requirement in tailwindcss 4.0.0 (2025-01-21)
v4 targets Safari 16.4+, Chrome 111+ and Firefox 128+, because it depends on @property and color-mix() for core features. It will not work in older browsers; projects that must support them should stay on v3.4.
The stale belief: That upgrading to the current major is browser-support-neutral.
Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.
Source: Tailwind CSS docs — Upgrade guide, "Browser requirements"
Not corrections — recorded for honesty
Claims seen in a run but not yet verified against a primary source. Never treated as findings:
- The battery's two most derivable-looking probes were derived by the subject furthest below the floor, not the nearest. Claude Sonnet 5 (2026-01) produced the correct
@variantstacked and compound syntax and the correcttab-*scale; Claude Opus 5 (2026-05, three weeks nearer the release) got both wrong and derived only@container-size. Nothing in the Index predicts which control derives what, and distance below the floor evidently does not. (open since 2026-09-06) - Does the block-axis denial survive a probe that does not mention writing modes? Every task in this battery framed the need through vertical-rl, which is a rare enough layout that a subject may be reasoning "Tailwind would not bother" rather than recalling. A probe that asks for the same utilities in a plain horizontal document would separate the two, and it bounds how far F1 generalises: a belief held only under an exotic framing is a weaker prior than one held in ordinary markup. (open since 2026-09-03)
- The model gave the v4 shadow-sm value as
0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)and shadow-xs as0 1px 2px 0 rgb(0 0 0 / 0.05). The direction of the rename is verified against the upgrade guide; the exact pixel values were not checked against the shipped theme.css. (open since 2026-08-31) - The model gave shadow-sm as
0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1). The direction of the scale rename is verified against the upgrade guide, but the exact pixel values of the v4 shadow scale were not independently checked against the shipped theme.css. (open since 2026-08-31)
Findings, code and citations: data/tailwindcss/ — one JSON file and one write-up per model, each finding carrying the release that broke the belief, its publication date and a verbatim quote from the primary source. Corrections: data/tailwindcss/facts.json. This file is generated by tools/build-corrections.mjs; if the prose and the data ever disagree, that is a bug in the generator, not a stale pack.