tailwindcss correction pack

Paste into CLAUDE.md, .cursorrules, or any agent rules file on a project that uses tailwindcss.

tailwindcss correction pack · for projects on tailwindcss@^4

What we actually measured

6 Claude models were asked for idiomatic tailwindcss code with no tools, purely from training knowledge. 12 reproduced failures across 19 runs, each verified against the release that broke the belief.

ModelStated cutofftailwindcss version attribution stopsLag inside the window
Claude Fable 5.12026-064.1.0 · 2025-04-01~14 months
Claude Opus 52026-054.1.0 · 2025-04-01~13 months
Claude Fable 52026-014.1.0 · 2025-04-01~9 months
Claude Sonnet 52026-014.1.0 · 2025-04-01~9 months
Claude Opus 5.52026-064.2.0 · 2026-02-18~4 months
Claude Haiku 4.52025-024.0.0 · 2025-01-21~1 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 19 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.

Two dates, and they answer different questions. The entries below were last checked against their primary sources on 2026-09-06 — that date is the floor, and it is the only verification claim this file makes. Separately, 5 of the 35 entries have had their own text change since that sweep, the newest on 2026-09-28. A text change is a change to the record — a re-scored severity, a corrected release date, a clarified note — and it is not a re-check against the source. Do not read the later date as the fresher of two checks; per-entry dates are in substance_changed_on in the JSON.

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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);
}

CHALLENGED AND HELD 2026-09-23 (JOURNAL/205). This fact was queued for repair as false at the current release (JOURNAL/203, BACKLOG 0e(d)-i item 1) and the repair was refused: the fact is true and the instrument was wrong. The probe asked its question with the candidate tab-4, which is this fact’s own stale_code example, and tailwindcss ships a NATIVE tab-* utility from 4.3.0 — so at 4.3.0 and 4.3.3 the compiler answered hover:tab-4 out of its own utility set with no @layer block involved, and an absence test became a presence test on a different object. Re-measured with zzq-4, a name no release ships: a class defined in @layer utilities gets NO variant support at any v4 rung, 4.0.0 through 4.3.3, while @utility gets it at all five. Controls both ways — tab-4 is confirmed native from 4.3.0 with no custom block present, and zzq-4 is confirmed native at no rung.

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

bg-[--var] (CSS variable shorthand in arbitrary values)

Renamed in tailwindcss 4.0.0 (2025-01-21) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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"

prefix

Renamed in tailwindcss 4.0.0 (2025-01-21) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 6 releases, tailwindcss 3.4.17 through 4.3.3

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 @source parser handles inline( (and not ) 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) · boundary measured on 6 releases, tailwindcss 3.4.17 through 4.3.3

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, safelist and separator are 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) · boundary measured on 5 releases, tailwindcss 4.0.0 through 4.3.3

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

@tailwind base / @tailwind components / @tailwind utilities

Removed in tailwindcss 4.0.0 (2025-01-21) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

The @tailwind base and @tailwind components directives no longer do anything: on v4 each compiles to an empty sheet, so preflight and the components layer are gone. @tailwind utilities is still accepted and still emits the utility sheet. A v4 entry file pulls all three of preflight, theme and utilities in with one 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";

