SyntaxStudy
Sign Up
Tailwind CSS Arbitrary Properties, Variants, and Dynamic Classes
Tailwind CSS Beginner 1 min read

Arbitrary Properties, Variants, and Dynamic Classes

Tailwind v3.3 extended arbitrary value support to arbitrary CSS properties — any CSS property not covered by a built-in utility can be applied using the [property:value] syntax directly in your class attribute. [mask-type:luminance] generates mask-type: luminance, [content-visibility:auto] generates the CSS property verbatim, and [scroll-snap-align:start] applies a scroll-snap property. This makes Tailwind a complete CSS authoring solution even for CSS features that postdate the framework. Arbitrary variants extend the variant system in the same way. Instead of only the built-in hover:, focus:, and responsive prefixes, you can write [&:nth-child(3)]:bg-blue-100 to target the third child, [&>li]:list-disc to style direct list-item children, or [.open_&]:flex to apply styles when an ancestor has the open class. The & represents the current element in these CSS selector expressions. Dynamic class name generation from JavaScript requires care because Tailwind's JIT scanner reads source files as text and extracts class names by pattern matching. It does not execute JavaScript, so concatenated strings like 'bg-' + color or template literals like `text-${size}-500` produce classes that will be purged. The fix is always to use complete, unbroken class name strings in your source — computed through conditional expressions that evaluate to full class names, not string fragments.
Example
<!-- Arbitrary CSS properties (Tailwind v3.3+) -->
<div class="[mask-type:luminance] [content-visibility:auto]">
  Arbitrary CSS properties
</div>

<!-- Scroll snap with arbitrary properties -->
<div class="overflow-x-auto flex gap-4 [scroll-snap-type:x_mandatory]">
  <div class="shrink-0 w-80 [scroll-snap-align:start] bg-white rounded-xl p-4">Slide 1</div>
  <div class="shrink-0 w-80 [scroll-snap-align:start] bg-white rounded-xl p-4">Slide 2</div>
  <div class="shrink-0 w-80 [scroll-snap-align:start] bg-white rounded-xl p-4">Slide 3</div>
</div>

<!-- Arbitrary variants: target specific children -->
<ul class="[&>li]:py-2 [&>li]:border-b [&>li]:border-gray-200 [&>li:last-child]:border-0">
  <li>First item</li>
  <li>Second item</li>
  <li>Third item</li>
</ul>

<!-- nth-child arbitrary variant -->
<div class="grid grid-cols-3 gap-2 [&>*:nth-child(3n+1)]:bg-blue-50 [&>*:nth-child(3n+2)]:bg-green-50 [&>*:nth-child(3n)]:bg-rose-50">
  <div class="p-3 rounded">A</div>
  <div class="p-3 rounded">B</div>
  <div class="p-3 rounded">C</div>
</div>

<!-- CORRECT: full class names in conditionals (JIT can detect these) -->
<div class="{{ $status === 'active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' }}
            px-2 py-1 rounded-full text-xs font-semibold">
  Status badge
</div>

<!-- WRONG: JIT cannot detect these concatenated fragments -->
<!--
  BAD: class="bg-{{ $color }}-500"
  BAD: class="`text-${size}`"
  GOOD: always write full class names
-->