Skip to content

@utility

Define a utility, or add declarations to a built-in utility with the same name.

@utility name { ... }
@utility name-* { ... }
@utility name (implementation) { ... }

@utility defines a utility for style(), @apply, and the other places that accept utilities. Write it at the top level of the stylesheet.

Static and functional utilities

A name without * defines a static utility. A name that ends in -* defines a functional utility, and --value() reads the rest of the candidate.

Stylesheet

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

@utility stack-* {
  display: flex;
  flex-direction: column;
  gap: --spacing(--value(integer));
}

Source

import { style } from "stillwind/react";

export const list = style("content-auto stack-3");

Compiled code

export const list = {className:"sw_70m3s"};

Generated stylesheet without preflight

/*! stillwind v0.0.0 | MIT License | https://stillwind.dev */
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties, theme, base, components, utilities;

@layer theme {
  :root, :host {
    --spacing: .25rem;
  }
}

@layer utilities {
  .sw_70m3s {
    content-visibility: auto;
    display: flex;
    flex-direction: column;
    gap: calc(var(--spacing) * 3);
  }
}

Values and modifiers

--value() lists the accepted forms of the value, and the first matching form decides. --modifier() reads the part after / in the same way.

Form Accepts Example candidate
--tab-size-* A token of the namespace tab-width-wide
integer A bare value of the type tab-width-2
[integer] An arbitrary value of the type tab-width-[3]

Stylesheet

@theme {
  --tab-size-wide: 8;
}

@utility tab-width-* {
  tab-size: --value(--tab-size-*, integer, [integer]);
}

@utility text-fluid-* {
  font-size: --value(--text-*);
  line-height: --modifier(--leading-*, [length]);
}

Source

import { style } from "stillwind/react";

export const code = style("tab-width-wide text-fluid-lg/tight");

Compiled code

export const code = {className:"sw_9jkuo"};

Generated stylesheet without preflight

/*! stillwind v0.0.0 | MIT License | https://stillwind.dev */
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties, theme, base, components, utilities;

@layer theme {
  :root, :host {
    --text-lg: 1.125rem;
    --leading-tight: 1.25;
    --tab-size-wide: 8;
  }
}

@layer utilities {
  .sw_9jkuo {
    tab-size: var(--tab-size-wide);
    font-size: var(--text-lg);
    line-height: var(--leading-tight);
  }
}

A declaration whose --value() matches no form is left out. A candidate that matches no declaration produces no CSS, and unknown-utility reports it.

Definitions with the same name

A definition with an existing name adds its declarations to the existing utility. It does not replace it. Every definition that accepts the candidate contributes, and a later definition wins in the cascade.

Stylesheet

@utility container {
  padding-inline: 1rem;
}

Source

import { style } from "stillwind/react";

export const page = style("container");

Compiled code

export const page = {className:"sw_5yksj"};

Generated stylesheet without preflight

/*! stillwind v0.0.0 | MIT License | https://stillwind.dev */
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties, theme, base, components, utilities;

@layer utilities {
  .sw_5yksj {
    width: 100%;
    padding-inline: 1rem;

    @media (width >= 40rem) {
      max-width: 40rem;
    }

    @media (width >= 48rem) {
      max-width: 48rem;
    }

    @media (width >= 64rem) {
      max-width: 64rem;
    }

    @media (width >= 80rem) {
      max-width: 80rem;
    }

    @media (width >= 96rem) {
      max-width: 96rem;
    }
  }
}

A static definition replaces a functional one for its exact name: @utility shadow-lg { ... } replaces the built-in shadow-* for shadow-lg only. To remove built-in definitions, use @stillwind-undef-utility.

Body

A body can contain nested rules, @apply, @keyframes, and @property. A nested selector without & selects descendants: [data-icon] means & [data-icon]. Variants select the utility element, and the nested selector applies inside them.

The compiler moves @keyframes and @property rules of a used utility to the keyframes slot, because browsers ignore them inside a style rule. An unused utility adds no such rules.

To use a registered custom property, register it with @stillwind-property and require it with @stillwind-require-properties.

The implementation descriptor

(implementation) marks the fixed declarations of the body as part of the utility’s implementation. The checkImplementation option of a lint rule decides whether the rule checks them:

Stylesheet

@utility icon-box (implementation) {
  width: 1em;
  height: 1em;
}

Source

import { style } from "stillwind/react";

export const icon = style("icon-box");

Compiled code

export const icon = {className:"sw_smxmp"};

Generated stylesheet without preflight

/*! stillwind v0.0.0 | MIT License | https://stillwind.dev */
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties, theme, base, components, utilities;

@layer utilities {
  .sw_smxmp {
    width: 1em;
    height: 1em;
  }
}

Declarations that read the candidate through --value() or --modifier() are still checked. The built-in sr-only and not-sr-only utilities also use this descriptor.

Conditions

A definition inside @media or another conditional at-rule keeps its declarations, and the compiler ignores the condition. ignored-definition-condition reports it. Write the condition inside the body instead, or use a variant.