@lunawerx/normwind · v3.8.0 · node 22+
Your Tailwind classes are longer than Tailwind needs them to be.
A zero-config CLI and GitHub Action that finds bloated utility strings and rewrites them into their short, canonical form. No rules file, no sort order, no opinions imposed on your repo.
run with --fix to rewrite
// at a glance
Two kinds of noise, collapsed.
Verbose utility combinations, and arbitrary values that Tailwind already has a name for. Both are found by an audit and both can be rewritten for you.
Also gap-x-4 gap-y-4, top-0 bottom-0, items-start justify-items-start, self-end justify-self-end, and the rest of Tailwind's shorthand families.
// why you can trust it
NormWind doesn't invent mappings.
Every "after" is something Tailwind itself considers equivalent to the "before". There is no heuristic and no house style in the middle.
-
Shorthand groups come from
eslint-plugin-tailwindcss's utility-group definitions. -
Canonical values come from Tailwind's own
designSystem.canonicalizeCandidatesengine. - Your Tailwind wins. It resolves the Tailwind install in the scanned project first, so fixes match the version that will actually build it.
- Nothing is written into your repo. The bundled canonical snapshot makes the first run fast and deterministic, and stays inside the package.
On Tailwind 4.0, which predates the canonicalization API, NormWind still audits and fixes shorthand groups and deliberately leaves arbitrary values alone rather than applying newer semantics to an older engine.
<!-- before -->
<div class="px-4 py-4 mt-3 mb-3
rounded-[24px] w-6 h-6">
<!-- after: normwind --fix -->
<div class="p-4 my-3
rounded-3xl size-6">
// fix modes
Audit it, preview it, or let it rewrite.
Start read-only. Widen the blast radius only when you want to.
normwind
Audit the current project and report every finding with its file and line. Exits 1 when there is anything to clean up, so it works as a gate without any extra flags.
--fix
The safe default. Rewrites .vue files, then re-runs the audit so you see what is left. Per-file fault isolation means one unparseable file cannot take the run down with it.
--fixall
The whole codebase: Vue, JS, MJS, TS, JSX, and TSX. Same transforms, wider reach, for when you want the cleanup done in one pass.
--dry-run
Pair it with either fix mode to see exactly which files would be rewritten. Nothing touches disk.
--json
Machine-readable findings for CI dashboards, custom reporters, or anything else that would rather not parse a terminal.
Scope it
Pass a folder or a glob: normwind src, or normwind "apps/web/**/*.{tsx,ts}". With no arguments it scans the project and skips node_modules, dist, and the usual build scratch.
// in ci
Utility bloat stops landing on main.
There is a first-party GitHub Action that annotates the pull request inline and fails it when findings exist. On any other provider it is one command, because the exit code already says everything.
- Inline annotations land on the exact class string, not in a log you have to go read.
- Deterministic: the bundled snapshot means no cold-boot Tailwind compile on every run.
- Bounded: a single run refuses to push more than 1,000 unique cache misses through the live canonicalizer, so scanning untrusted code cannot balloon into an out-of-memory crash.
name: NormWind
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
tailwind-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: LunarWerxs/NormWind@v3
// how it compares
NormWind vs. the tools you already run.
NormWind isn't a replacement for your formatter or linter: it does a narrower job than either. Here's how it lines up against the two tools most Tailwind projects already have installed.
vs. prettier-plugin-tailwindcss
Prettier's official Tailwind plugin sorts your classes into a recommended order and trims exact duplicates and stray whitespace. It doesn't merge px-4 py-4 into p-4 or turn rounded-[24px] into rounded-3xl, that's a different kind of rewrite. The two run side by side without conflict: Prettier orders, NormWind shortens.
vs. eslint-plugin-tailwindcss
Its enforces-shorthand rule does the same kind of merge NormWind does, and in fact NormWind's own shorthand groups are sourced from this plugin's utility-group data. The difference is setup: that rule needs ESLint installed and a cssConfigPath pointed at your Tailwind CSS file. NormWind ships the same data as a zero-config, standalone CLI and GitHub Action, no ESLint required.
// faq
Questions people ask before they install it.
Short, direct answers pulled straight from the docs above.
Is NormWind free?
Yes. NormWind is free, open-source software released under the MIT License, not affiliated with Tailwind Labs. It ships as an npm package you can run directly with npx @lunawerx/normwind, with no rules file, no sort order, and no configuration required before you start.
Does NormWind require any configuration?
No. NormWind is zero-config: there is no rules file to write, no sort order to define, and no opinions imposed on your repository. Point it at a project with npx @lunawerx/normwind for an immediate audit, or narrow the scan to a folder or glob if you want.
What does NormWind actually fix?
NormWind collapses two kinds of Tailwind class-string noise: verbose utility combinations, such as px-4 py-4 becoming p-4, and arbitrary values that already have a canonical named utility, such as rounded-[24px] becoming rounded-3xl. Every rewrite comes from Tailwind's own canonicalization engine, never a stylistic guess.
Which file types does NormWind support?
By default NormWind scans Vue, Svelte, Astro, HTML/HTM, JS, MJS, CJS, TS, JSX, TSX, MTS, and CTS files. The safe --fix mode covers every markup format (Vue, Svelte, Astro, HTML), while --fixall applies the same rewrites across the full JavaScript and TypeScript file set as well.
Does NormWind work with GitHub Actions or other CI systems?
Yes. NormWind ships a first-party GitHub Action that annotates pull requests inline and fails the job when findings exist. On any other CI provider, running npx @lunawerx/normwind --json is enough, since the exit code already reports pass or fail and the JSON output is stable for a custom reporter.
Can NormWind fix my code automatically, or does it just report issues?
Both. Running normwind alone only audits and reports findings, exiting with code 1 if anything is found. Adding --fix safely rewrites markup files and re-runs the audit, while --fixall extends the same rewrites across the whole codebase. Pair either with --dry-run to preview changes without writing to disk.
Does NormWind work offline?
Yes. NormWind resolves the Tailwind install already inside your project, or falls back to a canonical snapshot bundled with the package, so audits and fixes run without any network calls. The only time NormWind touches the network is the one-off npm or npx download of the package itself.
How is NormWind different from Prettier or eslint-plugin-tailwindcss?
They do different jobs. prettier-plugin-tailwindcss only reorders classes and trims exact duplicates, not px-4 py-4 into p-4. eslint-plugin-tailwindcss's enforces-shorthand rule does that same merge, but needs ESLint installed and a Tailwind CSS config path set. NormWind bundles the same shorthand data as a zero-config CLI and GitHub Action, no ESLint setup required.
// one command
You don't have to install it to try it.
Point it at the project and read the report. Nothing is written unless you ask for a fix.