Custom utilities and variants

Tailwind CSS · Advanced · 12 min

When a pattern repeats across a codebase, a first-class utility or variant keeps markup clean and consistent.

Tailwind v4 lets you add utilities and variants that behave exactly like the built-in ones: they work with every variant, sort correctly and land in the utilities layer.

Static utilities with @utility:

@utility content-auto { content-visibility: auto; }

Now content-auto, md:content-auto and hover:content-auto all work.

Functional utilities end in -* and read the value with --value():

@utility tab-* { tab-size: --value(integer); }

makes tab-2, tab-4 and so on. --value(--tab-size-*) reads from a theme namespace instead, and --value([integer]) accepts arbitrary values like tab-[7]. Several --value() forms can be combined as fallbacks in one declaration.

Custom variants with @custom-variant:

@custom-variant theme-midnight (&:where([data-theme=midnight] *));

lets you write theme-midnight:bg-black. The block form accepts @slot for complex cases, such as wrapping in a media query.

Using variants in your own CSS: inside a regular rule, @variant dark { ... } applies Tailwind's dark variant to those declarations.

Plugins: @plugin "@tailwindcss/typography"; loads a JavaScript plugin. The official typography plugin provides prose for rich text, and the forms plugin resets form controls. A legacy v3 config can still be loaded with @config "./tailwind.config.js";.

Example

/* app.css */
@import "tailwindcss";
@plugin "@tailwindcss/typography";

@utility scrollbar-hidden {
  scrollbar-width: none;
  &::-webkit-scrollbar { display: none; }
}

@theme { --tab-size-github: 8; }
@utility tab-* {
  tab-size: --value(--tab-size-*, integer, [integer]);  /* theme, number, or [n] */
}

@custom-variant theme-midnight (&:where([data-theme=midnight], [data-theme=midnight] *));

.legacy-widget {
  background: white;
  @variant dark { background: black; }   /* reuse Tailwind's dark variant */
}

/* Markup: <pre class="tab-4 md:tab-github scrollbar-hidden">...</pre>
           <article class="prose theme-midnight:prose-invert">...</article> */

Remember

  • @utility adds utilities that work with all variants
  • Functional utilities use name-* and --value()
  • @custom-variant defines new variants; @variant uses one inside CSS
  • @plugin loads JS plugins such as typography

Watch out for

  • Writing custom utilities as plain classes in @layer utilities, where v4 no longer treats them as variant-aware utilities; use @utility.

Check yourself

  1. You define @utility tab-* { tab-size: --value(integer); }. Which class works?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: tailwindcss.com/docs/adding-custom-styles#adding-custom-utilities