npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

twaz-ts

v0.26.4

Published

Check and automatically fix Tailwind CSS utility class order in JSX/TSX files

Readme

twaz

Check and automatically fix Tailwind CSS utility class order in JSX/TSX files.

twaz enforces a consistent, readable order for className, class, and cn() arguments. It scans source files, reports violations, and can reorder classes in place with --fix.

Table of contents

Installation

npm install -D twaz

Or run without installing:

npx twaz src

Usage

CLI

# Check class order (default: current directory)
twaz

# Check a specific directory or file
twaz src
twaz src/components/Button.tsx

# Automatically fix class order in place
twaz --fix src

Programmatic API

import { checkClassString, sortClassString, runScan } from "twaz";

checkClassString("bg-muted text-sm absolute");
// → [{ token: "bg-muted", group: "background & fill color", after: "text size" }, ...]

sortClassString("bg-muted text-sm absolute top-0");
// → "absolute top-0 text-sm bg-muted"

const { violations } = runScan(["src"], { fix: false });

Development

npm install
npm run dev          # run CLI with tsx (debug)
npm run fix          # run CLI with --fix via tsx
npm run typecheck    # TypeScript check
npm run build        # production build with tsdown

What gets scanned

By default, twaz scans .tsx and .jsx files. It looks for class strings in:

  • className="..." / className='...'
  • className={...} / className={"..."}
  • class="..."
  • cn("...") / classNames("...")

Directories node_modules, dist, and .git are skipped.


Tailwind class order rules

When writing or editing className, class, or cn() arguments, order utility classes in this sequence. Separate groups with a single space. Keep variant prefixes attached to each utility (e.g. hover:bg-primary, not hover: bg-primary).

Precedence overview

Lower numbers sort earlier (left). Higher numbers sort later (right).

| # | Group | Examples | |---|-------|----------| | 1 | Position anchor | relative, absolute, fixed, sticky, static | | 2 | Position offsets | inset-*, top-*, right-*, bottom-*, left-* | | 3 | Self & group | self-*, group, group/name | | 4 | Element | shrink-*, grow-*, select-*, whitespace-*, compress-zero | | 5 | Margin & padding | m-*, p-*, and axis variants | | 6 | Width & height | w-*, h-*, min-*, max-*, size-*, aspect-* | | 7 | Display | block, inline, hidden, visible | | 8 | Text size | text-xs, text-sm, text-base, text-lg, etc. | | 9 | Font | font-* (e.g. font-medium, font-mono) | | 10 | Text color | text-red-500, text-muted-foreground, text-center | | 11 | Background & fill color | bg-*, fill-*, stroke-*, gradients, opacity-* | | 12 | Variant modifiers | hover:, focus:, disabled:, aria-*:, data-*:, dark:, md: | | 13 | Transition | transition-*, duration-*, animate-* | | 14 | Border | border, border-*, outline-*, ring-*, divide-* | | 15 | Rounding | rounded-* | | 16 | Shadow | shadow-* | | 17 | Truncate & overflow | truncate, overflow-*, text-ellipsis | | 18 | Children (grid & flex) | grid-*, flex, gap-*, items-*, justify-*, etc. | | 19 | End | cursor-*, pointer-events-*, z-* (always last) |

Within the same group, the original relative order is preserved (stable sort).


1. Position anchor

Positioning mode comes first — before any offset values.

relative | absolute | fixed | sticky | static

Variant-prefixed anchors (e.g. md:absolute) are treated as variant modifiers (group 12) and sort after base color utilities.

2. Position offsets

Offset utilities immediately follow the position anchor.

inset-* | top-* | right-* | bottom-* | left-*

3. Self & group

Self-alignment and group markers for child state styling.

self-* | group | group/accordion-trigger

Named groups like group/accordion-trigger are recognized as group utilities, not variant prefixes.

4. Element

Intrinsic element behavior — flex item sizing, text selection, whitespace.

shrink-* | grow-* | basis-* | select-* | whitespace-* | compress-zero

5. Margin & padding

Spacing around the element. Unprefixed only — responsive or state-prefixed spacing (e.g. md:px-4, hover:p-2) is treated as a variant modifier (group 12).

m-* | mx-* | my-* | mt-* | mr-* | mb-* | ml-*
p-* | px-* | py-* | pt-* | pr-* | pb-* | pl-*

6. Width & height

Box dimensions.

w-* | h-* | min-w-* | max-w-* | min-h-* | max-h-* | size-* | aspect-*

7. Display

Display mode utilities.

block | inline | hidden | visible | isolate

Variant-prefixed display utilities (e.g. md:hidden) sort as variant modifiers.

8. Text size

Font size tokens only — not color or alignment.

text-xs | text-sm | text-base | text-lg | text-xl | text-2xl … text-9xl

9. Font

Font family, weight, and related typography — after text size, before color.

font-medium | font-mono | font-condensed | font-*

10. Text color

Text color and text-related non-size utilities. Unprefixed only.

text-red-500 | text-muted-foreground | text-background | text-center | text-left

Anything matching text-* that is not a text size token (group 8) belongs here.

11. Background & fill color

Surface and decorative color. Unprefixed only.

bg-* | fill-* | stroke-* | from-* | to-* | via-* | opacity-*
accent-* | caret-* | decoration-*

