Overview

Arbitrary values let you write any CSS value inline as a Tailwind class: w-[412px], bg-[oklch(0.72_0.18_264)], mt-[calc(var(--header-height)+1rem)]. They exist to handle values the design system has not yet tokenized, not to replace the design system. The parent reference is tailwind; for the token system see tailwind-theme.

Extract to @theme after the third use

One arbitrary value in a file is a one-off. Two uses in the same component might be a coincidence. Three uses of the same value across the codebase is a missing design token.

/* Before: the same value scattered across three files */
/* w-[412px] in card.tsx, modal.tsx, sidebar.tsx */
 
/* After: one token, three uses */
@theme {
  --width-panel: 412px;
}
/* Now use: w-panel */

This is the extraction rule. It has no exception for “convenient” or “obvious” values. The point of the design system is that every value has a name and a home.

Use arbitrary values for genuinely one-off overrides

Some values are legitimately one-off: a clip-path, a background gradient with specific stops, a CSS grid template that is unique to one layout. For these, arbitrary values are the right tool.

<div class="[clip-path:polygon(0_0,100%_0,100%_80%,0_100%)]">
<div class="bg-[linear-gradient(135deg,oklch(0.72_0.18_264),oklch(0.55_0.22_310))]">
<div class="grid-[auto_1fr_auto]">

If the value appears once and there is no design-system motivation to name it, an arbitrary value is correct. Document why with a comment if the value is not self-explanatory.

Use arbitrary properties for non-standard CSS

Any CSS property can be set with the [property:value] syntax.

<div class="[content-visibility:auto] [contain-intrinsic-size:0_500px]">
<p class="[text-wrap:pretty] [hyphenate-limit-chars:8_4_4]">

This works for experimental properties, vendor-specific properties, and CSS properties Tailwind does not expose as named utilities. No plugin or custom utility needed for a single-use case.

Write arbitrary variants for one-off selectors

Arbitrary syntax also applies to variants. Use [&...] for selectors that do not have a named variant.

<ul class="[&>li]:py-2 [&>li:first-child]:pt-0 [&>li:last-child]:pb-0">

The & refers to the element bearing the class. This handles nth-child, not, is, attribute selectors, and anything else CSS supports. If the pattern appears in three or more places, define a @custom-variant instead; see tailwind-variants.

Avoid using arbitrary values to work around a missing token

Arbitrary values that should be tokens are the most common form of design system debt.

Signs you are using an arbitrary value when you should add a token:

  • The value matches a number from the design spec but not from the default Tailwind scale.
  • You have copied the same arbitrary value from another component by muscle memory.
  • A search for the value in your codebase returns more than two results.

When you find one of these, add the token to @theme, search-and-replace the arbitrary values, and commit the cleanup as a separate commit from the feature work.

Prefer var(--token) over arbitrary values in complex expressions

Complex calc() or clamp() expressions that reference design tokens read better using var() than repeating raw values.

<!-- Less clear: magic numbers, no design intent expressed -->
<div class="mt-[calc(64px+1rem)]">
 
<!-- Better: names express intent, calc stays readable -->
<div class="mt-[calc(var(--spacing-header)+var(--space-4))]">

Arbitrary values containing var() references are still arbitrary values, but they preserve the connection to the design system.

Keep arbitrary values out of shared components

Shared components should use named tokens. An arbitrary value in a component that is copied across many projects becomes a maintenance problem when the design system changes.

If you are building a component for a library or design system, every value should come from a @theme token or a CSS custom property that the consumer can override. Arbitrary values are for product code, not for reusable components.