Arbitrary Values and Custom Utilities
Use one-off values, custom properties, selector variants, and custom CSS without undermining consistency.
Arbitrary values and properties
When a design requires a value outside the standard scale, Tailwind supports bracket notation for many utilities, such as w-[37rem], grid-cols-[220px_minmax(0,1fr)], or top-[var(--header-height)]. Arbitrary values are useful for precise exceptions, but frequent one-off values may indicate a missing design token.
Spaces in arbitrary values often use underscores in class syntax. Follow the exact syntax supported by your Tailwind version, especially for CSS functions and custom properties. Avoid placing user-controlled input directly into class names.
<div style="--sidebar-width: 14rem" class="grid grid-cols-1 gap-5 lg:grid-cols-[var(--sidebar-width)_minmax(0,1fr)]">
<aside class="rounded-xl border p-4">Sidebar</aside>
<main class="min-w-0 rounded-xl border p-4">Main content</main>
</div>- Use arbitrary values for real exceptions, not every value.
- Use underscores where arbitrary-value syntax needs spaces.
- Promote repeated one-off values into reusable design tokens.
CSS variables and design tokens
CSS custom properties let you express design decisions by role. A variable can hold a brand color, header height, spacing value, or surface color. Tailwind utilities can reference CSS variables, allowing a token to be changed centrally while components retain their structure.
A good token system separates raw palette values from semantic roles. For example, a blue palette may contain several shades, while --color-action represents the shade chosen for primary actions. This makes future rebranding and theme changes easier.
--color-action explains the role; --blue-700 describes the current color. Semantic tokens make theme changes easier to reason about.
:root {
--color-action: #1d4ed8;
--color-surface: #ffffff;
--color-text: #0f172a;
--radius-card: 1rem;
}
[data-theme="dark"] {
--color-surface: #0f172a;
--color-text: #f1f5f9;
}When to write custom CSS or extract components
Tailwind is not a rule that forbids CSS files. Use custom CSS for complex pseudo-elements, intricate animations, third-party overrides, or selectors that become awkward as utilities. Use components for repeated UI structures with meaningful behavior or API boundaries.
In React, extract a component when repetition, semantic meaning, behavior, or testing needs justify it—not merely because a class string has many tokens. A PrimaryButton can centralize interaction states and accessibility; a generic wrapper with dozens of boolean props can become harder to maintain than the original markup.
const buttonVariants = {
primary: "bg-blue-600 text-white hover:bg-blue-700 focus-visible:outline-blue-600",
secondary: "border border-slate-300 bg-white text-slate-900 hover:bg-slate-50 focus-visible:outline-slate-500",
};
export function Button({ variant = "primary", children, ...props }) {
return (
<button
{...props}
className={`inline-flex items-center justify-center rounded-lg px-4 py-2.5 text-sm font-semibold focus-visible:outline-2 focus-visible:outline-offset-2 ${buttonVariants[variant]}`}
>
{children}
</button>
);
}variant to the supported keys and merge caller classes with a deliberate class-merging strategy if overrides are allowed.When is extracting a Tailwind pattern into a component most useful?