@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 rewrites bloated utility strings into their canonical short form.
run with --fix to rewrite
// at a glance
Two kinds of noise, collapsed.
Verbose utility combinations, and arbitrary values Tailwind already names. Both are audited and both can be rewritten.
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, it still fixes shorthand groups and leaves arbitrary values alone.
<!-- 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
Audits the project and reports every finding with file and line. Exits 1, so it gates without extra flags.
--fix
The safe default. Rewrites .vue files then re-audits, with per-file fault isolation.
--fixall
The whole codebase: Vue, JS, MJS, TS, JSX, TSX. Same transforms, wider reach, 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 glob. With no arguments it scans the project and skips node_modules and build scratch.
// in ci
Utility bloat stops landing on main.
A first-party GitHub Action annotates the PR and fails it on findings. Elsewhere, one command.
- 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.
vs. prettier-plugin-tailwindcss
Sorts classes and trims duplicates, but does not merge px-4 py-4 into p-4. Prettier orders, NormWind shortens.
vs. eslint-plugin-tailwindcss
Its enforces-shorthand rule does the same merge, but needs ESLint and a cssConfigPath. NormWind is zero-config.
// 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.