Arbitrary Values & Properties
The theme covers most of what you need, but sometimes a design calls for something exact: a logo that must sit exactly 117px from the top, a colour from a client's brand guide, a grid with an unusual column pattern, or a progress bar whose width comes from a database. For these cases Tailwind lets you write arbitrary values right in the class name.
In this lesson you will use square-bracket values, CSS variables with the parentheses shorthand, type hints for ambiguous values, arbitrary CSS properties, and arbitrary variants. You will also learn the safe way to pass values that are only known at run time.
Square brackets for one-off values
Almost every utility accepts a value in square brackets instead of a theme key:
<div class="top-[117px] w-[calc(100%-2rem)] bg-[#0f766e] text-[22px] grid-cols-[14rem_1fr_auto]">...</div>Tailwind copies the value into the CSS as it is. w-[calc(100%-2rem)] becomes width: calc(100% - 2rem), and Tailwind even adds the spaces around the minus sign that calc() needs. Arbitrary values work with every variant, so md:top-[140px] and hover:bg-[#115e59] are fine.
Class names cannot contain spaces, because a space separates two classes. Write an underscore where the CSS needs a space: grid-cols-[14rem_1fr_auto] becomes grid-template-columns: 14rem 1fr auto. If you need a real underscore, escape it with a backslash, as in before:content-['file\_name']. Inside url() underscores are kept as they are, so image paths with underscores work.
CSS variables: the parentheses shorthand
To use a CSS custom property as the value, write it in round brackets:
<div class="bg-(--brand-color) p-(--card-padding) text-(--heading-size)">...</div>bg-(--brand-color) is short for bg-[var(--brand-color)]. You can even add an opacity modifier: bg-(--brand-color)/50.
Some utilities do two jobs. text-* sets either a colour or a font size, so with a variable Tailwind cannot tell which you mean. Add a type hint before the value:
text-(length:--heading-size)sets the font size.text-(color:--heading-colour)sets the colour.- The same works with brackets:
text-[length:var(--size)].
Dynamic values: a CSS variable in the style attribute
Values that come from your data, such as a student's progress percentage, a product's brand colour or a chart bar's height, cannot be written in your source files, so Tailwind cannot generate classes for them. The clean solution is to set a CSS variable in a style attribute and read it with a class:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Progress bars</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3"></script>
</head>
<body class="flex max-w-md flex-col gap-4 p-6">
<h2 class="font-semibold">Course progress</h2>
<div style="--progress: 72%">
<p class="flex justify-between text-sm"><span>Aarav</span><span>72%</span></p>
<div class="mt-1 h-3 rounded-full bg-slate-200">
<div class="h-3 w-(--progress) rounded-full bg-emerald-500"></div>
</div>
</div>
<div style="--progress: 35%">
<p class="flex justify-between text-sm"><span>Isha</span><span>35%</span></p>
<div class="mt-1 h-3 rounded-full bg-slate-200">
<div class="h-3 w-(--progress) rounded-full bg-amber-500"></div>
</div>
</div>
</body>
</html>Every bar uses the same class, w-(--progress); only the variable in style changes. In a PHP template you would write style="--progress: <?= (int) $percent ?>%", and in React style={{ '--progress': percent + '%' }}. Never build the class name itself, such as w-[${percent}%], because Tailwind will not find it.
Arbitrary properties
If Tailwind has no utility for a CSS property at all, write the whole declaration in square brackets, again with underscores for spaces:
<p class="[text-wrap:stable] [font-variant-caps:small-caps] hover:[text-shadow:0_0_8px_white]">...</p>
<div class="[--card-padding:1.5rem] p-(--card-padding) md:[--card-padding:2rem]">...</div>The second line is a handy pattern: an arbitrary property sets a CSS variable, a breakpoint changes it, and another utility uses it.
Arbitrary variants
Variants can be arbitrary too. Write a selector in square brackets, with & standing for the element itself:
| Class | Generated selector |
|---|---|
[&:nth-child(3)]:font-bold | .class:nth-child(3) |
[&>li]:py-2 | .class > li |
[&_a]:underline | .class a (any descendant link) |
[@supports(display:grid)]:grid | an @supports rule |
[&_a]:underline is especially useful when you cannot add classes to the inner elements, such as HTML that comes from a database:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Arbitrary variants</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3"></script>
</head>
<body class="p-6">
<div class="max-w-prose text-slate-700 [&_a]:text-sky-700 [&_a]:underline [&_h2]:mt-6 [&_h2]:text-xl [&_h2]:font-bold [&_h2]:text-slate-900 [&_li]:ms-5 [&_li]:list-disc [&_p]:mt-3">
<h2>Admission notice</h2>
<p>Forms for the B.Sc. Computer Science course in Rohtak are now open.</p>
<p>Bring these documents to the office:</p>
<ul>
<li>Class 12 marksheet</li>
<li>Two passport-size photos</li>
</ul>
<p>Read the <a href="#">full prospectus</a> before you apply.</p>
</div>
</body>
</html>The HTML inside the <div> has no classes at all; every style comes from arbitrary variants on the parent. For long articles, the typography plugin's prose class (see Typography) is usually simpler, but this technique is great for small blocks.
Many common needs already have named variants, which read better than brackets: *: for direct children, has-[...] for :has(), supports-[display:grid]: for feature queries, data-[state=open]: and aria-[sort=ascending]: for attributes, and min-[400px]: for one-off breakpoints. Prefer those when they exist.
When to use arbitrary values
Arbitrary values are an escape hatch, not the main road. A good rule:
- Used once, for something that really is unique? An arbitrary value is perfect.
- Used twice or more, or part of your brand? Add it to the theme with
@theme, give it a name, and usebg-brandinstead ofbg-[#0f766e]everywhere. See Customising with @theme. - Known only at run time? Set a CSS variable in
styleand read it withw-(--x)or similar.
Common mistakes
- Spaces inside brackets.
grid-cols-[1fr 2fr]is read as two broken classes. Usegrid-cols-[1fr_2fr]. - Building arbitrary classes from data.
`w-[${n}px]`never appears in your files. Use a CSS variable instyleinstead. - The v3 variable syntax.
bg-[--brand]is the old form; writebg-(--brand)in v4. - Arbitrary values everywhere. A page full of
p-[13px]andtext-[#333]loses the consistency that makes Tailwind useful. Prefer theme values.
Key points
- Square brackets accept any CSS value:
top-[117px],bg-[#0f766e],w-[calc(100%-2rem)]. - Use underscores for spaces inside brackets, and
\_for a real underscore. bg-(--brand)is the v4 shorthand for a CSS variable; add type hints liketext-(length:--size)when needed.- For run-time values, set a CSS variable in
styleand read it with a class such asw-(--progress). - Arbitrary properties (
[mask-type:luminance]) and arbitrary variants ([&_a]:underline) cover anything else. - Repeated arbitrary values belong in the theme.
Exercise
A marks report in Kochi shows each subject as a horizontal bar. Write the HTML for three subjects (Maths 88, Physics 74, Chemistry 91) where each bar's width comes from a --score CSS variable set in the style attribute. Make the bar colour come from a second variable, --bar, using the parentheses shorthand. Give the bar container a fixed height of exactly 14px with an arbitrary value.
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Marks report</title>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3"></script>
</head>
<body class="flex max-w-md flex-col gap-3 p-6">
<div style="--score: 88%; --bar: #0284c7">
<p class="text-sm">Maths: 88</p>
<div class="h-[14px] rounded bg-slate-200"><div class="h-full w-(--score) rounded bg-(--bar)"></div></div>
</div>
<div style="--score: 74%; --bar: #d97706">
<p class="text-sm">Physics: 74</p>
<div class="h-[14px] rounded bg-slate-200"><div class="h-full w-(--score) rounded bg-(--bar)"></div></div>
</div>
<div style="--score: 91%; --bar: #059669">
<p class="text-sm">Chemistry: 91</p>
<div class="h-[14px] rounded bg-slate-200"><div class="h-full w-(--score) rounded bg-(--bar)"></div></div>
</div>
</body>
</html>The classes are identical for every subject; only the style attribute changes, which is exactly how you would output the bars from a loop over database rows.
Quick Quiz
1. How do you write grid-template-columns: 1fr 2fr as a Tailwind class?
Underscores stand for spaces inside arbitrary values, because spaces separate class names.
2. What is the Tailwind v4 way to use the CSS variable --brand as a background colour?
v4 uses round brackets as a shorthand for var(); the square-bracket form without var() is from v3.
3. A progress value comes from your database. What is the safest way to set the bar width?
Tailwind cannot see class names created at run time, but a fixed class can read a CSS variable set in style.
4. What does [&_p]:mt-3 on a <div> do?
In an arbitrary variant & is the element itself and _ is a space, so the selector is .class p.
Learn Tailwind CSS with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