12. Variant modifiers

Any utility with a variant prefix that was not already placed in an earlier group. This includes responsive, state, dark mode, ARIA, and data attribute variants.

hover:* | focus:* | disabled:* | aria-*:* | data-*:* | dark:* | md:* | lg:*
group-hover:* | group-data-*:* | hidden:*

Rules:

  • Prefixed margin/padding, display, position, border, rounding, shadow, and children utilities land here.
  • Variant-prefixed transition utilities also land here (base transition-* without a prefix is group 13).
  • The : prefix stays attached to the utility name.

13. Transition

Motion and animation for the element itself (unprefixed).

transition-* | duration-* | animate-*

Placed after color-defining classes so base colors are established before motion.

14. Border

Borders, outlines, rings, and dividers. rounded-* is excluded (see group 15).

border | border-* | outline-* | ring-* | divide-*

15. Rounding

Corner radius.

rounded-* | rounded-sm | rounded-md | rounded-full

16. Shadow

Box shadows.

shadow-* | shadow-sm | shadow-md | shadow-lg

17. Truncate & overflow

Text truncation and overflow — before children layout utilities.

truncate | overflow-* | text-ellipsis

18. Children (grid & flex)

Layout that affects children. Within this group, grid-* sorts before flex utilities when both are present.

grid | grid-* | inline-grid
flex | inline-flex | flex-*
gap-* | items-* | justify-* | content-* | place-*
order-* | col-* | row-* | space-x-* | space-y-* | list-*

Variant-prefixed children utilities (e.g. md:flex) sort as variant modifiers.

19. End

Interaction and stacking — always last.

cursor-* | pointer-events-* | z-*

Examples

Position before offsets; text color before background

// ❌ BAD
<div className="top-0 left-0 absolute bg-muted text-sm text-muted-foreground" />

// ✅ GOOD
<div className="absolute top-0 left-0 text-sm text-muted-foreground bg-muted" />

Font after text size; hover after base color

// ❌ BAD
<button className="font-medium text-sm hover:bg-primary flex px-4 h-9 bg-background border rounded-md" />

// ✅ GOOD
<button className="px-4 h-9 text-sm font-medium text-foreground bg-background hover:bg-primary border rounded-md flex" />

Group near position; z-index last

// ❌ BAD
<div className="px-3 py-2 group/accordion-trigger relative z-50 flex cursor-pointer" />

// ✅ GOOD
<div className="relative group/accordion-trigger px-3 py-2 text-sm font-medium text-muted-foreground bg-muted flex cursor-pointer z-50" />

Overflow before flex children

// ❌ BAD
<div className="flex gap-2 overflow-auto truncate w-full" />

// ✅ GOOD
<div className="w-full overflow-auto truncate flex gap-2" />

How violations are detected

twaz walks each class string left to right. Each token is classified into a group number. If a token's group number is lower than a token that appeared earlier, it is reported as a violation — it should have appeared earlier in the string.

Unrecognized tokens are ignored for ordering (they do not trigger violations and stay in place during --fix).


Execution flow

flowchart TD
    Start([CLI or programmatic API]) --> Entry{Entry point}

    Entry -->|CLI| ParseArgs[parseArgs]
    ParseArgs -->|help| Help[Print help → exit 0]
    ParseArgs -->|paths + options| RunScan[runScan]

    Entry -->|API| RunScan
    Entry -->|checkClassString / sortClassString| Classify[classify token]

    RunScan --> CollectFiles[collectFiles]
    CollectFiles --> Walk{Directory?}
    Walk -->|yes| Recurse[walk — skip node_modules, dist, .git]
    Walk -->|no| SingleFile[Add matching file]
    Recurse --> Files[.tsx / .jsx file list]
    SingleFile --> Files

    Files --> FixMode{fix option?}

    FixMode -->|yes| FixLoop[For each file]
    FixLoop --> ReadFix[readFileSync]
    ReadFix --> ExtractFix[extractClassStrings]
    ExtractFix --> ApplyFixes[applyFixes]
    ApplyFixes --> CheckEach{checkClassString}
    CheckEach -->|violations| SortFix[sortClassString → classify]
    SortFix --> WriteFile[writeFileSync in place]
    CheckEach -->|ok| SkipFix[Skip]
    WriteFile --> Recheck[Re-scan with scanForViolations]
    SkipFix --> Recheck
    Recheck --> ReportFix[printViolations / return result]

    FixMode -->|no| ScanLoop[For each file]
    ScanLoop --> ReadScan[readFileSync]
    ReadScan --> ExtractScan[extractClassStrings]
    ExtractScan --> CheckScan[checkClassString]
    CheckScan --> ClassifyScan[classify each token]
    ClassifyScan --> CompareOrder[Compare group numbers left → right]
    CompareOrder --> Violations[Collect FileViolation records]
    Violations --> ReportScan[printViolations / return result]

    ReportFix --> Exit{Violations remain?}
    ReportScan --> Exit
    Exit -->|yes| Exit1[exit 1]
    Exit -->|no| Exit0[exit 0]

In conclusion

twaz is an acronym standing for "Tailwind from A to Z" (referring not to alphabetical order, but to the order of values ​​in the CSS layout model).

Happy coding!