Skip to content

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 ​

AutofixEditor suggestionsDesign systemOptions
NoNoOptional — used when entryPoint is setallow, 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 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). 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.

Released under the MIT License.