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

biome-plugin-tailwindcss

v1.0.0

Published

Biome GritQL plugin with Tailwind CSS linting rules

Downloads

121

Readme

🧩 biome-plugin-tailwindcss

npm version npm downloads License: MIT PR Checks Biome ≥ 2.0

Tailwind CSS v4 linting rules for Biome — enforce design scale, detect deprecated classes, and suggest shorthands

FeaturesInstallationUsageRulesContributing


👋 Hello there!

biome-plugin-tailwindcss is a Biome GritQL plugin that adds Tailwind CSS v4 linting rules for JSX class attributes. It helps teams keep their Tailwind usage consistent — enforcing design scale values, catching deprecated v3 classes, and suggesting available shorthands.

Four plugin rules ship today. Class ordering is already built into Biome core as useSortedClasses. Rules that require multi-class analysis or Tailwind config access are documented below as candidates for upstream Biome contributions.

✨ Features

🎯 Plugin Rules (GritQL)

  • no-arbitrary-value — flags arbitrary CSS values in class attributes (w-[42px], text-[#bada55]) and encourages the use of configured design-scale values
  • enforces-negative-arbitrary-values — catches the wrong negative-arbitrary form -top-[5px] and enforces the correct top-[-5px] syntax
  • migration-from-tailwind-3 — detects 22 class patterns renamed or removed in Tailwind CSS v4: all six opacity utilities (bg-opacity-*bg-black/50), flex shorthands (flex-growgrow), shadow/blur/rounded scale renames, outline-noneoutline-hidden, and more
  • 💡 enforces-size-shorthand — detects w-X h-X pairs with equal values and suggests the size-X shorthand introduced in Tailwind v4; covers all 48 default size values with zero false positives

🏗️ Built-in Biome Coverage

📋 Rule Coverage

| Rule | Status | Source | |---|---|---| | classnames-order | ✅ Biome core (useSortedClasses) | docs | | no-arbitrary-value | ✅ This plugin | source | | enforces-negative-arbitrary-values | ✅ This plugin | source | | migration-from-tailwind-3 | ✅ This plugin | source | | enforces-size-shorthand | ✅ This plugin | source | | no-contradicting-classname | 🔧 Needs Biome core PR | — | | enforces-shorthand | 🔧 Needs Biome core PR | — | | no-custom-classname | 🔧 Needs Biome core PR | — | | no-unnecessary-arbitrary-value | 🔧 Needs Biome core PR | — |


📖 Table of Contents


📦 Installation

npm install --save-dev biome-plugin-tailwindcss
# or
pnpm add --save-dev biome-plugin-tailwindcss
# or
yarn add --save-dev biome-plugin-tailwindcss

Peer Dependencies

This plugin requires Biome ≥ 2.0.0. GritQL plugin support was introduced in Biome 2. Install it if you haven't already:

npm install --save-dev @biomejs/biome

🚀 Usage

Biome does not currently resolve plugins from node_modules automatically. You must reference .grit files using their explicit node_modules paths.

ℹ️ Note for all examples: the useSortedClasses nursery rule sorts your Tailwind classes just like prettier-plugin-tailwindcss. The unsafe fix won't apply on save — run biome lint --fix --unsafe to apply it, or enable the rule as "error" with a pre-commit hook.

Adding to an existing biome.json

If you already have a biome.json, add a plugins array and extend your existing linter section. Only the highlighted keys are new:

{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "plugins": [
    "./node_modules/biome-plugin-tailwindcss/rules/no-arbitrary-value.grit",
    "./node_modules/biome-plugin-tailwindcss/rules/enforces-negative-arbitrary-values.grit",
    "./node_modules/biome-plugin-tailwindcss/rules/migration-from-tailwind-3.grit",
    "./node_modules/biome-plugin-tailwindcss/rules/enforces-size-shorthand.grit"
  ],
  "linter": {
    "enabled": true,
    "rules": {
      "nursery": {
        "useSortedClasses": {
          "level": "warn",
          "fix": "unsafe"
        }
      }
    }
  }
}

The plugins key is merged with any existing rules — it does not replace them. If you already have a rules.nursery block, add useSortedClasses inside it.

Option A — Recommended preset (copy-paste ready)

Copy the contents of presets/recommended.json into your biome.json. It enables all four plugin rules and the built-in useSortedClasses at warn severity.

Option B — Strict preset

Copy presets/strict.json to use the single all.grit bundle (all rules in one file) with useSortedClasses set to error.

{
  "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
  "plugins": [
    "./node_modules/biome-plugin-tailwindcss/rules/all.grit"
  ],
  "linter": {
    "enabled": true,
    "rules": {
      "nursery": {
        "useSortedClasses": {
          "level": "error",
          "fix": "unsafe"
        }
      }
    }
  }
}

Option C — Individual rules

Pick only the rules you need:

{
  "plugins": [
    "./node_modules/biome-plugin-tailwindcss/rules/no-arbitrary-value.grit"
  ]
}

📚 Rules

no-arbitrary-value

Flags Tailwind CSS arbitrary values in class and className attributes. Arbitrary values bypass your design scale and make the codebase harder to maintain consistently.

// ✗ Invalid — arbitrary values
<div className="w-[42px] h-[calc(100%-2rem)] text-[#bada55]" />

// ✓ Valid — scale values
<div className="w-10 h-full text-red-500" />

Applies to both JSX string attributes and expression containers (including clsx, cx, cva, cn, twMerge).

Severity: warn


enforces-negative-arbitrary-values

Tailwind CSS expects negative arbitrary values in the form property-[-value], not -property-[value]. The latter can cause confusion with double-negative variables and is not supported in all versions.

// ✗ Invalid — negative modifier before property name
<div className="-top-[5px] -left-[10px]" />

// ✓ Valid — negative value inside brackets
<div className="top-[-5px] left-[-10px]" />

Note: multi-component utility names with arbitrary values (e.g. border-t-[4px]) are not flagged — only class tokens that start with a leading - are.

Severity: error


migration-from-tailwind-3

Detects Tailwind CSS v3 class names that were renamed or removed in Tailwind CSS v4. Run this rule during your v3 → v4 migration to locate all affected usages.

Opacity utilities (removed — use opacity modifier syntax)

| v3 class | v4 replacement | |---|---| | bg-opacity-{n} | bg-{color}/{n} e.g. bg-black/50 | | text-opacity-{n} | text-{color}/{n} e.g. text-white/75 | | border-opacity-{n} | border-{color}/{n} | | divide-opacity-{n} | divide-{color}/{n} | | ring-opacity-{n} | ring-{color}/{n} | | placeholder-opacity-{n} | placeholder-{color}/{n} |

Renamed utilities

| v3 class | v4 class | |---|---| | flex-grow | grow | | flex-grow-0 | grow-0 | | flex-shrink | shrink | | flex-shrink-0 | shrink-0 | | overflow-ellipsis | text-ellipsis | | decoration-slice | box-decoration-slice | | decoration-clone | box-decoration-clone | | outline-none | outline-hidden | | shadow | shadow-sm | | shadow-sm | shadow-xs | | drop-shadow | drop-shadow-sm | | drop-shadow-sm | drop-shadow-xs | | blur | blur-sm | | blur-sm | blur-xs | | backdrop-blur | backdrop-blur-sm | | backdrop-blur-sm | backdrop-blur-xs | | rounded | rounded-sm | | rounded-sm | rounded-xs |

// ✗ Invalid — v3 class names
<div className="flex-grow bg-opacity-50 shadow outline-none" />

// ✓ Valid — v4 equivalents
<div className="grow bg-black/50 shadow-sm outline-hidden" />

Severity: warn

Tip: After running this rule, use manual review for the shadow/blur/rounded scale renames — the scale shifted, so the correct v4 replacement depends on your intended visual weight.


💡 enforces-size-shorthand

Tailwind CSS v4 introduced the size-{n} utility, which sets both width and height to the same value in a single class. This rule detects when w-X and h-X appear together with an identical value and suggests using size-X instead.

// ✗ Flagged — redundant pair when values are equal
<div className="w-4 h-4 p-2" />
<div className="flex items-center w-full h-full" />

// ✓ Valid — use the size-* shorthand
<div className="size-4 p-2" />
<div className="flex items-center size-full" />

Note: the rule only flags pairs where both values are the same (zero false positives). w-4 h-8 is intentionally ignored because size-* would not produce the same result.

Covers all 48 default Tailwind v4 size values: numeric scale (096) and named values (full, screen, auto, fit, min, max, px, svh, lvh, dvh, and more).

Severity: hint (advisory)


❓ Why are some rules missing?

The four rules listed as 🔧 require capabilities that GritQL does not currently support:

  • Multi-class analysisno-contradicting-classname and enforces-shorthand need to compare multiple class tokens in the same attribute against a property map. GritQL matches one AST node at a time.
  • Tailwind config accessno-custom-classname and no-unnecessary-arbitrary-value need to resolve the full class list from your Tailwind theme, which requires executing or statically parsing tailwind.config.js (or @theme in v4 CSS files).

These rules must be implemented directly in the Biome repository in Rust, where they are tracked as:

| Rule | Biome issue | |---|---| | no-contradicting-classname | noContradictingClasses | | enforces-shorthand | useShorthandClasses | | no-custom-classname | noUnknownTwClass | | no-unnecessary-arbitrary-value | noUnnecessaryArbitraryValue |

Tailwind v4's CSS-first configuration (@theme in .css files) makes this significantly more tractable — Biome can parse CSS natively, which opens the door to reading custom configuration without executing JavaScript.


📃 Scripts

| Script | Command | Description | |---|---|---| | test | node tests/run.mjs | Run all rule tests | | biome:check | biome check . | Check formatting + lint | | biome:ci | biome ci --reporter=github | CI mode with GitHub annotations | | biome:fix | biome check --write . | Auto-fix formatting and safe lint fixes | | biome:format | biome format . | Check formatting only | | biome:format:fix | biome format --write . | Auto-fix formatting | | biome:lint | biome lint . | Lint only (no format) | | biome:lint:fix | biome lint --write . | Auto-fix safe lint issues |


🧪 Testing

The test runner writes a temporary biome.json per test, invokes biome lint, and counts plugin diagnostic lines in the output.

# Install dependencies
npm install

# Run all tests
npm test

Test matrix (10 tests):

| Suite | Invalid fixture | Valid fixture | |---|---|---| | no-arbitrary-value | triggers ≥ 1 diagnostic | produces 0 diagnostics | | enforces-negative-arbitrary-values | triggers ≥ 1 diagnostic | produces 0 diagnostics | | migration-from-tailwind-3 | triggers ≥ 1 diagnostic | produces 0 diagnostics | | enforces-size-shorthand | triggers ≥ 1 diagnostic | produces 0 diagnostics | | all.grit (combined) | each invalid triggers ≥ 1 | arbValid + migValid + szValid clean |


📁 Project Structure

biome-plugin-tailwindcss/
├── rules/                              # GritQL rule files
│   ├── no-arbitrary-value.grit
│   ├── enforces-negative-arbitrary-values.grit
│   ├── migration-from-tailwind-3.grit
│   ├── enforces-size-shorthand.grit
│   └── all.grit                        # All rules in a single or {} block
├── presets/                            # Ready-to-use biome.json snippets
│   ├── recommended.json                # All 4 rules at warn/hint severity
│   └── strict.json                     # All rules via all.grit at error severity
├── tests/
│   ├── run.mjs                         # Custom Node.js test runner
│   ├── valid/                          # Fixtures that must produce 0 diagnostics
│   │   ├── no-arbitrary-value.jsx
│   │   ├── enforces-negative-arbitrary-values.jsx
│   │   ├── migration-from-tailwind-3.jsx
│   │   └── enforces-size-shorthand.jsx
│   └── invalid/                        # Fixtures that must produce ≥ 1 diagnostic
│       ├── no-arbitrary-value.jsx
│       ├── enforces-negative-arbitrary-values.jsx
│       ├── migration-from-tailwind-3.jsx
│       └── enforces-size-shorthand.jsx
├── .github/
│   ├── workflows/
│   │   ├── pr-check.yml                # Biome CI + test runner on every PR
│   │   └── publish.yml                 # semantic-release to npm on main push
│   └── dependabot.yml
├── CLAUDE.md                           # Claude Code guidance
├── CONTRIBUTING.md                     # Guide for adding new rules
├── biome.json                          # Dev linting config
└── package.json

Key Directories

  • rules/ — One .grit file per rule plus all.grit which combines every rule in a single or {} block for the strict preset
  • presets/ — Copy-paste biome.json snippets for common setups; these reference node_modules paths so they work with npm install
  • tests/ — Each rule has two fixtures: valid/<rule>.jsx (must lint clean) and invalid/<rule>.jsx (must produce ≥ 1 plugin diagnostic)

Important Configuration Files

  • presets/recommended.json — Production-ready config with all four plugin rules and useSortedClasses enabled
  • presets/strict.json — Tighter config using all.grit bundle with useSortedClasses at error
  • CONTRIBUTING.md — GritQL regex tips, the standard wrapper pattern, and the list of rules that belong in Biome core

🤝 Contributing

Contributions are welcome!

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/new-rule)
  3. Follow the pattern in CONTRIBUTING.md — each rule needs a .grit file, two test fixtures, an entry in all.grit, and an update to tests/run.mjs
  4. Run tests: npm test
  5. Open a Pull Request

📜 License

This package is licensed under the MIT License. See the LICENSE file for details.


📧 Contact & Support


Made with ❤️ by the community

If this plugin helped you, please consider giving it a ⭐ on GitHub!

⬆ Back to Top