---
title: "@custom-variant"
description: "Define a variant with a selector, or with a block that marks the styled content with @slot."
image: "https://stillwind.dev/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://stillwind.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# @custom-variant

```css
@custom-variant name (selector);
@custom-variant name {
  selector {
    @slot;
  }
}
@custom-variant name scope(document) ...;
```

`@custom-variant` defines a variant. Write it at the top level of the
stylesheet.

## Example

The short form takes a selector in parentheses. The block form wraps the
content with any rules, and `@slot` marks where the content goes.

### Stylesheet

```css
@custom-variant theme-dark (&:where([data-theme="dark"], [data-theme="dark"] *));

@custom-variant hocus {
  &:hover,
  &:focus-visible {
    @slot;
  }
}
```

### Source

```tsx
import { style } from "stillwind/react";

export const link = style("theme-dark:text-white hocus:underline");
```

### Compiled code

```ts
export const link = {className:"sw_r7v8w"};
```

### Generated stylesheet without preflight

```css
/*! 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 {
    --color-white: #fff;
  }
}

@layer utilities {
  .sw_r7v8w:where([data-theme="dark"], [data-theme="dark"] *) {
    color: var(--color-white);
  }

  .sw_r7v8w:hover, .sw_r7v8w:focus-visible {
    text-decoration-line: underline;
  }
}
```

The block form can also contain at-rules, such as `@media` or `@supports`,
around `@slot`.

## Selectors without &

`&` refers to the element that the utility styles. As in CSS Nesting, a
selector without `&` selects descendants:
`@custom-variant expanded ([aria-expanded="true"])` means
`& [aria-expanded="true"]`.
[implicit-nesting-selector](/docs/lints/implicit-nesting-selector)
reports such a selector, and its fix writes the `&` without changing the
meaning.

## Document-level elements

`scope(document)` after the name keeps selectors without `&` as written, so the
variant can select a document-level element such as `:host` or `:root`.

### Stylesheet

```css
@custom-variant shell scope(document) (:host);
```

### Source

```tsx
import { style } from "stillwind/react";

export const css = style.css("shell:block shell:p-4 [button]:rounded-md");
```

### Compiled code

```ts
export const css = "/*! stillwind v0.0.0 | MIT License | https://stillwind.dev */\n/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n:where(:root, :host){--spacing:0.25rem;--radius-md:0.375rem;}:host{display:block;}:host{padding:calc(var(--spacing) * 4);}button{border-radius:var(--radius-md);}";
```

In `style.css()`, the first such variant selects the element that the utility
styles. In `style()`, the rule stays document-wide, and
[document-scoped-selector](/docs/lints/document-scoped-selector) reports it.

## Definitions with the same name

A later variant definition with the same name replaces the earlier one.
[duplicate-definition](/docs/lints/duplicate-definition) reports it. To match
several selectors, list them in one definition.

The order of variant definitions decides the output order of rules with
different variants. A definition in your stylesheet comes after the built-in
variants.

Source: https://stillwind.dev/docs/at-rules/custom-variant/index.mdx
