# no-arbitrary-value

oxlint rule that reports Tailwind CSS arbitrary values like `w-[200px]` or `bg-[#ff0000]`, so values
come from your theme; an allow list covers exceptions.

## At a glance

| Autofix | Editor suggestions | Design system                            | Options                                 |
| ------- | ------------------ | ---------------------------------------- | --------------------------------------- |
| No      | No                 | Optional — used when `entryPoint` is set | `allow`, `allowVariables`, `entryPoint` |

## What this rule does

Flags any Tailwind utility that uses an arbitrary value — the escape hatch — so your team is forced
to either reach for a design token or extend the theme instead of one-off literals. Anything the
parser recognizes as an arbitrary value qualifies: dimensions (`w-[200px]`, `text-[14px]`), colors
(`bg-[#ff0000]`), arbitrary calc expressions (`p-[calc(1rem+2px)]`), gradients, and the same inside
variants (`hover:w-[200px]`) or with the `!` modifier (`!w-[200px]`, `w-[200px]!`).

**Both spellings count.** `bg-(--brand)` is Tailwind v4 sugar for `bg-[var(--brand)]` — the same
arbitrary value, written two ways — and only the bracket form used to be reported. That made
[`enforce-consistent-variable-syntax`](./enforce-consistent-variable-syntax) a laundry service: with
its default `shorthand` setting, its autofix rewrote the reported form into the unreported one and
the violation disappeared with the code unchanged in substance.

DS-optional — no `entryPoint` is needed: whether a class is an arbitrary value is a syntactic check,
cheap on large repos. With one configured, the report also names the fix — the closest steps and
tokens of your theme, with their values, and the file to add a token to:

```text
"w-[203px]" uses an arbitrary value. Closest in your theme: w-50.5 (202px) or w-51 (204px).
If none fits, add a token to src/styles.css.
```

An exact step or token is offered alone (`h-[26px]` → `h-6.5 (26px)`); otherwise the nearest one
below and above. A value that isn't a length (a colour, a `calc()`) gets just the file. The design
system is loaded on the first violation, never for a clean file; `allowVariables: 'runtime'` uses it
too, to know which CSS variables your stylesheet defines. There is no autofix: replacing an
arbitrary value with a token requires human judgement, so the rule only reports.

Arbitrary _variants_ (`[&>svg]:w-4`) are not arbitrary _values_ and are left alone. The rule looks
at the value side of the utility, not the selector prefix.

## Options

### `allow`

`string[]`, default `[]`.

A list of utility prefixes where arbitrary values are tolerated. The match is a literal
`String.prototype.startsWith` against the utility's bare form (after stripping `!` and any
variants), so entries like `"grid-cols-"` permit `grid-cols-[18rem_1fr]` without unlocking
`grid-rows-[...]`. Common candidates: container queries (`@container`), grid templates
(`grid-cols-`, `grid-rows-`), and truly bespoke transforms (`bg-[url(`).

```jsonc
{
  "tailwindcss/no-arbitrary-value": ["error", {
    "allow": ["grid-cols-", "grid-rows-", "bg-[url("]
  }]
}
```

Tip: start with `[]` to see where your codebase actually needs escapes, then promote the legitimate
ones into `allow` rather than disabling the rule wholesale.

### `allowVariables`

`'none' | 'runtime' | 'all'`, default `'none'`.

What to do with a value that is nothing but a CSS variable — `w-(--sidebar-width)`,
`h-[var(--radix-select-trigger-height)]`, `text-(length:--size)`, with or without a fallback or an
opacity modifier:

- `'none'` reports them, like any arbitrary value.
- `'runtime'` allows the variables **no CSS in your design system defines** — the ones JavaScript or
  an inline `style` sets at runtime, like Radix's `--radix-*` or a sidebar's `--sidebar-width`, for
  which no token exists. A variable your stylesheet defines (`--primary`) is still reported: a named
  utility exists for it (see [`prefer-theme-tokens`](./prefer-theme-tokens)). This mode reads the
  design system; without an `entryPoint` nothing can be proven undefined, so references are
  reported.
- `'all'` allows every pure variable reference.

A value that does more than read a variable — `w-[calc(var(--x)*2)]` — is an arbitrary value in
every mode.

```jsonc
{ "tailwindcss/no-arbitrary-value": ["warn", { "allowVariables": "runtime" }] }
```

### `entryPoint`

`string`, optional. Per-rule override of `settings.tailwindcss.entryPoint`, read only by
`allowVariables: 'runtime'`.

## Examples

### ✗ Incorrect

```tsx
// One-off dimension instead of a spacing token
<div className="w-[200px] p-[10px]" />

// Hex literal instead of a theme color
<div className="bg-[#ff0000] text-white" />

// Variant doesn't change the verdict
<div className="hover:w-[200px]" />

// `!` modifier doesn't either
<div className="!w-[200px]" />

// The v4 shorthand is the same arbitrary value
<div className="bg-(--brand) border-(length:--stroke)" />
```

### ✓ Correct

```tsx
// Use spacing tokens
<div className="w-64 p-2.5" />

// Use a theme color you declared in @theme
<div className="bg-brand text-white" />

// Arbitrary variants are fine — they target selectors, not values
<div className="[&>svg]:w-4" />

// Allowed via `allow: ["grid-cols-"]`
// options: { "allow": ["grid-cols-"] }
<div className="grid grid-cols-[18rem_1fr]" />
```

## Interactions with other rules

- **`no-hardcoded-colors`**: a strict subset focused on color utilities (`bg-[#ff0]`,
  `text-[rgb(...)]`). Enable that one if you want the granular color-only message; enable both for
  layered enforcement. They'll both fire on the same class, which is usually desired during
  migration.
- **`no-unnecessary-arbitrary-value`**: this rule prohibits arbitrary values outright;
  `no-unnecessary-arbitrary-value` is gentler — it only flags arbitraries that have an exact named
  equivalent (`w-[100%]` → `w-full`). Both can coexist: the gentler rule autofixes the trivial cases
  and this rule catches what's left.
- **`prefer-theme-tokens`**: similar intent, but DS-dependent. `prefer-theme-tokens` actually
  consults your `@theme` and suggests the matching token. Pair them: this rule is your stop-gap when
  no token exists yet.
- **`enforce-consistent-variable-syntax`**: converts between `bg-[var(--x)]` and `bg-(--x)`. Since
  both forms report here, running it — in either direction — cannot move a class out of this rule's
  reach.

## When to disable it

- **Prototyping / spike branches** where speed beats discipline. Re-enable before merging.
- **Files that genuinely need an arbitrary value** (one-off marketing pages, dynamic CSS-in-JS
  shims). Prefer `allow` with a tight prefix list, or disable per-line via
  `// oxlint-disable-next-line tailwindcss/no-arbitrary-value`.
- **Libraries that ship Tailwind utilities consumers re-skin**: the arbitrary form is sometimes the
  cleanest contract. Document the decision and disable the rule in that package.
