@lunawerx/normwind · v3.7.0 · node 20+

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.

Free & MIT-licensed Zero config Tailwind v4 Vue, JS, TS, JSX, TSX

// 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.

px-4 py-4p-4
rounded-[24px]rounded-3xl
pl-2 pr-2px-2
w-[100%]w-full
mt-3 mb-3my-3
h-[1.5rem]h-6
w-6 h-6size-6
p-[1rem]p-4
left-0 right-0inset-x-0
m-[8px]m-2
content-center justify-centerplace-content-center
overflow-x-hidden overflow-y-hiddenoverflow-hidden

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.canonicalizeCandidates engine.
  • 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.

Card.vue — before / after
<!-- 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.

0Nothing to clean up.
1Findings reported. This is the one CI should fail on.
2Usage or runtime error, kept distinct so a broken invocation never reads as a clean repo.

// 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.
.github/workflows/normwind.yml
name: NormWind

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  tailwind-audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: LunarWerxs/NormWind@v3

// 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.