RE-SCORED 2026-09-23 (JOURNAL/207) from S1 breaks-build to S2 silently-wrong, measured against both of the consumers a reader actually runs. tools/audit/tw-postcss-severity.mjs installs @tailwindcss/postcss and @tailwindcss/cli at each rung's own version and builds this fact's own stale_code through both. It BUILDS in 10 of 10 v4 cells — five releases by two consumers — with no exception, no non-zero exit and no warning. What the reader is left holding is either the utility sheet with preflight absent (8 cells) or, through the PostCSS plugin at 4.0.0 and 4.1.0, a stylesheet with no rules in it at all: the three directives passed through untouched, 59 bytes in and 59 bytes out. Both outcomes are silent, so both score the same, and neither is breaks-build. Five controls hold on every cell, each throwing rather than returning a value: the ERA-CORRECT entry as witness (@import "tailwindcss"; emits 23 bytes carrying no utility on 3.4.17, so a witness pinned to it reports the era control as a broken install — the probe file's note 2 reaching a second instrument); a marker candidate per arm that no other arm's markup contains; the fictitious candidate zzq-4, which no release ships; two arms that MUST fail, one rejected by the parser and one importing a stylesheet that does not exist, because "it did not throw" and "this harness cannot report a throw" are otherwise the same output; and a byte-identity check for the adapter's per-input-path cache. Era control: on 3.4.17 this same entry IS the correct entry, and it emits preflight through both consumers (10,275b and 10,557b). THE 4.2.0 SPLIT IS NOT A LIBRARY BOUNDARY AND IS DELIBERATELY NOT FILED. Through the PostCSS plugin alone the utility sheet appears at 4.2.0 and at no rung below it, which reads exactly like a dated change and was one sentence from being filed as one. The CLI emits it at every v4 rung, 4.0.0 included. The two consumers DISAGREE at 4.0.0 and 4.1.0, so what the plugin's column shows is a difference between two of this library's own entry points and not something tailwindcss did at 4.2.0. A fact dated there would be a true measurement of a false claim about the library — JOURNAL/199's answer-from-the-wrong-corpus, with the corpus being a consumer. THE STATEMENT IS UNCHANGED, and that is a decision rather than an omission. Its clauses are what is true now, at 4.3.3, through both consumers; a sentence true now and false at a release below the fact's own introduced_in is what a dated fact already says (JOURNAL/201). This re-score corrects what getting the fact wrong COSTS, which is a different claim from what the fact asserts. No finding is charged against this fact's api, so no published severity moves with it. PREVIOUS CORRECTION, 2026-09-23 (JOURNAL/205). The statement previously read "The three @tailwind directives were removed", which is true of two of the three and was published for 23 days (first committed 2026-08-31, 671cc8d). Measured on the seven-rung ladder: @tailwind utilities; alone compiles to a working utility sheet at every v4 rung (93 bytes carrying .flex), while @tailwind base; emits 15 bytes against 9,901 at 3.4.0 and 10,210 at 3.4.17, and @tailwind components; emits 21. The directive is doing the work rather than being ignored — the control is an empty entry (0 bytes) and a comment-only entry (13 bytes), neither carrying the witness. That session read the entry through this package's own compile() and recorded, with its limit stated, that it did not throw; the limit was that a reader does not call compile(), and closing it is what JOURNAL/207 did.

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"

border / divide (default colour)

Default changed in tailwindcss 4.0.0 (2025-01-21) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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"

container (center / padding configuration)

New requirement in tailwindcss 4.0.0 (2025-01-21) · boundary measured on 6 releases, tailwindcss 3.4.17 through 4.3.3

The container utility's center and padding configuration options still work in v4, but only from a config loaded explicitly with @config. They are not gone — a config file sitting in the project root is simply never read (LF2), so a v3 project carried into v4 loses its centring and padding with no error. Either add @config "./tailwind.config.js"; to the entry stylesheet, or extend the utility in CSS with @utility container. Both work at every v4 release.

The stale belief: That a tailwind.config.js in the project root still configures the container, because theme.container.center and theme.container.padding are where those live.

/* Stale */
// tailwind.config.js, in the project root, with no @config in the entry stylesheet:
theme: { container: { center: true, padding: '2rem' } }
// v4 never reads this file. .container is emitted without either declaration.

/* Current */
/* Either — keep the JS config and load it explicitly: */
@config "./tailwind.config.js";
@import "tailwindcss";

/* or — drop the JS config and extend the utility in CSS: */
@import "tailwindcss";
@utility container {
  margin-inline: auto;
  padding-inline: 2rem;
}

CORRECTED AND RE-SCORED 2026-09-23 (JOURNAL/213), from S1 breaks-build to S2 silently-wrong, against a measurement rather than against the documentation. The statement previously read "The container utility's center and padding configuration options no longer exist", which is FALSE at every release this Index tracks and was published for 23 days (first committed 2026-08-31, 671cc8d). tools/audit/tw-project-facts.mjs builds a scratch project per arm and drives BOTH consumers a reader runs (@tailwindcss/postcss and @tailwindcss/cli, each pinned to the rung, each as a process). Loaded through @config, the options EMIT — margin-inline: auto and the configured padding — at 4.0.0, 4.1.0, 4.2.0, 4.3.0 and 4.3.3, through both consumers: ten v4 cells, no exception, no non-zero exit, no warning. THREE CONTROLS STAND BEHIND THAT, because an accusation against the library on one clean cell is JOURNAL/205. lf22-none is the BASELINE — the same @config mechanism and layout with no container key emits neither declaration, so v4's own container utility is not the source. lf22-value is the ATTRIBUTION — padding: '1.7rem', a value no release ships and no other arm writes, reaches .container at all five v4 rungs through both consumers; presence is a weaker claim than provenance. lf22-utility drives the fact's own prescribed correction and it works at every v4 rung, which is why it is now published as one of two routes rather than as the only one. WHAT IS DATED AT 4.0.0 IS THE DETECTION, AND IT IS EXECUTED RATHER THAN INFERRED FROM LF2: the lf22-auto arm (added with this correction) is lf22-config with the @config line removed and NOTHING else changed — same config, same key, same place — and the options are ignored at 4.0.0 and above and honoured at 3.4.17, contiguous, both consumers. Inferring this half from LF2 would have been a boundary read off a neighbour measured with a theme COLOUR, which is the shape of the defect this note corrects. THE OUTCOME THE OLD STATEMENT DESCRIBED IS REAL AND THE MECHANISM IT NAMED IS NOT: a v3 project carried into v4 does lose its container centring, because the file is not read, not because the options were removed. The cited upgrade guide says they "no longer exist in v4"; the compiler disagrees, and where the primary source and the executed measurement disagree this Index publishes the measurement and keeps the citation. The re-score follows the outcome: nothing throws and nothing fails to build, the reader is handed an uncentred container, which is silently-wrong and not breaks-build — the same correction LF1 took on this library the same day (JOURNAL/207). No finding is charged against this fact's api (checked, not assumed: the four container-adjacent records in the corpus are all @container-size, a container-QUERY utility, and none names a fact id), so the re-score moves this fact and nothing else.

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"

hover variant

Behaviour changed in tailwindcss 4.0.0 (2025-01-21) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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"

px-* / py-* / mx-* / my-* / inset-x-* / inset-y-* / scroll-px-* / scroll-my-* / border-x-* / border-y-*

Behaviour changed in tailwindcss 4.0.0 (2025-01-21) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

Since 4.0.0 every x/y axis utility compiles to a LOGICAL property, not a pair of physical ones: px-* is padding-inline, py-* is padding-block, mx-*/my-* are margin-inline/margin-block, inset-x-*/inset-y-* are inset-inline/inset-block, scroll-px-*/scroll-py-*/scroll-mx-*/scroll-my-* are the scroll-padding-*/scroll-margin-* inline and block properties, and border-x-*/border-y-* set border-inline-width/border-block-width. In 3.x each of them emitted the physical pair (padding-left + padding-right, top + bottom, and so on). The single-side utilities did NOT move — pl-*, mt-*, left-* and the corner radii such as rounded-t-* still emit physical properties — and a single-side utility still sorts after its axis utility, so px-4 pl-0 leaves the left padding at zero on every release, 3.4 and 4.x alike.

The stale belief: That px-4 compiles to padding-left and padding-right (and py-*, mx-*, inset-x-* to their physical pairs) in Tailwind v4, as they did in v3 — so an axis utility is the same box on every element regardless of its writing mode.

/* Stale */
/* what a v3 reader expects `px-4 inset-x-0` to produce — true in 3.x, false since 4.0.0 */
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.inset-x-0 { left: 0; right: 0; }

/* Current */
/* what tailwindcss 4.x emits for `px-4 py-6 inset-x-0` */
.px-4 { padding-inline: calc(var(--spacing) * 4); }
.py-6 { padding-block: calc(var(--spacing) * 6); }
.inset-x-0 { inset-inline: calc(var(--spacing) * 0); }

/* need the physical sides in a vertical writing mode? name them: */
/* <div class="[writing-mode:vertical-rl] pl-4 pr-4"> */

WHERE THE DIFFERENCE IS VISIBLE, AND WHERE IT IS NOT. In a horizontal writing mode the rendered box is the same as in 3.x — left-to-right or right-to-left — because both padding sides are set to one value either way, and the override order a reader relies on is unchanged (pl-0 is emitted after px-4 on all seven rungs of the ladder, 3.4.0 through 4.3.3; the 4.0 alphas fixed this deliberately, CHANGELOG #14777). The difference is the element whose writing mode is vertical: by the CSS Logical Properties specification the inline axis there runs top-to-bottom, so px-4 pads the TOP and BOTTOM — a spec reading, not measured here. So this fact charges only where a draw's code or advice depends on the physical reading (a vertical writing mode, or a claim about what the emitted CSS is that a reader then acts on); a draw that merely writes px-4 in an ordinary layout ships working code and is at most a recorded miss. VERIFIED BY COMPILATION 2026-09-28 on the bisector's ladder (%TEMP%\sp-tw-bisect, seven rungs): every axis utility named in the statement emits the physical pair at 3.4.0 and 3.4.17 and the logical property at 4.0.0, 4.1.0, 4.2.0, 4.3.0 and 4.3.3; pl-0, mt-0, left-2 and rounded-t-md emit physical properties at every rung. space-x-* and divide-x-* moved to logical properties at the same release and are LF18's subject, not this fact's. FOUND BY THE BATTERIES, NOT BY A RELEASE NOTE: both Opus 5.5 twins of tailwindcss/v6 told the reader py-6 and inset-x-0 are physical, and both Fable 5.1 twins of tailwindcss/v7 got it right and dated it (JOURNAL/312, /314). Neither is charged retroactively: the fact did not exist when those runs were graded.

Reproduced against: no model yet. Verified from the primary source only — this is a correction, not an Index entry.

Source: [tailwindcss CHANGELOG — [4.0.0-alpha.31] - 2024-10-29, Changed (shipped in the first stable, 4.0.0, 2025-01-21)](https://raw.githubusercontent.com/tailwindlabs/tailwindcss/main/CHANGELOG.md) · 2024-10-29 · tailwindcss 4.0.0, shipped package — px-4 compiles to padding-inline, py-6 to padding-block, inset-x-0 to inset-inline, border-x-2 to border-inline-width; pl-0 to padding-left. · tailwindcss 3.4.17, shipped package — px-4 compiles to padding-left + padding-right, py-6 to padding-top + padding-bottom, inset-x-0 to left + right, border-x-2 to border-left-width + border-right-width.

ring

Default changed in tailwindcss 4.0.0 (2025-01-21) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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.

@variant (stacked and compound variants)

Added in tailwindcss 4.3.0 (2026-05-08) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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:focus block 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:focus and @variant hover, focus, active each THROW Cannot use @variant with unknown variant: … — a hard build failure, not a silent no-op — while @variant hover and the nested form compile. At 4.3.0 and 4.3.3 all five compile.

Reproduced against: Claude Fable 5.1 (S3), Claude Opus 5.5 (S3) — tailwindcss/v3-a, 2026-09-06; tailwindcss/v5-a, 2026-09-27.

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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 working scrollbar-thin markup that it generates nothing, and sending the reader to a third-party plugin or to ::-webkit-scrollbar — the latter is not equivalent, because ::-webkit-scrollbar is unimplemented in Firefox while scrollbar-width and scrollbar-color are the standard properties this family emits. VERIFIED BY COMPILATION 2026-09-06: every class in correct_code built 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-thin and scrollbar-thumb-rounded are 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; CSS scrollbar-color takes exactly two colours and the utilities cover exactly those two.

Reproduced against: Claude Fable 5.1 (S2), Claude Opus 5.5 (S2) — tailwindcss/v3-a, 2026-09-06; tailwindcss/v5-a, 2026-09-27.

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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-normal and zoom-reset do 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-4 and tab-8 emit 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), Claude Opus 5.5 (S2) — tailwindcss/v3-a, 2026-09-06; tailwindcss/v5-a, 2026-09-27.

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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 working pbs-6 markup 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. Substituting pt-* for pbs-* is also S2, but only in a non-horizontal writing mode - in writing-mode: horizontal-tb the 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 in correct_code built 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-4 and me-4 emit at both; pis-4, pie-4, mis-4, mie-4 and border-is-2 emit at none of the three. That last check is the point of the pis-* 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-block and inline-flex are unchanged and still set display; inline-4 and block-full are the new sizing utilities and do not collide with them.

Reproduced against: Claude Fable 5.1 (S2), Claude Opus 5 (S2) — tailwindcss/v2-a, 2026-09-03; tailwindcss/v7-a, 2026-09-28.

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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-0 and inset-s-0 compile to the SAME declaration (inset-inline-start: calc(var(--spacing) * 0)), so nobody's working markup broke. Do not restate this fact as removed. The only difference the compiler shows is cosmetic and appears later: at 4.3.3 inset-s-0 emits the constant-folded inset-inline-start: 0px while start-0 still emits the calc() form. Same computed value.

Reproduced against: Claude Fable 5.1 (S3), Claude Opus 5 (S3) — tailwindcss/v2-a, 2026-09-03; tailwindcss/v7-a, 2026-09-28.

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.

@container-size

Added in tailwindcss 4.3.0 (2026-05-08) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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-size emits nothing at 4.2.4 and container-type: size at 4.3.0 and 4.3.3; @container emits container-type: inline-size at 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.

bg-gradient-*

Deprecated in tailwindcss 4.0.0 (2025-01-21) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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, not removed: bg-gradient-to-r still compiles to a working linear gradient at every 4.x release. It is not an exact alias — from 4.1.0 the two spellings emit different declarations, and only bg-linear-* gets the @supports interpolation branch — so adopt the new name for that as well as for the angle and radial/conic families. 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, still compiled but no longer identical to the new one.


<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) and CORRECTED 2026-09-23 (JOURNAL/205). The statement previously claimed a MECHANISM — "4.x rewrites a bg-gradient-to-* candidate to bg-linear-to-* before compiling it, so v3 markup still renders a gradient" — and that clause (first committed 2026-09-03, 20 days published) is false from 4.1.0. Measured on the seven-rung ladder, declaration bodies only: the two spellings emit identical declarations at 4.0.0 and diverge at every rung from 4.1.0, where bg-linear-to-r gained @supports (background-image: linear-gradient(in lab, red, red)) around an in oklab override and bg-gradient-to-r kept the older unconditional in oklab form. Control: neither spelling appears in the sheet when it is not asked for, at any rung, so the bodies compared are the ones the run generated. The fact’s other two claims were re-checked separately and both survive — bg-linear-45 and bg-radial are absent at 3.4.0 and 3.4.17 and present at every v4 rung, and bg-gradient-to-r emits a linear-gradient at all seven. What is withdrawn is the alias-identity claim, not the rename and not the still-works clause.

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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 removed at 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.mjs built 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 with utilities.functional(...) or utilities.static(...) and paired with utilities.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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) · boundary measured on 7 releases, tailwindcss 3.4.0 through 4.3.3

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) and theme(--color-red-500) both emit color: oklch(0.637 0.237 25.331), and theme(screens.xl) and theme(--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 fixes theme('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 @variant stacked and compound syntax and the correct tab-* 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 Fable 5.1's stated boundary hold under a cue on the other libraries where Opus 5.5's did not (valibot 1.1.0, zod 4.2.0)? Its zod stop already spreads 4.1.0/4.2.0, so zod is the cheapest place to look. (open since 2026-09-28)
  • Is the under-report specific to tailwindcss, or does every Opus 5.5 ladder hold one minor it can describe when cued? The ladder has no cue by design; only a battery on the rung above a stated boundary can answer it, library by library. (open since 2026-09-28)
  • 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)

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.


The Stale Priors Index, Unattended Works (https://stalepriors.com/corrections/tailwindcss.md), CC BY 4.0. Retrieved 2026-09-06. Licence: <https://creativecommons.org/licenses/by/4.0/> (SPDX-License-Identifier: CC-BY-4.0). Quoted changelog text belongs to its authors; nothing here grants any right in the libraries described. Terms in full: <https://stalepriors.com/data/LICENSE>.

Honest note on maintenance: this pack is generated, not written by hand. tools/build-corrections.mjs renders it from the verified release facts in data/tailwindcss/facts.json joined to the findings in the runs it cites, and the build fails if a finding names an API no verified fact corrects. Hand-written packs drifted from the data twice before this was automated; the dataset remains the authority.