Skip to content

Commit 9b8c63f

Browse files
FreekHeijtingclaude
andcommitted
feat(skill): tailwind-syntax-arbitrary-values
Syntax skill: arbitrary-value system (value/modifier/variant/property/ type-hinted forms), JIT literal-token detection, v3 brackets vs v4 parens shorthand for CSS variables, type-hint disambiguation, space encoding with underscore. Anti-patterns: dynamic class interpolation, v4-parens-in-v3, missing type hints, arbitrary erosion of theme tokens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e5d260d commit 9b8c63f

4 files changed

Lines changed: 1084 additions & 0 deletions

File tree

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
---
2+
name: tailwind-syntax-arbitrary-values
3+
description: >
4+
Use when needing a one-off value that is not in the theme: specific hex colors,
5+
arbitrary pixel sizes, calc() expressions, custom grid templates, unusual font sizes,
6+
specific transforms, custom selectors, runtime CSS-variable values. Use also when
7+
reading existing Tailwind markup and encountering `bg-[#1da1f2]`, `w-[calc(100%-2rem)]`,
8+
`data-[state=open]:`, `[mask-type:luminance]`, `bg-(--brand)`, `[&_p]:`, or any other
9+
square-bracket / parens-bracket utility, and needing to know what it compiles to.
10+
Use also when an arbitrary value is being misinterpreted (background-size vs
11+
background-position, font-size vs color) and a type hint is required.
12+
Prevents (a) reaching for arbitrary syntax when a theme token already exists (lazy
13+
arbitrary values fragment design systems), (b) dynamic class-string concatenation
14+
like `bg-[${color}]` that never appears as a literal token and silently generates
15+
no CSS, (c) using v4 parens CSS-var syntax `bg-(--brand)` in a v3 project where it
16+
is parsed incorrectly, (d) using literal spaces inside `[...]` which terminates the
17+
class-name token, (e) mistaking a type-hint prefix as the property name itself
18+
(`text-[color:var(--c)]` is a `color` utility, not a property assignment), and (f)
19+
reaching for `text-[14.5px]` when `text-base/[1.4]` size-plus-line-height syntax
20+
fits, or `mt-17` in v4 where dynamic integer spacing avoids arbitrary syntax entirely.
21+
Covers the complete arbitrary-value system: value-arbitrary `bg-[#hex]`, `w-[calc()]`,
22+
`text-[Npx]`, `top-[-Npx]`, modifier-arbitrary `[&:nth-child(3)]:`, `[&>*]:`, `[&_p]:`,
23+
variant-arbitrary `data-[state=open]:`, `aria-[expanded=true]:`, `supports-[display:grid]:`,
24+
arbitrary-property `[mask-type:luminance]`, `[scroll-snap-stop:always]`, type-hint
25+
disambiguation `bg-[length:200px_100px]`, `bg-[image:url(...)]`, `text-[color:var(--c)]`,
26+
`text-[length:var(--c)]`, CSS-variable shorthand `bg-(--brand)` (v4 parens) vs
27+
`bg-[var(--brand)]` (v3 brackets), space encoding with `_`, comma encoding with `_`
28+
in v4 (`grid-cols-[max-content_1fr]`), source-detection rules (classes must be
29+
literal whole tokens), JIT performance model (v3 JIT vs v4 Oxide), and the v4
30+
dynamic-integer fallback for spacing that obviates many arbitrary use cases.
31+
Keywords: arbitrary value, arbitrary class, brackets syntax, bracket notation,
32+
one-off color, bg-[hex], w-[calc], text-[px], custom value, custom selector,
33+
arbitrary variant, arbitrary modifier, arbitrary property, [mask-type], data-[state],
34+
aria-[expanded], type hint, length color image, bg-[length:], text-[color:],
35+
CSS variable Tailwind, bg-(--var), v4 parens syntax, bg-[var()], v3 brackets,
36+
underscore space, why does my arbitrary class not work, dynamic class not generated,
37+
JIT compiler, just in time, Oxide engine, performance, literal token, class detection,
38+
source scanning, mt-17, dynamic spacing.
39+
license: MIT
40+
compatibility: "Designed for Claude Code. Requires Tailwind CSS v3.4 or v4.0+."
41+
metadata:
42+
author: OpenAEC-Foundation
43+
version: "1.0"
44+
---
45+
46+
# Tailwind CSS Syntax: Arbitrary Values
47+
48+
Arbitrary values are Tailwind's escape hatch: any utility can accept a one-off value via square-bracket syntax (or parens for CSS variables in v4). The syntax is identical in v3 and v4 ; the only difference is the v4 parens shorthand for CSS variables and the v4 Oxide engine that generates the CSS roughly 5x faster. Mastering arbitrary values means knowing WHEN to reach for them (rarely) and WHEN to use a theme token (almost always).
49+
50+
## Quick Reference
51+
52+
### Five arbitrary-value patterns
53+
54+
| Pattern : example : compiles to |
55+
|--|
56+
| Value-arbitrary : `bg-[#1da1f2]` : `background-color: #1da1f2` |
57+
| Modifier-arbitrary : `[&:nth-child(3)]:underline` : custom selector wraps the utility |
58+
| Variant-arbitrary : `data-[state=open]:rotate-180` : `[data-state="open"] { rotate: 180deg }` |
59+
| Arbitrary property : `[mask-type:luminance]` : `mask-type: luminance` (no utility namespace at all) |
60+
| Type-hinted arbitrary : `bg-[length:200px_100px]` : disambiguates ambiguous values |
61+
62+
### Top 3 most-used patterns
63+
64+
```html
65+
<!-- 1. One-off color or size (works v3 + v4) -->
66+
<button class="bg-[#1da1f2] text-[14.5px] w-[calc(100%-2rem)]">Custom values</button>
67+
68+
<!-- 2. CSS variable for runtime theming -->
69+
<!-- v4 parens shorthand -->
70+
<div class="bg-(--brand-bg) text-(--brand-fg)">v4</div>
71+
<!-- v3 brackets equivalent -->
72+
<div class="bg-[var(--brand-bg)] text-[var(--brand-fg)]">v3</div>
73+
74+
<!-- 3. Arbitrary variant for one-off selector -->
75+
<ul class="[&_li]:list-disc [&>li:first-child]:font-bold">
76+
<li>First (bold)</li>
77+
<li>Second</li>
78+
</ul>
79+
```
80+
81+
### Type hints when ambiguous
82+
83+
| Hint | When needed | Example |
84+
|------|-------------|---------|
85+
| `length:` | distinguish from color/keyword on `text-*`, `bg-*` with CSS var | `text-[length:var(--my-size)]` |
86+
| `color:` | distinguish from length on `text-*`, `bg-*` with CSS var | `text-[color:var(--my-color)]` |
87+
| `image:` | distinguish from length/color on `bg-*` | `bg-[image:url('/hero.png')]` |
88+
| `family-name:` | distinguish on `font-*` with CSS var | `font-[family-name:var(--my-font)]` |
89+
| `position:` / `size:` | distinguish on `bg-*` | `bg-[position:50%_50%]`, `bg-[size:cover]` |
90+
91+
## Decision Trees
92+
93+
### Should I use arbitrary syntax at all?
94+
95+
```
96+
need a value outside the default theme?
97+
├── value used in 2+ places?
98+
│ └── ALWAYS extend the theme (v4 @theme or v3 tailwind.config.js) ; NEVER duplicate arbitrary values
99+
├── value used exactly once + unlikely to repeat?
100+
│ └── arbitrary value is fine
101+
├── value comes from runtime data (user picks color, server sends size)?
102+
│ └── ALWAYS use inline style `style={{...}}` for fully dynamic values, NEVER arbitrary
103+
├── value is an integer multiple of base spacing (Tailwind v4)?
104+
│ └── ALWAYS use dynamic integer utility `mt-17`, `gap-43` ; NEVER `mt-[68px]` when on v4
105+
└── value is a CSS variable from your theme?
106+
├── v4 : ALWAYS use parens shorthand `bg-(--brand)` (shorter)
107+
└── v3 : ALWAYS use brackets with `var()` `bg-[var(--brand)]`
108+
```
109+
110+
### Picking the right arbitrary-value flavour
111+
112+
```
113+
what am I customising?
114+
├── the value of an existing utility (color, size, calc()) : VALUE-arbitrary
115+
│ └── `bg-[#hex]`, `w-[calc()]`, `text-[Npx]`, `rotate-[7.5deg]`
116+
├── a CSS property Tailwind does not ship a utility for : ARBITRARY PROPERTY
117+
│ └── `[mask-type:luminance]`, `[scroll-snap-stop:always]`, `[contain:layout]`
118+
├── the selector the utility applies to : MODIFIER-arbitrary
119+
│ └── `[&_p]:text-red-500`, `[&>*]:p-4`, `[&.dragging]:cursor-grabbing`
120+
├── a custom attribute or media match : VARIANT-arbitrary
121+
│ └── `data-[state=open]:rotate-180`, `aria-[sort=ascending]:bg-blue-50`, `supports-[display:grid]:grid`
122+
└── a value that is ambiguous (string could parse multiple ways) : TYPE-HINTED arbitrary
123+
└── `bg-[length:200px_100px]`, `text-[color:var(--c)]`, `text-[length:var(--c)]`
124+
```
125+
126+
### Why is my arbitrary class not generating CSS?
127+
128+
```
129+
class is in the DOM but no CSS rule exists?
130+
├── built via string concatenation (`bg-[${color}]` or `bg-[#${hex}]`)?
131+
│ └── ROOT CAUSE : Tailwind scans source as literal tokens. Concatenation NEVER produces
132+
│ a complete literal class. Fix : static map or inline `style`.
133+
├── value contains an unencoded space (`grid-cols-[1fr 2fr]`)?
134+
│ └── ROOT CAUSE : space terminates the class-name token. Fix : `grid-cols-[1fr_2fr]`.
135+
├── value contains a CSS variable without proper syntax?
136+
│ ├── v3 : MUST be `bg-[var(--c)]` (full var() form inside brackets)
137+
│ └── v4 : either `bg-[var(--c)]` (still works) or `bg-(--c)` (shorthand)
138+
├── using v4 parens syntax `bg-(--c)` in v3 project?
139+
│ └── ROOT CAUSE : v3 does NOT parse parens-arbitrary. Fix : use `bg-[var(--c)]` in v3.
140+
├── ambiguous value (length vs color from CSS var)?
141+
│ └── ROOT CAUSE : Tailwind cannot disambiguate at compile time. Fix : type hint
142+
│ `text-[color:var(--c)]` or `text-(color:--c)` (v4).
143+
└── class only appears in production after a build?
144+
└── ROOT CAUSE : content scanning missed the source file. Fix : adjust content glob
145+
(v3) or add `@source "./path"` (v4).
146+
```
147+
148+
## Patterns
149+
150+
### Pattern 1: Value-arbitrary
151+
152+
The single most common form. Square brackets wrap a CSS value the utility would otherwise refuse.
153+
154+
```html
155+
<!-- Colors -->
156+
<div class="bg-[#1da1f2]">Twitter blue</div>
157+
<div class="text-[rgb(20,30,40)]">Custom rgb</div>
158+
<div class="border-[oklch(0.65_0.196_254)]">Custom oklch</div>
159+
160+
<!-- Sizes -->
161+
<div class="w-[calc(100%-2rem)]">Width via calc</div>
162+
<div class="h-[80vh]">Viewport-relative</div>
163+
<div class="text-[14.5px]">Sub-pixel font size</div>
164+
<div class="text-[14.5px]/[1.3]">Size + line-height in one</div>
165+
<div class="top-[-113px]">Negative top (auto-detected as negative)</div>
166+
167+
<!-- Grids -->
168+
<div class="grid grid-cols-[24rem_2.5rem_minmax(0,1fr)]">Custom 3-column layout</div>
169+
170+
<!-- Transforms / filters -->
171+
<div class="rotate-[7.5deg] blur-[2.5px] hue-rotate-[15deg]">Custom rotations</div>
172+
```
173+
174+
### Pattern 2: Modifier-arbitrary (custom selector)
175+
176+
The `[&...]:utility` form gives the utility a custom selector. `&` is the styled element ; `_` represents a space in the selector.
177+
178+
```html
179+
<!-- Direct child selector -->
180+
<ul class="[&>li]:list-disc [&>li]:ml-4">
181+
<li>One</li>
182+
<li>Two</li>
183+
</ul>
184+
185+
<!-- Descendant selector (use _ for the space) -->
186+
<article class="[&_p]:text-slate-600 [&_h2]:text-2xl [&_a]:text-blue-600">
187+
<h2>Heading</h2>
188+
<p>Body</p>
189+
<a>Link</a>
190+
</article>
191+
192+
<!-- Sibling and attribute selectors -->
193+
<input class="peer [&~p]:hidden focus:[&~p]:block" />
194+
195+
<!-- Pseudo-class shortcut for things not in the built-in list -->
196+
<li class="[&:nth-child(-n+3)]:font-bold">First 3 items bold</li>
197+
<li class="[&::-webkit-scrollbar]:hidden">Hide webkit scrollbar</li>
198+
199+
<!-- Complex selector combining multiple parts -->
200+
<div class="[&>[data-active]+span]:text-blue-600">
201+
<button data-active>...</button>
202+
<span>Sibling of active gets blue</span>
203+
</div>
204+
```
205+
206+
### Pattern 3: Variant-arbitrary (custom attribute or query)
207+
208+
Built-in variants like `aria-checked:` and `data-active:` are shortcuts. Arbitrary variants accept any value or any selector.
209+
210+
```html
211+
<!-- Custom data attribute value -->
212+
<div data-state="open" class="data-[state=open]:rotate-180 data-[state=closed]:rotate-0">
213+
Chevron
214+
</div>
215+
216+
<!-- Custom aria attribute value -->
217+
<th aria-sort="ascending" class="aria-[sort=ascending]:bg-blue-50">
218+
Name
219+
</th>
220+
221+
<!-- Browser feature detection -->
222+
<div class="grid-cols-3 grid supports-[display:grid]:gap-4 not-supports-[backdrop-filter]:bg-white">
223+
Grid + fallback
224+
</div>
225+
226+
<!-- Custom at-rule -->
227+
<div class="[@media(prefers-reduced-data:reduce)]:bg-none">
228+
Saves bandwidth
229+
</div>
230+
<div class="[@container(width>=600px)]:grid">
231+
Container-query escape hatch
232+
</div>
233+
```
234+
235+
### Pattern 4: Arbitrary property (no utility namespace)
236+
237+
When Tailwind ships no utility for a CSS property at all, use `[property:value]`.
238+
239+
```html
240+
<!-- CSS properties without Tailwind utilities -->
241+
<div class="[mask-type:luminance] hover:[mask-type:alpha]">SVG mask switching</div>
242+
<section class="[scroll-snap-stop:always] [scroll-snap-align:center]">Snap target</section>
243+
<div class="[contain:layout] [content-visibility:auto]">Optimisation hints</div>
244+
<div class="[view-transition-name:hero]">View Transitions API</div>
245+
<input class="[appearance:textfield] [-moz-appearance:textfield]">Strip number arrows</input>
246+
```
247+
248+
These compile to a direct CSS declaration with no class-name prefix : the `[mask-type:luminance]` utility produces `mask-type: luminance;`.
249+
250+
### Pattern 5: Type hints (disambiguation)
251+
252+
```html
253+
<!-- Ambiguous : 200px_100px could be size or position -->
254+
<div class="bg-[length:200px_100px]">Background-size</div>
255+
<div class="bg-[position:200px_100px]">Background-position</div>
256+
257+
<!-- Ambiguous : a CSS var could be color or length -->
258+
<div class="text-[color:var(--accent)]">Color utility</div>
259+
<div class="text-[length:var(--size)]">Font-size utility</div>
260+
261+
<!-- v4 parens shorthand with type hint -->
262+
<div class="text-(color:--accent)">v4 color</div>
263+
<div class="text-(length:--size)">v4 size</div>
264+
265+
<!-- Always-needed type hints -->
266+
<div class="bg-[image:url('/hero.png')]">Force image interpretation</div>
267+
<div class="font-[family-name:var(--my-font)]">Force family-name interpretation</div>
268+
```
269+
270+
Without the hint, Tailwind would either guess wrong or refuse to compile.
271+
272+
### Pattern 6: CSS variables (v3 vs v4)
273+
274+
```html
275+
<!-- v3 : ALWAYS brackets with full var() form -->
276+
<div class="bg-[var(--brand-bg)] text-[var(--brand-fg)]">v3</div>
277+
278+
<!-- v4 : EITHER brackets with var() (still works) OR parens shorthand -->
279+
<div class="bg-[var(--brand-bg)] text-[var(--brand-fg)]">v4 explicit</div>
280+
<div class="bg-(--brand-bg) text-(--brand-fg)">v4 shorthand (preferred)</div>
281+
282+
<!-- v4 parens + type hint for ambiguous CSS vars -->
283+
<div class="text-(color:--accent)">v4 color from CSS var</div>
284+
<div class="text-(length:--size)">v4 size from CSS var</div>
285+
```
286+
287+
The v4 parens shorthand IS NOT available in v3. Mixing in a v3 project breaks silently.
288+
289+
## Performance: JIT and Class Detection
290+
291+
Both v3 and v4 use Just-In-Time compilation : Tailwind scans source files for class names and generates CSS only for the classes that appear. ALWAYS write class names as **complete literal tokens** in source. NEVER concatenate, interpolate, or build them dynamically at template-render time.
292+
293+
```jsx
294+
// BROKEN : never generates CSS
295+
<div className={`bg-[${color}]`} />
296+
297+
// FIXED : the literal class string appears in source
298+
const STYLE_MAP = { primary: 'bg-[#1da1f2]', secondary: 'bg-[#888]' }
299+
<div className={STYLE_MAP[variant]} />
300+
301+
// FIXED for truly dynamic values : use inline style
302+
<div style={{ backgroundColor: color }} />
303+
```
304+
305+
| Version | Engine | Full build | Incremental |
306+
|---------|--------|-----------|-------------|
307+
| v3.4 JIT | JavaScript-based JIT | baseline | baseline |
308+
| v4.0+ Oxide | Rust + Lightning CSS | ~5x faster | ~100x faster |
309+
310+
The performance gain comes from Oxide ; the source-detection RULES are identical.
311+
312+
## Reference Links
313+
314+
- `references/methods.md` : complete arbitrary-value syntax reference (every flavour, every type hint, v3 vs v4 differences).
315+
- `references/examples.md` : worked examples for value/modifier/variant/property/type-hinted/CSS-variable forms across both versions.
316+
- `references/anti-patterns.md` : the dynamic-class-string trap, missing type hints, v4-parens-in-v3 mistakes, spaces inside brackets, reaching for arbitrary when a theme token or v4 dynamic integer fits.
317+
318+
## Verified Sources
319+
320+
- https://tailwindcss.com/docs/styling-with-utility-classes (arbitrary values, type hints, v4 parens shorthand)
321+
- https://tailwindcss.com/docs/adding-custom-styles (arbitrary values + JIT class-detection rules)
322+
- https://tailwindcss.com/blog/just-in-time-the-next-generation-of-tailwind-css (v3 JIT origin)
323+
- https://tailwindcss.com/blog/tailwindcss-v4 (v4 Oxide engine + parens shorthand)
324+
- https://tailwindcss.com/docs/detecting-classes-in-source-files (literal-token scan rule)
325+
326+
Last verified : 2026-05-19 (Tailwind CSS v4.x current).

0 commit comments

Comments
 (0)