Tailwind CSS v4 introduces a powerful feature: the ability to define your own utility classes directly in CSS using the @utility
directive. Combined with --value()
placeholders, this unlocks advanced, dynamic class patterns β all without JavaScript or tailwind.config.js
.
This README explains how @utility
works, how to use --value(integer)
and --value(--theme-key-*)
, and provides working examples including an innovative color utility pattern.
The @utility
directive allows you to define custom CSS classes as part of Tailwindβs generation process.
There are two main types of utility classes you can define:
@utility card {
@apply bg-white p-4 rounded shadow;
}
Generates a single class .card
.
@utility tab-* {
tab-size: --value(integer);
}
Generates:
.tab-2
βtab-size: 2;
.tab-4
βtab-size: 4;
The *
acts as a placeholder for a dynamic segment, and --value(integer)
extracts it as a raw number.
When you define a class like mt-*
, and use --value(integer)
:
@utility mt-* {
margin-top: calc(0.25rem * --value(integer));
}
Then:
<div class="mt-4"></div>
...generates:
.mt-4 {
margin-top: calc(0.25rem * 4);
}
This is useful for spacing, sizes, grid values, etc.
For cases where you want to dynamically generate utility classes that map to CSS variables (like theme colors), you can combine --value()
with a variable prefix.
@theme {
--color-primary: oklch(65% 0.25 270);
--color-accent: oklch(75% 0.22 320);
}
Then define a dynamic utility:
@utility custom-link-* {
color: --value(--color-*);
}
Which interprets:
<a class="custom-link-primary">Primary</a>
<a class="custom-link-accent">Accent</a>
...as:
.custom-link-primary {
color: var(--color-primary);
}
.custom-link-accent {
color: var(--color-accent);
}
color: --value(--color-*);
...is valid and works, even though itβs not yet documented in the official Tailwind docs. You're using the value in the class name (primary
, accent
, etc.) to reference a CSS variable --color-*
.
@import "tailwindcss";
@theme {
--color-primary: oklch(65% 0.25 270);
--color-accent: oklch(75% 0.22 320);
}
@utility custom-link-* {
position: relative;
color: --value(--color-*);
font-weight: 600;
&:hover {
text-decoration: none;
}
&::before,
&::after {
position: absolute;
content: "";
width: 0;
transition: width 0.2s;
}
&::before {
bottom: -0.25rem;
left: 45%;
border-top: 2px solid --value(--color-*);
}
&::after {
bottom: -0.25rem;
right: 45%;
border-bottom: 2px solid --value(--color-*);
}
&:hover::before,
&:hover::after {
width: 55%;
}
}
- Always define pseudo-classes and pseudo-elements (
:hover
,::before
, etc.) inside the@utility
block. Tailwind does not support them directly in the utility name. --value(--color-*)
is a clever, working hack that mimicsvar(--color-primary)
based on class name.--value(integer)
is only for numeric input.- This system keeps your CSS highly composable and scalable β perfect for design systems and tokens.
Tailwind CSS v4's @utility
and --value()
system allows fine-grained, semantic, and dynamic class creation β all from pure CSS.
Whether you're building token-based design systems or want full control over generated classes, this approach gives you powerful tools without relying on JS configuration.
Happy styling! π