# Rules

The 27 rules in `oxlint-tailwindcss`, grouped by what they enforce. Click into any rule for examples
and option reference.

## Correctness

These rules catch problems that would generate invalid or unexpected CSS.

- [no-unknown-classes](./no-unknown-classes) — disallow class names the design system doesn't know
  about.
- [no-conflicting-classes](./no-conflicting-classes) — flag classes that fight over the same CSS
  property.
- [no-contradicting-variants](./no-contradicting-variants) — `flex hover:flex` is redundant (only on
  literal class lists on native elements, not `cn`/`twMerge` fragments).
- [no-dark-without-light](./no-dark-without-light) — a `dark:` colour should have a light-mode pair
  (only on literal class lists on native elements, not `cn`/`twMerge` fragments).
- [no-duplicate-classes](./no-duplicate-classes) — same class twice is dead weight.
- [no-dynamic-classes](./no-dynamic-classes) — `bg-${color}-500` is built at runtime, so Tailwind
  generates no CSS for it.

## Modernization

- [no-deprecated-classes](./no-deprecated-classes) — `flex-grow` → `grow`, etc.
- [enforce-canonical](./enforce-canonical) — `-m-0` → `m-0`, `start-2` → `inset-s-2`, etc.
- [no-unnecessary-arbitrary-value](./no-unnecessary-arbitrary-value) — `w-[100%]` → `w-full` when
  the named class emits identical CSS.
- [prefer-theme-tokens](./prefer-theme-tokens) — `border-(--border)` → `border-border` when a named
  utility maps to the same theme variable.
- [prefer-scale-token](./prefer-scale-token) — `p-[10px]` → `p-2.5`, `w-[200px]` → `w-50` when the
  token is the same VALUE (report-only; the CSS differs textually).
- [enforce-negative-arbitrary-values](./enforce-negative-arbitrary-values) — `-top-[5px]` →
  `top-[-5px]`.

## Consistency

- [enforce-sort-order](./enforce-sort-order) — sort classes in the official Tailwind order.
- [consistent-variant-order](./consistent-variant-order) — `dark:hover:` vs `hover:dark:`.
- [enforce-consistent-important-position](./enforce-consistent-important-position) — prefix `!flex`
  vs suffix `flex!`.
- [enforce-consistent-line-wrapping](./enforce-consistent-line-wrapping) — one class per line vs all
  on one.
- [enforce-consistent-variable-syntax](./enforce-consistent-variable-syntax) — `bg-(--x)` vs
  `bg-[var(--x)]`.
- [enforce-logical](./enforce-logical) — `ml-4` → `ms-4` for RTL support.
- [enforce-physical](./enforce-physical) — `ms-4` → `ml-4` for LTR-only projects.
- [enforce-shorthand](./enforce-shorthand) — `mt-2 mr-2 mb-2 ml-2` → `m-2`.
- [no-unnecessary-whitespace](./no-unnecessary-whitespace) — collapse runs of spaces.

## Design-system guardrails

- [no-arbitrary-value](./no-arbitrary-value) — disallow `w-[3.14rem]` and friends.
- [no-borrowed-component-styles](./no-borrowed-component-styles) — a plain element that rebuilds one
  of your components from its classes (experimental).
- [no-default-palette](./no-default-palette) — disallow `bg-red-500` when your theme has colors of
  its own.
- [no-hardcoded-colors](./no-hardcoded-colors) — disallow `bg-[#fff]`, prefer theme tokens.
- [no-restricted-classes](./no-restricted-classes) — ban specific classes or regex patterns.
- [max-class-count](./max-class-count) — cap how many classes a single class string can stack.

## Per-rule pages

Each rule's page covers:

- **What it does**, in one paragraph.
- **Options** with their default values and a description per option.
- **Correct / incorrect examples**.
- **When to disable it** — the use cases where this rule is the wrong fit.
- For DS-dependent rules: whether `entryPoint` is required (always yes in v1) and what happens when
  it's missing.

## Defaults reference

A quick lookup table for what each rule does when you turn it on without overriding
`meta.defaultOptions`.

### DS-dependent rules

These rules require `settings.tailwindcss.entryPoint` to be set; they emit a fatal
`designSystemUnavailable` diagnostic when it isn't.

| Rule                             | Default options                         |
| -------------------------------- | --------------------------------------- |
| `enforce-canonical`              | `{ reportNonEquivalent: false }`        |
| `enforce-sort-order`             | `{ mode: 'default' }`                   |
| `no-borrowed-component-styles`   | `{ components: [] }`                    |
| `no-conflicting-classes`         | `{ reportRedundant: true }`             |
| `no-default-palette`             | `{ allow: [] }`                         |
| `no-unknown-classes`             | `{ allowlist: [], ignorePrefixes: [] }` |
| `no-unnecessary-arbitrary-value` | `{}`                                    |
| `prefer-scale-token`             | `{}`                                    |
| `prefer-theme-tokens`            | `{}`                                    |

### DS-optional rules

These work with nothing configured — their fallback is deterministic on its own — and get more
accurate when an `entryPoint` is available. None of them can emit `designSystemUnavailable`. Each
also accepts a rule-level `entryPoint` that overrides the shared setting.

| Rule                               | Default options                                              | What the design system adds                                                                                                                                               |
| ---------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `consistent-variant-order`         | `{}` (same order with or without a DS)                       | What the project's own variants are: pseudo-element, barrier or breakpoint                                                                                                |
| `enforce-consistent-line-wrapping` | `{ printWidth: 80, group: 'newLine', wrapStrings: 'never' }` | The project prefix, so `wrapLines: 'all'` grouping treats it as transparent                                                                                               |
| `enforce-logical`                  | `{ allowlist: [], direction: 'both', sizing: false }`        | Confirms the suggested class exists and that your Tailwind has the 4.2 utilities (block axis, sizes, `inset-s`); without a DS the block axis and sizes are only suggested |
| `enforce-physical`                 | `{ allowlist: [], direction: 'both', sizing: false }`        | Confirms the suggested class exists                                                                                                                                       |
| `enforce-shorthand`                | `{}`                                                         | Verifies each merge against the emitted CSS                                                                                                                               |
| `no-arbitrary-value`               | `{ allow: [], allowVariables: 'none' }`                      | With `allowVariables: 'runtime'`, which CSS variables your stylesheet defines                                                                                             |
| `no-contradicting-variants`        | `{}`                                                         | What each variant's selector does                                                                                                                                         |
| `no-dark-without-light`            | `{ variants: ['dark'] }`                                     | Tells colours from the rest, and groups the base by declared CSS property                                                                                                 |
| `no-deprecated-classes`            | `{}`                                                         | Derives the rename list instead of using the built-in table                                                                                                               |
| `no-dynamic-classes`               | `{}`                                                         | Your project's own utilities and variants count as Tailwind's                                                                                                             |
| `no-hardcoded-colors`              | `{ allow: [] }`                                              | Names your theme's colors in the message                                                                                                                                  |

### DS-independent rules

| Rule                                    | Default options                 |
| --------------------------------------- | ------------------------------- |
| `enforce-consistent-important-position` | `{ position: 'suffix' }`        |
| `enforce-consistent-variable-syntax`    | `{ syntax: 'shorthand' }`       |
| `enforce-negative-arbitrary-values`     | (no options)                    |
| `max-class-count`                       | `{ max: 20 }`                   |
| `no-duplicate-classes`                  | (no options)                    |
| `no-restricted-classes`                 | `{ classes: [], patterns: [] }` |
| `no-unnecessary-whitespace`             | (no options)                    |
