Skip to content

docs: migrating from eslint-plugin-tailwindcss - #233

Merged
sergioazoc merged 3 commits into
mainfrom
docs/migration-eslint-plugin-tailwindcss
Oct 10, 2026
Merged

sergioazoc merged 3 commits into
mainfrom
docs/migration-eslint-plugin-tailwindcss

Conversation

@sergioazoc

Copy link
Copy Markdown
Owner

Summary

A guide for the most-used Tailwind lint plugin, eslint-plugin-tailwindcss. Its latest is 4.4.0, with Tailwind v4 and 9 rules. The guide is built like /migration/from-better-tailwindcss: data-driven, in EN/ES, and checked against the plugin itself.

  • /migration/from-eslint-plugin-tailwindcss (EN/ES) has:

    • its 9 rules and 7 settings mapped, with what differs, verified by running both plugins;
    • the switch from an ESLint flat config to .oxlintrc.json, step by step, with a before/after;
    • what else changes (Vue/Svelte templates, classes in variables, Tailwind v3);
    • the rules it doesn't have.

    It's in the sidebar of both locales and has an FAQ entry.

  • Data and generation: data/eslint-plugin-tailwindcss.json renders through the same migration* functions in scripts/blocks.ts. The btw guide's output is unchanged: generate leaves no diff.

  • Checks:

    • tests/docs/eslint-plugin-tailwindcss-map.test.ts: every rule, option and setting of ours exists, and the prose names the data's version and rule count.
    • bench/interop.mjs, weekly in interop.yml: every row's example is reported by both rules, and every rule of theirs has a row, against the pinned 4.4.0 and @latest.
  • How their plugin runs: as an oxlint JS plugin, no ESLint needed. It registers as tailwindcss too, so oxlint won't load it beside ours ("Plugin name 'tailwindcss' is already registered"). The interop lints it on its own, with an absolute cssConfigPath, since a relative one resolves against the nearest eslint.config.* / package.json. The guide's step 4 tells users to remove it in the same change.

@shadcn/lint gets no migration guide: it's a complement, not a replacement (see /shadcn).

Test plan

  • pnpm test (3821 + 318), format, lint, lint:types, typecheck. generate leaves no diff, and pnpm -C packages/docs build: check-dist ok.
  • Bench npm test (67). node interop.mjs, pinned and @latest: "9 rules map". Changing an example to inset-px makes it fail on both sides.

🤖 Generated with Claude Code

https://claude.ai/code/session_019vTh6S5A41M1HLkmk3FaM1

eslint-plugin-tailwindcss (francoismassart) reads Tailwind v4 since its 4.x
releases, and had no guide here. /migration/from-eslint-plugin-tailwindcss
(EN/ES) maps each of 4.4.0's 9 rules and 7 settings, with what differs, and
walks from an ESLint flat config to an .oxlintrc.json.

Everything it says comes from the 4.4.0 package or from running both plugins:
- Their canonical rule folds in what has its own rule here (v3 renames,
  bg-[var(--x)], !p-4, -m-[5px]), and rewrites what this one leaves because
  the CSS differs ([&>*]: → *:, aspect-[16/9] → aspect-video).
- Their shorthand also makes truncate; four corners fold at once here.
- Their no-unnecessary-arbitrary-value turns m-[8px] / w-[20rem] into tokens:
  prefer-scale-token here, as a suggestion.
- Settings: theirs replace the defaults, ours add to them; functions →
  callees + tags (classNames(…) and tw(…) as a call aren't read here); keys of
  an object are classes in any callee here, only in parseKeyFunctions there;
  a relative cssConfigPath resolves against the nearest eslint.config.* or
  package.json, entryPoint against the nearest oxlint config.
- Both plugins are named `tailwindcss`: oxlint refuses to load the two side by
  side, so the guide says to remove theirs in the same step.

Data-driven like the better-tailwindcss guide: data/eslint-plugin-tailwindcss.json
renders the rules, settings and "rules they don't have" blocks through
migration* functions in scripts/blocks.ts, which the btw* blocks now share.
Sidebar and FAQ entries in both locales. Tests: blocks.test.ts for the three
blocks, and eslint-plugin-tailwindcss-map.test.ts for this plugin's side
(rules, settings and options exist, each mapped once, and the prose names the
data's version and rule count in both locales).

Claude-Session: https://claude.ai/code/session_019vTh6S5A41M1HLkmk3FaM1
…t the plugin

bench/interop.mjs now does for eslint-plugin-tailwindcss what it does for
better-tailwindcss: every row's example must be reported by their rule and by
ours, and every rule of theirs must have a row. Pinned at 4.4.0 in
bench/package.json; interop.yml also installs @latest weekly.

It runs as an oxlint JS plugin, but registers as `tailwindcss` like this one,
so oxlint refuses to load both ("Plugin name 'tailwindcss' is already
registered"): it is linted on its own. A relative cssConfigPath resolves
against the nearest eslint.config.* or package.json above the linted file —
bench/'s for the interop project — so it gets an absolute one.

mappingGaps takes the guide's file prefix and plugin name (defaults: btw,
better-tailwindcss), with a test. A mutated example (inset-px) fails both
sides, exit 1.

Claude-Session: https://claude.ai/code/session_019vTh6S5A41M1HLkmk3FaM1
@sergioazoc
sergioazoc merged commit 3572512 into main Oct 10, 2026
14 checks passed
@sergioazoc
sergioazoc deleted the docs/migration-eslint-plugin-tailwindcss branch October 10, 2026 01:06
@sergioazoc sergioazoc mentioned this pull request Oct 10, 2026
3 tasks done
sergioazoc added a commit that referenced this pull request Oct 10, 2026
- packages/oxlint-tailwindcss/package.json and the Claude Code plugin's
  plugin.json at 1.16.0.
- CHANGELOG: Unreleased → 1.16.0 — Features (#216, #232: wrapStrings "all")
  and Documentation (#233: the eslint-plugin-tailwindcss migration guide).
  Nothing changes with the same config.
- bench/results/latest.json regenerated after the bump (only the version and
  timings move), /benchmark and /comparison regenerated from it.

Claude-Session: https://claude.ai/code/session_019vTh6S5A41M1HLkmk3FaM1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant