# Stillwind > Compile component styles, enforce your design system, and ship static CSS. Index: https://stillwind.dev/llms.txt # Getting started > Add Stillwind to a Vite and React application, style a component, and check your styles. Source: https://stillwind.dev/docs · Markdown: https://stillwind.dev/docs/index.md This guide adds Stillwind to a Vite application that uses React. At the end, a button uses Stillwind styles, and the CLI checks them. For another framework or build tool, follow the same steps and use the build setup from [Framework integrations](/docs/integrations). To let a coding agent do the setup, see [Coding agents](/docs/agents). > **Early development** > > Stillwind is in early development. Test your application's build and pages before you release > changes. ## Install the packages ```sh vp i stillwind vp i -D @stillwind/integration @stillwind/cli ``` `stillwind` provides the style API. `@stillwind/integration` compiles your styles during development and builds. `@stillwind/cli` checks your styles from the command line. ## Create a stylesheet Create `src/stillwind.css`: ```css @import "stillwind"; @theme { --color-accent: oklch(0.6 0.2 250); } ``` `@import "stillwind"` loads the default Tailwind CSS v4 theme and utilities. Each token in `@theme` adds utilities. For example, `--color-accent` adds `bg-accent` and `text-accent`. ## Configure the project Create `stillwind.config.ts` in the application root: ```ts title="stillwind.config.ts" import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.{ts,tsx,vue,svelte,astro}"], }, ], }); ``` `css` selects the stylesheet. `files` selects the files that the CLI and the editor check. > **Builds ignore files** > > The build compiles every module that your application imports, whether or not `files` matches it. > The CLI and the language server (LSP) use `files` to find source files, including files that the > application does not import. Add Stillwind to `vite.config.ts`: ```ts import { stillwind } from "@stillwind/integration/vite"; import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [stillwind(), react()], }); ``` Place `stillwind()` before `react()`. Stillwind must compile styles before React compiles JSX. The plugin finds `stillwind.config.ts` in the application root. ## Style a component ```tsx import { style } from "stillwind/react"; export function SaveButton({ loading }: { loading: boolean }) { return ( ); } ``` `style()` returns props, such as `className`, so you spread the result onto the element. Each argument adds utilities. `loading && "opacity-60"` adds `opacity-60` only while `loading` is true. Start the development server. The button uses the accent color from your stylesheet. The build writes the CSS for these utilities to a static CSS file. ## Check your styles Change `bg-accent` to `bg-acent`, and run the CLI from the application root: ```sh vp exec stillwind lint ``` The CLI reports `bg-acent` as an unknown utility. The build does not report this mistake: the misspelled utility produces no CSS, and the page still renders. Run the CLI in CI to catch such mistakes before you merge them. ## Next steps - [Why Stillwind](/docs/why): what Stillwind checks and why - [The basics of style()](/docs/guides/style): variants, conditions, and reusable styles - [Design systems](/docs/guides/design-systems): how to restrict colors, spacing, and breakpoints to your theme - [Checks in the editor and CI](/docs/guides/lint-adoption): how to run the checks in CI and the editor - [Editors](/docs/editors): how to install the extension - [Formatters](/docs/formatters): how to sort utilities with your formatter ## Set up with an AI agent [Coding agents](/docs/agents) gives a prompt that sets up Stillwind or migrates an existing project. The `stillwind` package includes this documentation and skills for the agent. # Coding agents > Let a coding agent set up Stillwind or migrate a project, using the documentation and skills in the stillwind package. Source: https://stillwind.dev/docs/agents · Markdown: https://stillwind.dev/docs/agents/index.md The `stillwind` package includes this documentation as Markdown files in `node_modules/stillwind/docs/`. It also includes skills for coding agents. An agent that reads these files uses the documentation of the installed version. ## Start with a prompt Give this prompt to your agent in a new or an existing project: ```text Set up Stillwind in this project. If the stillwind package is not installed, install it with the project's package manager. Then read node_modules/stillwind/docs/agents.md and follow its instructions for agents. ``` The agent decides whether the project needs a new setup or a migration. Before it migrates existing styles, it explains the options and asks how far to go. ## What the package includes | Path | Content | | ------------------------------ | ------------------------------------------------------------ | | `docs/` | This documentation as Markdown | | `skills/setup-stillwind/` | A skill that sets up Stillwind in a project without styles | | `skills/migrate-to-stillwind/` | A skill that migrates Tailwind CSS or other CSS to Stillwind | | `skills/stillwind/` | A skill for everyday work in a project that uses Stillwind | | `AGENTS_TEMPLATE.md` | A section for the instruction file of your project | ## Instructions for agents 1. Inspect the project. Find the package manager, the build tool, the framework, and the current styling. In a monorepo, find the application that the user means. 2. Choose a skill and follow its `SKILL.md`. The skills are in `node_modules/stillwind/skills/`: - Use `setup-stillwind` when the project has no styles to migrate, such as a new project. - Use `migrate-to-stillwind` when the project uses Tailwind CSS, plain CSS, CSS Modules, a preprocessor, or CSS-in-JS. 3. Tell the user which skill you chose and why, in one sentence. For later work in a project that uses Stillwind, follow `skills/stillwind/SKILL.md`. ## Install the skills Agents can read the skills from `node_modules/stillwind/skills/` without installation. To let your agent load them automatically, link them into its skill directory, such as `.claude/skills/` for Claude Code or `.agents/skills/` for Codex: ```sh mkdir -p .claude/skills ln -s ../../node_modules/stillwind/skills/stillwind .claude/skills/stillwind ``` A link keeps the skill in sync with the installed version. Copy the directory instead when your system does not support links. ## Add project instructions Add the contents of `node_modules/stillwind/AGENTS_TEMPLATE.md` to the instruction file of your project, such as `AGENTS.md` or `CLAUDE.md`. The section tells agents in later sessions that the project uses Stillwind and where its documentation is. ## Read the documentation online Every page of this website has a **View as Markdown** link. Give your agent the [documentation index](/docs/llms.txt), or retrieve the [full documentation](/llms-full.txt) in one request. # At-rule reference > Find the at-rules that define tokens, utilities, and variants in your stylesheet, and read their syntax and output. Source: https://stillwind.dev/docs/at-rules · Markdown: https://stillwind.dev/docs/at-rules/index.md Your stylesheet defines the design system that `style()` uses. Besides ordinary CSS, it can contain at-rules that define tokens, utilities, and variants, and at-rules that control where the generated CSS goes. Some of them come from Tailwind CSS v4, and Stillwind evaluates them with its own rules. The others start with `@stillwind-`. Each page shows the syntax of an at-rule and examples. An example shows the stylesheet, the source that uses it, and the complete generated stylesheet. The generated stylesheet on these pages leaves out the preflight reset. ## Define the design system | At-rule | Use it to | Origin | | ---------------------------------------------------------------------------- | ------------------------------------------------------------- | --------- | | [@theme](/docs/at-rules/theme) | Define design tokens and shared values | Tailwind | | [@utility](/docs/at-rules/utility) | Define a utility or add declarations to an existing one | Tailwind | | [@custom-variant](/docs/at-rules/custom-variant) | Define a variant | Tailwind | | [@slot](/docs/at-rules/slot) | Mark where a variant definition inserts the styled content | Tailwind | | [@plugin](/docs/at-rules/plugin) | Load a Stillwind or Tailwind CSS v4 plugin | Tailwind | | [@stillwind-property](/docs/at-rules/stillwind-property) | Register a custom property that utilities need | Stillwind | | [@stillwind-require-properties](/docs/at-rules/stillwind-require-properties) | Declare the registered custom properties that a utility needs | Stillwind | ## Use utilities in your own rules | At-rule | Use it to | Origin | | ---------------------------------- | ----------------------------------------------------- | -------- | | [@apply](/docs/at-rules/apply) | Insert the declarations of utilities into a rule | Tailwind | | [@variant](/docs/at-rules/variant) | Apply variants, such as `hover` or `dark`, to a block | Tailwind | ## Place the generated CSS | At-rule | Use it to | Origin | | ---------------------------------------------------------------- | ------------------------------------------------------ | --------- | | [@stillwind-slot](/docs/at-rules/stillwind-slot) | Place generated utilities, theme values, and other CSS | Stillwind | | [@stillwind-css-template](/docs/at-rules/stillwind-css-template) | Define the output of `style.css()` | Stillwind | `@import "stillwind"` already places every slot. Use these at-rules when you write your own entry stylesheet or change the output of `style.css()`. ## Remove or limit definitions | At-rule | Use it to | Origin | | ---------------------------------------------------------------------------------------- | -------------------------------------------------------- | --------- | | [@stillwind-undef-utility](/docs/at-rules/stillwind-undef-utility) | Remove built-in or plugin utilities | Stillwind | | [@stillwind-undef-variant](/docs/at-rules/stillwind-undef-variant) | Remove built-in or plugin variants | Stillwind | | [@stillwind-undef-arbitrary-property](/docs/at-rules/stillwind-undef-arbitrary-property) | Reject arbitrary properties such as `[color:red]` | Stillwind | | [@stillwind-registry](/docs/at-rules/stillwind-registry) | Change definitions only in keyframes or view transitions | Stillwind | ## Write a utility library Stillwind's built-in utilities and variants use [registry directives](/docs/at-rules/registry-directives), such as `@stillwind-utility` and `@stillwind-theme-namespace`. Read them when you write a utility library or read the built-in definitions. Application stylesheets normally do not need them. ## Tailwind CSS features that Stillwind ignores Stillwind reads its settings from `stillwind.config.ts` and generates utilities only from `style()` calls. It ignores `@config`, `@source`, `@reference`, the `important`, `source`, and `prefix` modifiers of `@import`, and the `prefix()` option of `@theme`. [ignored-tailwind-feature](/docs/lints/ignored-tailwind-feature) reports them. When the loader cannot use a Stillwind at-rule, it skips the statement and compiles the rest of the stylesheet. [invalid-css-directive](/docs/lints/invalid-css-directive) reports the skipped statement. # @apply > Insert the declarations of utilities, including their variants, into a rule of your stylesheet. Source: https://stillwind.dev/docs/at-rules/apply · Markdown: https://stillwind.dev/docs/at-rules/apply/index.md ```css selector { @apply utilities; } ``` `@apply` replaces itself with the declarations of the listed utilities. Write it inside a style rule or a utility definition. Whitespace separates the utilities. ## Example ### Stylesheet ```css .button { @apply rounded-md px-4 py-2 hover:bg-blue-600 focus-visible:outline-2; } ``` ### 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-blue-600: oklch(54.6% .245 262.881); --spacing: .25rem; --radius-md: .375rem; } } @layer properties { @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { *, :before, :after, ::backdrop { --tw-outline-style: solid; } } } @property --tw-outline-style { syntax: "*"; inherits: false; initial-value: solid; } .button { padding-inline: calc(var(--spacing) * 4); padding-block: calc(var(--spacing) * 2); border-radius: var(--radius-md); &:hover { @media (hover: hover) { background-color: var(--color-blue-600); } } &:focus-visible { outline-style: var(--tw-outline-style); outline-width: 2px; } } ``` A utility with variants becomes nested rules, and the selector of the enclosing rule takes the place of the generated class. Utilities without variants come first in utility order. Utilities with variants follow in variant order. The stylesheet declares the theme values and registered properties that the applied utilities need, as it does for `style()`. ## Rules - A utility that does not resolve adds nothing. `.button { @apply unknown-x; }` becomes `.button {}`. - An applied utility can apply other utilities. A circular application is a compilation error. - In a utility definition, `@apply` expands after the variants of the utility apply, so the applied declarations stay under them. - `@apply` adds no class to any element. `@apply group` does not make the element a `group` for `group-hover:`. To apply a condition to your own declarations, use [@variant](/docs/at-rules/variant) instead. # @custom-variant > Define a variant with a selector, or with a block that marks the styled content with @slot. Source: https://stillwind.dev/docs/at-rules/custom-variant · Markdown: https://stillwind.dev/docs/at-rules/custom-variant/index.md ```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. # @plugin > Load a Stillwind plugin or a Tailwind CSS v4 JavaScript plugin, with optional options. Source: https://stillwind.dev/docs/at-rules/plugin · Markdown: https://stillwind.dev/docs/at-rules/plugin/index.md ```css @plugin "name"; @plugin "name" { option: value; } ``` `@plugin` loads JavaScript that generates CSS definitions, and the generated CSS replaces the statement. Write it at the top level of the stylesheet. ## Example ```css @import "stillwind"; @plugin "@stillwind/plugin-iconify"; ``` [`@stillwind/plugin-iconify`](/docs/plugins/iconify) adds icon utilities such as `icon-mdi--close`. Only the icons that your modules use reach the stylesheet. ## Finding the plugin The host looks up `name` in the `plugins` field of the project in `stillwind.config.ts` first. Otherwise, it imports the package or the local file from the stylesheet and uses its default export. See [Configuration](/docs/configuration). ## Options The block contains only `name: value;` declarations. Strings keep their quotes, so a `;` inside a string does not end a declaration. The loader rejects other statements and empty values. ## Tailwind CSS plugins Tailwind CSS v4 JavaScript plugins use the same statement, including plugins that `plugin.withOptions()` creates. The host supports `addBase`, `addUtilities`, `addComponents`, `matchUtilities`, `matchComponents`, `addVariant`, `matchVariant`, `theme`, `config`, and `prefix`. `prefix` returns the class name unchanged, because Stillwind has no class prefix. These plugins need the `tailwindcss` package. The options block converts values as Tailwind does. `true`, `false`, `null`, and numeric text become those values. A quoted string loses its quotes, other text stays a string, and a comma-separated list becomes an array. Definitions from a plugin register before the definitions of your stylesheet, wherever the `@plugin` statement stands. A `@utility` in your stylesheet therefore adds to a plugin utility with the same name, and [@stillwind-undef-utility](/docs/at-rules/stillwind-undef-utility) can remove plugin utilities. A plugin can select elements by fixed class names, such as `not-prose`. `style()` keeps these class names, as [Tailwind CSS compatibility](/docs/guides/tailwind-compatibility#class-names-that-a-plugins-selectors-use) describes. That page also lists the tested plugins. A CSS package does not use `@plugin`. Load it with `@import`, such as `@import "tw-animate-css";`. [invalid-css-import](/docs/lints/invalid-css-import) reports a plugin that the host cannot find or load, and invalid options. The stylesheet loads without that plugin. # Registry directives > Read the at-rules that define Stillwind's built-in utilities, variants, and theme metadata. Source: https://stillwind.dev/docs/at-rules/registry-directives · Markdown: https://stillwind.dev/docs/at-rules/registry-directives/index.md Stillwind defines its built-in utilities and variants in CSS, in the files that `@import "stillwind"` loads. These files use registry directives, which describe cascade order, theme metadata, and other information that ordinary definitions cannot express. Use them when you write a utility library. An application stylesheet normally uses [@utility](/docs/at-rules/utility) and [@custom-variant](/docs/at-rules/custom-variant) instead. Editors suggest these directives inside definitions and registry blocks. The [built-in definitions](https://github.com/SegaraRai/stillwind/tree/main/packages/core/css) show complete examples. ## Definitions ### @stillwind-utility ```css @stillwind-utility name-* (tw-sort: property) { ... } ``` Defines a utility with the same body language as `@utility`. The position of the definition decides its cascade order: a later definition wins over an earlier one when both set the same property. The optional `tw-sort` descriptor names the property that Tailwind-compatible class sorting uses for the definition. The header also accepts `implementation`. All `@stillwind-utility` definitions of a name register before plugin definitions and the `@utility` definitions of the stylesheet. ### @stillwind-variant ```css @stillwind-variant name (descriptors) { ... } ``` Defines a variant with the body language of `@custom-variant`. Its position decides the output order of variant rules. The optional header is a comma-separated list: | Descriptor | Value | | ----------- | ----------------------------------------------------------------------------------------- | | `value` | The accepted value type of a `*` name, such as `[supports-condition]` | | `modifier` | The accepted modifier type | | `scope` | `document`, which keeps selectors without `&` as written | | `sort` | A theme namespace, such as `--breakpoint-*`, whose token values order the variants | | `direction` | `asc` or `desc`, the order of the `sort` values | | `group` | A group name. Variants of a group are ordered together, at the position of the first one. | | `bare` | `true` when the variant also matches a bare token name, such as `lg` for `min-lg` | ```css @stillwind-variant min-* (sort: --breakpoint-*, direction: asc, bare: true) { @media (width >= --value(--breakpoint-*)) { @slot; } } ``` ### @stillwind-intrinsic ```css @stillwind-intrinsic name; ``` Replaces the body of a `@stillwind-variant` with a behavior that the compiler implements, such as `not-*`. The names are internal to Stillwind. ### @stillwind-arbitrary-property ```css @stillwind-arbitrary-property pattern; ``` Places arbitrary-property candidates for the matching properties, such as `[color:red]`, at this position in the cascade order of utilities. The built-in definitions end with `@stillwind-arbitrary-property *;`, so other arbitrary properties come after every built-in utility. The statement also accepts the matching properties again after [@stillwind-undef-arbitrary-property](/docs/at-rules/stillwind-undef-arbitrary-property). ## Theme metadata ### @stillwind-theme-namespace ```css @stillwind-theme-namespace --spacing-* [length] scale(px: 1px); ``` Declares a theme namespace. The bracketed types limit the arbitrary values and typed variables that the namespace accepts. [incompatible-theme-value](/docs/lints/incompatible-theme-value) uses them to check tokens. `scale()` makes the token with the namespace name, such as `--spacing`, the scale of the namespace. A numeric key then resolves to a multiple of the scale, and `--spacing(4)` lowers to the same multiple. Each argument of `scale()` names a key with a fixed value, such as `px` for `1px`. ### @stillwind-theme-exclude ```css @stillwind-theme-exclude --text-* --text-shadow-*; ``` Makes a `--value(--text-*)` pattern skip the tokens of the narrower namespaces that follow the first one. Without it, `--text-shadow-lg` would be a value of `--text-*`. ### @stillwind-theme-order ```css @stillwind-theme-order spacing color; ``` Sets the order of namespaces in the declarations that the `theme` slot emits. ### @stillwind-omit-token-prefix ```css @stillwind-omit-token-prefix --shadow- --tw-shadow:; ``` Leaves out the tokens with the prefix when the generated CSS contains the condition text. ## Values and dependencies ### @stillwind-format ```css @stillwind-format transform compose-space; @stillwind-format --tw-translate-* zero-length; ``` Declares how the compiler treats the values of the matching properties. `compose-space` marks a property whose declarations compose space-separated parts. `zero-length` gives a zero value the length type that `calc()` needs. ## Classes and relationships ### @stillwind-marker ```css @stillwind-marker group (modifiable); ``` Declares a marker class: a class that a candidate adds to the element as written, so that selectors such as `.group:hover &` can match it. `modifiable` also accepts a named modifier, so `group/menu` adds the class `group/menu`. The classes that the selectors of `@utility` and `@custom-variant` definitions name are also marker classes. [invalid-marker-use](/docs/lints/invalid-marker-use) reports a candidate that names a marker class but cannot add it, such as `hover:group`. ### @stillwind-marker-source ```css @stillwind-marker-source ".card .title {}"; ``` Makes the classes that the rules in the string select marker classes, as a definition body does. It defines and emits nothing. Plugins use it for classes that their dynamic utilities use. ### @stillwind-relationship ```css @stillwind-relationship group-*; ``` Registers a pattern of utility or variant names that relate an element to another element, such as `group-*` and `peer-*`. [prefer-typed-relationships](/docs/lints/prefer-typed-relationships) reports candidates that match a pattern, because [style.ref()](/docs/macros/ref) can express the same relationship. The patterns do not change the generated CSS. # @slot > Mark where a variant definition inserts the styled content. Source: https://stillwind.dev/docs/at-rules/slot · Markdown: https://stillwind.dev/docs/at-rules/slot/index.md ```css @slot; ``` `@slot` marks where the block form of a variant definition inserts the declarations of the utility. Write it in the body of [@custom-variant](/docs/at-rules/custom-variant), or of a registry variant definition. ### Stylesheet ```css @custom-variant hocus { &:hover, &:focus-visible { @slot; } } ``` ### Source ```tsx import { style } from "stillwind/react"; export const link = style("hocus:underline"); ``` ### Compiled code ```ts export const link = {className:"sw_4x5c3"}; ``` ### 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 utilities { .sw_4x5c3:hover, .sw_4x5c3:focus-visible { text-decoration-line: underline; } } ``` `@slot` belongs to variant definitions only. [@stillwind-slot](/docs/at-rules/stillwind-slot) is a different at-rule: it places generated CSS in your stylesheet. # @stillwind-css-template > Define where style.css() places its utilities, theme values, and other supporting CSS. Source: https://stillwind.dev/docs/at-rules/stillwind-css-template · Markdown: https://stillwind.dev/docs/at-rules/stillwind-css-template/index.md ```css @stillwind-css-template name { ... @stillwind-slot utilities; } ``` `@stillwind-css-template` defines a template for the string that [style.css()](/docs/macros/css) returns. The `template` option of the call selects the template by name, and `default` applies when the call names none. Write it at the top level of the stylesheet. ## Example ### Stylesheet ```css @stillwind-css-template layered { @layer component { :host { @stillwind-slot theme; } @stillwind-slot utilities; } } ``` ### Source ```tsx import { style } from "stillwind/react"; export const css = style.css("p-4 text-blue-600", { selector: ":host", template: "layered", }); ``` ### 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@layer component{:host{--color-blue-600:oklch(54.6% 0.245 262.881);--spacing:0.25rem;}:host{padding:calc(var(--spacing) * 4);}:host{color:var(--color-blue-600);}}"; ``` The string contains the template's own CSS, and each slot receives the output of the call. The string never contains the preflight reset or the other rules of your stylesheet. ## Leave out supporting CSS A slot that the template omits drops its output. This template places only the utilities, so the string uses `--spacing` and `--color-blue-600` without declaring them. The page or the shadow root must declare them in another way. ### Stylesheet ```css @stillwind-css-template bare { @stillwind-slot utilities; } ``` ### Source ```tsx import { style } from "stillwind/react"; export const css = style.css("p-4 text-blue-600", { selector: ":host", template: "bare", }); ``` ### 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:host{padding:calc(var(--spacing) * 4);}:host{color:var(--color-blue-600);}"; ``` ## The default template `@import "stillwind"` defines `default`: ```css @stillwind-css-template default { :where(:root, :host) { @stillwind-slot theme; @stillwind-slot property-initials inherited; } :where(*, :host), ::before, ::after, ::backdrop { @stillwind-slot property-initials non-inherited; } @stillwind-slot keyframes; @stillwind-slot utilities; } ``` It places the theme values on the root of the tree that adopts the string. It emits no `@property` rules, because a registration inside a shadow root has no effect. No rule is in a layer. A later template with the same name replaces an earlier one, so your stylesheet can redefine `default` after `@import "stillwind"`. ## Rules - A template must contain a `utilities` slot. - A template cannot contain definitions, such as `@theme` or `@utility`. - The loader skips a template that breaks these rules, and [invalid-css-directive](/docs/lints/invalid-css-directive) reports it. - A template name that the stylesheet does not define reports [invalid-css-input](/docs/lints/invalid-css-input) at the call. The call then uses no template, and its string contains only the rules. # @stillwind-property > Register a custom property that the stylesheet declares with @property only when generated CSS needs it. Source: https://stillwind.dev/docs/at-rules/stillwind-property · Markdown: https://stillwind.dev/docs/at-rules/stillwind-property/index.md ```css @stillwind-property --name { syntax: ""; inherits: false; initial-value: 0deg; } ``` `@stillwind-property` registers a custom property. The compiler emits a standard `@property` rule, and an initial value for each element, only when the generated CSS needs the property. Write it at the top level of the stylesheet. ## Example ### Stylesheet ```css @stillwind-property --spin-angle { syntax: ""; inherits: false; initial-value: 0deg; } @utility spin-* { --spin-angle: --value([angle]); rotate: var(--spin-angle); } ``` ### Source ```tsx import { style } from "stillwind/react"; export const icon = style("spin-[45deg]"); ``` ### Compiled code ```ts export const icon = {className:"sw_7ic77"}; ``` ### 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 utilities { .sw_7ic77 { --spin-angle: 45deg; rotate: var(--spin-angle); } } @layer properties { @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { *, :before, :after, ::backdrop { --spin-angle: 0deg; } } } @property --spin-angle { syntax: ""; inherits: false; initial-value: 0deg; } ``` The `properties` slot places the `@property` rule. The `property-initials` slot places the initial values, for browsers that ignore `@property`. See [@stillwind-slot](/docs/at-rules/stillwind-slot). ## Descriptors The standard `@property` descriptors, `syntax`, `inherits`, and `initial-value`, go into the emitted rule as written. The compiler reads the other descriptors and removes them from the output: | Descriptor | Value | | ------------- | ------------------------------------------------------------------------------------------ | | `activation` | When to emit: `referenced` (default), `declared`, `either`, or `dependency` | | `depends-on` | Space-separated custom properties that are emitted with this one | | `group` | Space-separated group names. The registrations of a group are emitted together. | | `activate-on` | Space-separated ordinary properties. A declaration of one of them emits this registration. | | `value-for` | Comma-separated ordinary properties whose value rules check the values of this property | `referenced` emits the registration when a rule reads the property through `var()`. `declared` emits it when a rule sets the property. `either` emits it in both cases. `dependency` emits it only through `depends-on` or `group`. A utility can also require a registration with [@stillwind-require-properties](/docs/at-rules/stillwind-require-properties). ## Value checks `value-for` makes lint rules check the value of the custom property as a value of the listed properties: ```css @stillwind-property --box-size { syntax: "*"; inherits: false; value-for: width, height; } @utility box-* { --box-size: --value([length]); } ``` [no-arbitrary-spacing](/docs/lints/no-arbitrary-spacing), [no-arbitrary-colors](/docs/lints/no-arbitrary-colors), and [no-restricted-length-units](/docs/lints/no-restricted-length-units) check `box-[13px]` as a `width` and a `height` value. Each listed property uses its own rule settings. ## Registrations with the same name The last registration of a name decides. A registration in your stylesheet replaces the registration of a package. An ordinary `@property` rule in your stylesheet stays as written, and the compiler does not emit a registration with the same name. # @stillwind-registry > Define or remove utilities only for style(), keyframes, animation options, or view transitions. Source: https://stillwind.dev/docs/at-rules/stillwind-registry · Markdown: https://stillwind.dev/docs/at-rules/stillwind-registry/index.md ```css @stillwind-registry default, keyframe, animation, view-transition { ... } ``` Each place where utilities are written resolves them with its own set of definitions, called a registry. `@stillwind-registry` selects the registries that the definitions inside the block change. Write it at the top level of the stylesheet, or inside another `@stillwind-registry` block. | Registry | Resolves the utilities of | | ----------------- | ----------------------------------------------------------------------------- | | `default` | `style()`, `style.cva()`, `style.css()`, and `@apply` | | `keyframe` | The frames of `style.keyframes()` | | `animation` | The options of a keyframes use, such as `enter("both")` | | `view-transition` | The `group`, `old`, `new`, and `imagePair` fields of `style.viewTransition()` | A definition outside every block changes `default`, `keyframe`, and `view-transition`. `animation` starts empty and changes only through blocks that name it. ## Example The built-in `keyframe` and `view-transition` registries replace `duration-*` with definitions that set animation properties. A top-level `@utility` with a name that they replace also reaches those registries, and [bypassed-undefinition](/docs/lints/bypassed-undefinition) reports it. A `default` block keeps the definition out of them: ### Stylesheet ```css @stillwind-registry default { @utility duration-slow { transition-duration: 600ms; } } ``` ### Source ```tsx import { style } from "stillwind/react"; export const panel = style("transition duration-slow"); ``` ### Compiled code ```ts export const panel = {className:"sw_2wahw"}; ``` ### 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 { --default-transition-duration: .15s; --default-transition-timing-function: cubic-bezier(.4, 0, .2, 1); } } @layer utilities { .sw_2wahw { transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); transition-duration: var(--tw-duration, var(--default-transition-duration)); transition-duration: .6s; } } ``` A block that names `animation` and `view-transition` defines the utility for animations instead: ```css @stillwind-registry animation, view-transition { @utility duration-slow { animation-duration: 600ms; } } ``` ## Contents A block contains only these statements. Another statement is an error. - `@utility`, `@custom-variant`, and `@variant` definitions - [@stillwind-undef-utility](/docs/at-rules/stillwind-undef-utility), [@stillwind-undef-variant](/docs/at-rules/stillwind-undef-variant), and [@stillwind-undef-arbitrary-property](/docs/at-rules/stillwind-undef-arbitrary-property) - `@stillwind-arbitrary-property`, `@stillwind-utility`, and `@stillwind-variant` from the [registry directives](/docs/at-rules/registry-directives) - A nested `@stillwind-registry` block. The innermost block decides. The header names one or more registries, separated by commas. An unknown or missing name is an error, and [invalid-css-directive](/docs/lints/invalid-css-directive) reports it. [Animations](/docs/guides/animations) and [View transitions](/docs/guides/view-transitions) show how utilities such as `duration-300` set animation properties in those registries. # @stillwind-require-properties > Make a utility emit the registrations of custom properties that it needs. Source: https://stillwind.dev/docs/at-rules/stillwind-require-properties · Markdown: https://stillwind.dev/docs/at-rules/stillwind-require-properties/index.md ```css @utility name { @stillwind-require-properties --name --other; } ``` `@stillwind-require-properties` adds registrations to the requirements of a utility. When a module uses the utility, the stylesheet emits each listed registration. Write it in the body of a utility definition. Separate several names with spaces. ## Example `spin-*` only sets `--spin-angle`, and another rule reads it. Without the requirement, the stylesheet would not emit the registration, because the default activation waits for a `var()` reference. ### Stylesheet ```css @stillwind-property --spin-angle { syntax: ""; inherits: false; initial-value: 0deg; } @utility spin-* { @stillwind-require-properties --spin-angle; --spin-angle: --value([angle]); } ``` ### Source ```tsx import { style } from "stillwind/react"; export const icon = style("spin-[45deg]"); ``` ### Compiled code ```ts export const icon = {className:"sw_7ic77"}; ``` ### 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 utilities { .sw_7ic77 { --spin-angle: 45deg; } } @layer properties { @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { *, :before, :after, ::backdrop { --spin-angle: 0deg; } } } @property --spin-angle { syntax: ""; inherits: false; initial-value: 0deg; } ``` ## Errors Each name must be a complete custom property name that starts with `--`. The compiler reports an invalid name as [SW14003](/docs/errors/SW14003) in your stylesheet. When a module uses a utility with an invalid name, the compiler reports [SW14004](/docs/errors/SW14004), including for plugin utilities. A required name needs a [@stillwind-property](/docs/at-rules/stillwind-property) registration in the loaded stylesheets. A missing registration is [SW14005](/docs/errors/SW14005). # @stillwind-slot > Place generated utilities, theme values, keyframes, and property registrations in your stylesheet. Source: https://stillwind.dev/docs/at-rules/stillwind-slot · Markdown: https://stillwind.dev/docs/at-rules/stillwind-slot/index.md ```css @stillwind-slot name; ``` `@stillwind-slot` marks where the compiler writes one kind of generated CSS. `@import "stillwind"` already places every slot, so you need this at-rule only when you write your own entry stylesheet or a [CSS template](/docs/at-rules/stillwind-css-template). ## Slots These slots insert complete rules. Write them outside style rules: | Slot | Inserts | | ------------ | --------------------------------------------------- | | `utilities` | The rules of the utilities that modules use | | `keyframes` | The `@keyframes` rules that the used utilities need | | `properties` | The `@property` rules of the needed registrations | These slots insert declarations. Write them inside a style rule, which supplies the selector: | Slot | Inserts | | --------------------------------- | -------------------------------------------------------------------------- | | `theme` | The theme values that the generated CSS uses, and `@theme static` values | | `property-initials` | The initial values of the needed registrations | | `property-initials inherited` | The initial values of the needed registrations with `inherits: true` only | | `property-initials non-inherited` | The initial values of the needed registrations with `inherits: false` only | ## Example `@import "stillwind"` places the slots like this, in simplified form: ```css @layer properties, theme, base, components, utilities; @layer theme { :root, :host { @stillwind-slot theme; } @stillwind-slot keyframes; } @layer utilities { @stillwind-slot utilities; } @layer properties { *, ::before, ::after, ::backdrop { @stillwind-slot property-initials; } } @stillwind-slot properties; ``` The position of a slot decides the cascade order of its output. An enclosing `@layer` or conditional at-rule also applies to the output. Moving `utilities` before your own rules, or into another layer, can change which declarations win. ## Rules - A slot with an unknown name, or in a position that cannot contain its output, is skipped. [invalid-css-directive](/docs/lints/invalid-css-directive) reports it. - A stylesheet without a `utilities` slot is [SW14001](/docs/errors/SW14001). A missing slot for other generated CSS that a module needs, such as `keyframes`, is [SW14002](/docs/errors/SW14002). - A repeated slot repeats its output at every position. `@stillwind-slot` places generated CSS. [@slot](/docs/at-rules/slot) is a different at-rule for variant definitions. # @stillwind-undef-arbitrary-property > Reject arbitrary-property utilities such as [color:red] for the properties that match a pattern. Source: https://stillwind.dev/docs/at-rules/stillwind-undef-arbitrary-property · Markdown: https://stillwind.dev/docs/at-rules/stillwind-undef-arbitrary-property/index.md ```css @stillwind-undef-arbitrary-property property; @stillwind-undef-arbitrary-property prefix-*; @stillwind-undef-arbitrary-property *; ``` An arbitrary property is a utility that writes a declaration directly, such as `[color:red]`. `@stillwind-undef-arbitrary-property` rejects arbitrary properties for the matching CSS properties. A rejected candidate produces no CSS. Write the statement at the top level of the stylesheet or in a [@stillwind-registry](/docs/at-rules/stillwind-registry) block. ## Example ### Stylesheet ```css @stillwind-undef-arbitrary-property *; @stillwind-arbitrary-property --*; ``` ### Source ```tsx import { style } from "stillwind/react"; export const rejected = style("[color:red]"); export const accepted = style("[--gutter:1rem]"); ``` ### Compiled code ```ts export const rejected = {}; export const accepted = {className:"sw_obysi"}; ``` ### 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 utilities { .sw_obysi { --gutter: 1rem; } } ``` The first statement rejects every property. The second statement accepts custom properties again, so components can still set variables. ## Rules - The last matching statement decides for each property. A property that no statement matches is accepted. - `@stillwind-arbitrary-property pattern;` accepts the matching properties again. The [registry directives](/docs/at-rules/registry-directives) describe its other role. - Patterns use the same forms as [@stillwind-undef-utility](/docs/at-rules/stillwind-undef-utility), with property names instead of utility names. To report arbitrary values without rejecting them, use lint rules such as [no-arbitrary-colors](/docs/lints/no-arbitrary-colors) or [no-restricted-declarations](/docs/lints/no-restricted-declarations) instead. # @stillwind-undef-utility > Remove built-in or plugin utility definitions that match a name or a prefix. Source: https://stillwind.dev/docs/at-rules/stillwind-undef-utility · Markdown: https://stillwind.dev/docs/at-rules/stillwind-undef-utility/index.md ```css @stillwind-undef-utility name; @stillwind-undef-utility prefix-*; @stillwind-undef-utility *; ``` `@stillwind-undef-utility` removes the matching utility definitions that register before it. A removed utility becomes unknown, and [unknown-utility](/docs/lints/unknown-utility) reports its uses. Write it at the top level of the stylesheet or in a [@stillwind-registry](/docs/at-rules/stillwind-registry) block. ## Example ### Stylesheet ```css @stillwind-undef-utility float-*; @utility float-start { float: inline-start; } ``` ### Source ```tsx import { style } from "stillwind/react"; export const removed = style("float-left"); export const replaced = style("float-start"); ``` ### Compiled code ```ts export const removed = {}; export const replaced = {className:"sw_vofby"}; ``` ### 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 utilities { .sw_vofby { float: inline-start; } } ``` The statement removes every built-in `float-*` utility. `float-left` produces no class, and `float-start` uses only the new definition. ## Patterns A pattern is `*`, an exact name, or a prefix followed by `*`. `duration-*` matches the definition `duration-*` and the exact names that start with `duration-`, such as `duration-initial`. It does not match `-duration-*`: a pattern for negative names starts with `-`. ## Order The statements that `@plugin` generates register first. The other statements, including the built-in definitions that `@import "stillwind"` loads, follow in source order. An undefinition removes only definitions that register before it: - It removes plugin utilities, wherever the `@plugin` statement stands. - It removes the built-in utilities that `@import "stillwind"` loads before it. - It does not remove a `@utility` that comes after it, so you can remove a built-in utility and define your own utility with the same name. A replacement with the same name keeps the cascade position of the removed definition, so it wins against the same utilities as before. A definition with another name takes the position where you write it. A definition without `*` does not take the position of a removed `prefix-*` definition. The [logical stylesheets](/docs/concepts/stylesheets#logical-axis-utilities) replace utilities in this way. Outside a block, the statement changes the `default`, `keyframe`, and `view-transition` registries. To report uses of a utility without removing it, use [no-restricted-utilities](/docs/lints/no-restricted-utilities) instead. # @stillwind-undef-variant > Remove built-in or plugin variant definitions that match a name or a prefix. Source: https://stillwind.dev/docs/at-rules/stillwind-undef-variant · Markdown: https://stillwind.dev/docs/at-rules/stillwind-undef-variant/index.md ```css @stillwind-undef-variant name; @stillwind-undef-variant prefix-*; @stillwind-undef-variant *; ``` `@stillwind-undef-variant` removes the matching variant definitions that register before it. A removed variant becomes unknown, and [unknown-variant](/docs/lints/unknown-variant) reports its uses. Write it at the top level of the stylesheet or in a [@stillwind-registry](/docs/at-rules/stillwind-registry) block. ## Example ### Stylesheet ```css @stillwind-undef-variant print; ``` ### Source ```tsx import { style } from "stillwind/react"; export const toolbar = style("print:hidden"); ``` ### Compiled code ```ts export const toolbar = {}; ``` ### 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; ``` `print:hidden` produces no class, because `print` is unknown. Patterns and registration order work as in [@stillwind-undef-utility](/docs/at-rules/stillwind-undef-utility). A [@custom-variant](/docs/at-rules/custom-variant) after the statement can define a variant with the removed name again. # @theme > Define design tokens that add utilities, and shared values such as images. Source: https://stillwind.dev/docs/at-rules/theme · Markdown: https://stillwind.dev/docs/at-rules/theme/index.md ```css @theme [default] [asset] [inline | static] { --name: value; } ``` `@theme` defines design tokens as custom properties. A token in a known namespace adds utilities: `--color-brand` adds `bg-brand`, `text-brand`, and the other color utilities. Write `@theme` at the top level of the stylesheet. ## Example ### Stylesheet ```css @theme { --color-brand: oklch(0.6 0.2 250); --color-brand-muted: oklch(0.8 0.08 250); --radius-card: 0.75rem; } ``` ### Source ```tsx import { style } from "stillwind/react"; export const card = style("rounded-card bg-brand"); ``` ### Compiled code ```ts export const card = {className:"sw_j7rat"}; ``` ### 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-brand: oklch(60% .2 250); --radius-card: .75rem; } } @layer utilities { .sw_j7rat { background-color: var(--color-brand); border-radius: var(--radius-card); } } ``` The generated stylesheet declares only the tokens that the used utilities reference. `--color-brand-muted` is not used, so the stylesheet leaves it out. ## Options | Option | Effect | | --------- | ----------------------------------------------------------------------------------- | | `default` | Marks the tokens as defaults. A token without `default` replaces them in any order. | | `asset` | Declares shared values, such as images, that are not design tokens | | `inline` | Replaces each `var()` reference to a token with the token's value | | `static` | Declares the tokens even when no used utility references them | `inline` and `static` cannot be combined. The other options can be combined freely. ### Defaults ### Stylesheet ```css @theme { --color-brand: red; } @theme default { --color-brand: blue; --color-brand-muted: lightblue; } ``` ### Source ```tsx import { style } from "stillwind/react"; export const label = style("bg-brand text-brand-muted"); ``` ### Compiled code ```ts export const label = {className:"sw_4zrwj"}; ``` ### 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-brand: red; --color-brand-muted: lightblue; } } @layer utilities { .sw_4zrwj { color: var(--color-brand-muted); background-color: var(--color-brand); } } ``` `--color-brand` without `default` wins although it comes first. The default theme of Stillwind uses `default`, so your tokens replace its values. ### Inline and static values ### Stylesheet ```css @theme inline { --font-display: "Inter Display", sans-serif; } @theme static { --color-highlight: #fde047; } ``` ### Source ```tsx import { style } from "stillwind/react"; export const title = style("font-display"); ``` ### Compiled code ```ts export const title = {className:"sw_mjcfr"}; ``` ### 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-highlight: #fde047; } } @layer utilities { .sw_mjcfr { font-family: Inter Display, sans-serif; } } ``` `font-display` contains the value of `--font-display` instead of a `var()` reference, and the stylesheet does not declare `--font-display`. The stylesheet declares `--color-highlight`, although no utility uses it, so your own CSS can reference it. ### Assets ### Stylesheet ```css @theme asset { --image-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E"); } @utility checkmark { mask-image: var(--image-check); } ``` ### Source ```tsx import { style } from "stillwind/react"; export const done = style("checkmark"); ``` ### Compiled code ```ts export const done = {className:"sw_j94hj"}; ``` ### 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 { --image-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E"); } } @layer utilities { .sw_j94hj { mask-image: var(--image-check); } } ``` An asset is declared when a used rule references it, like a token. An asset adds no utilities. Token restrictions, `--value()` patterns, `theme()`, and `--theme()` do not see assets. ## Remove tokens Set a namespace to `initial` to remove its earlier tokens. `--*: initial` removes every token. ### Stylesheet ```css @theme { --color-*: initial; --color-ink: #1f2937; --color-paper: #ffffff; } ``` ### Source ```tsx import { style } from "stillwind/react"; export const page = style("bg-paper text-ink"); ``` ### Compiled code ```ts export const page = {className:"sw_rjnyv"}; ``` ### 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-ink: #1f2937; --color-paper: #fff; } } @layer utilities { .sw_rjnyv { color: var(--color-ink); background-color: var(--color-paper); } } ``` After this block, palette utilities such as `bg-red-500` are unknown. A reset affects only tokens of the same kind: `@theme { --*: initial; }` keeps assets, and `@theme asset { --*: initial; }` keeps design tokens. ## Placement The `theme` slot places the declarations. `@import "stillwind"` puts it on `:root, :host`. See [@stillwind-slot](/docs/at-rules/stillwind-slot). [incompatible-theme-value](/docs/lints/incompatible-theme-value) reports a token with a value of the wrong type for its namespace, such as a length in `--color-*`. [Design systems](/docs/guides/design-systems) shows how to name tokens by role and report values outside them. # @utility > Define a utility, or add declarations to a built-in utility with the same name. Source: https://stillwind.dev/docs/at-rules/utility · Markdown: https://stillwind.dev/docs/at-rules/utility/index.md ```css @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 ```css @utility content-auto { content-visibility: auto; } @utility stack-* { display: flex; flex-direction: column; gap: --spacing(--value(integer)); } ``` ### Source ```tsx import { style } from "stillwind/react"; export const list = style("content-auto stack-3"); ``` ### Compiled code ```ts export const list = {className:"sw_70m3s"}; ``` ### 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 { --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 ```css @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 ```tsx import { style } from "stillwind/react"; export const code = style("tab-width-wide text-fluid-lg/tight"); ``` ### Compiled code ```ts export const code = {className:"sw_9jkuo"}; ``` ### 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 { --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](/docs/lints/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 ```css @utility container { padding-inline: 1rem; } ``` ### Source ```tsx import { style } from "stillwind/react"; export const page = style("container"); ``` ### Compiled code ```ts export const page = {className:"sw_5yksj"}; ``` ### 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 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](/docs/at-rules/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](/docs/at-rules/stillwind-property) and require it with [@stillwind-require-properties](/docs/at-rules/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: - [prefer-logical-properties](/docs/lints/prefer-logical-properties) and [no-restricted-length-units](/docs/lints/no-restricted-length-units) skip them unless the option is `true`. The `em` values below are skipped. - [no-important](/docs/lints/no-important), [no-restricted-declarations](/docs/lints/no-restricted-declarations), and [no-restricted-theme-tokens](/docs/lints/no-restricted-theme-tokens) check them unless the option is `false`. ### Stylesheet ```css @utility icon-box (implementation) { width: 1em; height: 1em; } ``` ### Source ```tsx import { style } from "stillwind/react"; export const icon = style("icon-box"); ``` ### Compiled code ```ts export const icon = {className:"sw_smxmp"}; ``` ### 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 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](/docs/lints/ignored-definition-condition) reports it. Write the condition inside the body instead, or use a variant. # @variant > Apply variants, such as hover or dark, to a block of declarations in a rule of your stylesheet. Source: https://stillwind.dev/docs/at-rules/variant · Markdown: https://stillwind.dev/docs/at-rules/variant/index.md ```css selector { @variant variant:sequence, alternative { declarations } } ``` `@variant` wraps its block in the rules of the listed variants. Write it inside a style rule. ## Example ### Stylesheet ```css .card { background-color: white; @variant hover, focus-within { background-color: var(--color-gray-50); } @variant dark:hover { background-color: var(--color-gray-800); } } ``` ### 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-gray-50: oklch(98.5% .002 247.839); --color-gray-800: oklch(27.8% .033 256.848); } } .card { background-color: #fff; &:hover { @media (hover: hover) { background-color: var(--color-gray-50); } } &:focus-within { background-color: var(--color-gray-50); } @media (prefers-color-scheme: dark) { &:hover { @media (hover: hover) { background-color: var(--color-gray-800); } } } } ``` ## Rules - Each comma-separated sequence receives its own copy of the block. - In a sequence, the leftmost variant is the outermost one. `dark:hover` puts the `dark` media query around the `:hover` rule. - A sequence that does not resolve contributes nothing. `@variant nope, print { ... }` keeps only the `print` copy. - Nested blocks expand inside the rules of the enclosing sequences. - A `@variant` statement without a block stays in the output unchanged. [prefer-variant-directive](/docs/lints/prefer-variant-directive) reports an `@media` query that a variant of your theme can replace, such as a breakpoint. ## Variant definitions At the top level of the stylesheet, a `@variant` block that contains `@slot` defines a variant, for compatibility with older Tailwind CSS stylesheets. Use [@custom-variant](/docs/at-rules/custom-variant) for new definitions. # CLI > Commands, options, file selection, exit codes, and output formats of the Stillwind CLI. Source: https://stillwind.dev/docs/cli · Markdown: https://stillwind.dev/docs/cli/index.md `@stillwind/cli` sorts utilities, reports lint findings, applies automatic fixes, and starts the language server. It reads the same `stillwind.config.ts` as the build integration and the editor. ```sh vp i -D @stillwind/cli ``` To run the CLI in CI, see [Checks in the editor and CI](/docs/guides/lint-adoption). ## Commands | Command | Action | | ---------------- | ---------------------------------------------------- | | `stillwind fmt` | Sorts utilities | | `stillwind lint` | Reports lint findings, and can apply automatic fixes | | `stillwind lsp` | Starts the language server | `stillwind --help` lists the commands, and `stillwind --help` lists the options of a command. `stillwind --version` prints the package version. The short forms of these options are `-h` and `-v`. ## stillwind fmt ```sh vp exec stillwind fmt vp exec stillwind fmt --check ``` `fmt` sorts utilities with the configured theme and [`sort` options](/docs/configuration#sorting), and saves the changed files. It keeps the source layout and the spelling of each token. Lint severity and suppression comments do not affect `fmt`. `fmt` does not change whitespace. To format whitespace too, see [Formatters](/docs/formatters). | Option | Effect | | --------- | ------------------------------------------------------- | | `--check` | Reports the files that need sorting, and saves no files | `fmt` also accepts the [shared options](#shared-options) and [globs](#select-files). Text output shows a status for each listed file and a summary. ## stillwind lint ```sh vp exec stillwind lint vp exec stillwind lint --fix vp exec stillwind lint --max-warnings 0 ``` `lint` reports findings and saves no files. Text output shows a `help:` message when a finding has a suggested edit. Not every suggested edit is an automatic fix. | Option | Effect | | -------------------- | --------------------------------------------------------------------- | | `--fix` | Applies the enabled automatic fixes, and saves the changed files | | `--quiet` | Hides warnings, and keeps their count in the summary | | `--max-warnings ` | Fails when the number of warnings exceeds `n`, a non-negative integer | `lint` also accepts the [shared options](#shared-options) and [globs](#select-files). `--fix` respects suppression comments. It sorts utilities only when [prefer-sorted-utilities](/docs/lints/prefer-sorted-utilities) is enabled. It repeats until the files stop changing, with at most ten changing passes per file. Findings and the exit code describe the files after the last pass. `--max-warnings` counts distinct warnings in each project, including warnings that `--quiet` hides. When hidden warnings exceed the limit, the CLI still prints the reason for the failure. ## stillwind lsp ```sh vp exec stillwind lsp --stdio ``` `lsp` starts the [language server](/docs/editors#other-editors) over stdin and stdout. `--stdio` is required, and stdout carries only protocol messages. `lsp` accepts `--config`, but no globs. ## Shared options `fmt` and `lint` accept these options: | Option | Effect | | ----------------------- | --------------------------------------------------- | | `-c`, `--config ` | Uses this [configuration file](#select-files) | | `--format ` | Selects the [output format](#output-formats) | | `--color ` | Controls [colors](#colors-and-links) | | `--hyperlinks ` | Controls [links to source files](#colors-and-links) | An unknown option, a missing value, or an unsupported value returns exit code `2`. ## Select files The CLI searches upward from the working directory for `stillwind.config.*`. In each directory, it tries the extensions `ts`, `mts`, `cts`, `js`, `mjs`, `cjs`, and `json` in that order. `--config` selects a file directly. The CLI checks every file that `files` selects in each project, including files that your application never imports. It can read other imported files for analysis, but it does not change them. Globs narrow this selection to some of these files. A glob is relative to each project root, and it cannot add files outside `files`. You can pass several globs: ```sh vp exec stillwind lint 'src/**/*.tsx' 'src/**/*.css' ``` A leading `./` is accepted. An absolute path selects that file when `files` includes it. When nothing matches, the CLI returns exit code `2`. ## Saving files `fmt` and `lint --fix` work out the changes for all selected files before they save the first file. The CLI then saves only the files whose content changed, so modification times of other files stay the same. Outside edited ranges, UTF-8 BOMs, line endings, and trailing newlines stay intact. Files must contain valid UTF-8. When something fails: - A file that cannot be read, analyzed, or rewritten stays unchanged. The CLI continues with the other files, even after a saving failure. - A file that changed while the CLI worked is rejected and stays as it is. - A configuration or CSS failure affects every file that needs that configuration or CSS. - When projects overlap, every project that owns a file must produce the same final text. The CLI reports all failures and all saved files, and it returns exit code `2`. ## Exit codes | Code | Meaning | | ---- | ------------------------------------------------------------------------------------------------- | | `0` | The command succeeded. `lint` may still report warnings when you set no `--max-warnings` limit | | `1` | `fmt --check` found files to sort, `lint` found errors, or the warnings exceeded `--max-warnings` | | `2` | The options were invalid, no files matched, or configuration, analysis, or file access failed | Code `2` takes priority over code `1`. ## Output formats | Format | Output | | -------- | ------------------------------------------------------------------------------------- | | `text` | Messages for reading in a terminal; the default outside GitHub Actions | | `github` | Annotations that link findings to files; the default when `GITHUB_ACTIONS=true` | | `json` | [One JSON object](#json-output) for scripts and AI agents | | `files` | One path per line for each file that `fmt` changes or needs to change; only for `fmt` | Results use stdout, and execution failures in `text` and `files` formats use stderr. Configuration logs use stderr in every format. Only `text` output contains colors and links. ### Positions and paths `text` and `github` output count lines and columns from one. Columns count UTF-16 code units. `text` paths are relative to the working directory. `github` paths are relative to `GITHUB_WORKSPACE`, or to the working directory when that variable is not set. A path on another filesystem volume stays absolute. ### Source excerpts A finding at a source range shows a source excerpt, related locations, notes, and suggestions. Excerpts show tabs as four spaces, and they align wide characters and emoji. A long range shows its first four lines. A finding about a whole document has no position or excerpt. The CLI also omits the excerpt when it cannot read the source, or when the source changed after the analysis. ## Colors and links `--color` and `--hyperlinks` accept `auto`, `always`, and `never`. `always` and `never` ignore the environment. With `--color auto`, the CLI uses colors in terminals and supported CI logs. It respects `NO_COLOR`, `FORCE_COLOR`, and `TERM=dumb`. `--hyperlinks` controls OSC 8 links, which open local source files from the terminal. With `auto`, the CLI uses them in recognized terminals, including VS Code and Windows Terminal, and not in CI or redirected output. Terminals without OSC 8 support can still detect the printed `path:line:column` locations. ## JSON output `--format json` prints one object with the same fields in every case, including failures: ```json { "command": "lint", "config": "/project/stillwind.config.ts", "diagnostics": [], "failures": [], "changed": [], "summary": { "files": 1, "errors": 0, "warnings": 0, "failures": 0, "changed": 0, "suggestions": 0, "warningLimitExceeded": false }, "exitCode": 0 } ``` | Field | Value | | ------------- | ---------------------------------------------------------------------------------- | | `command` | `"fmt"` or `"lint"`, or `null` when the CLI failed before it selected a command | | `config` | The configuration path, or `null` when the CLI failed before the analysis | | `diagnostics` | The findings; `--quiet` removes warnings from this list | | `failures` | The execution failures | | `changed` | The files that the command changed; with `--check`, the files that it would change | | `summary` | Counts for the fields above; `warnings` includes warnings that `--quiet` hides | | `exitCode` | The [exit code](#exit-codes) | Each diagnostic contains `project`, a zero-based project index, and `report`. The report contains the message, its locations, notes, and suggested edits. Each failure contains `message` and `details`, and it can contain `project` and `file`. `details` lists the dependency problems that caused the failure. File paths are absolute. Locations in reports use URIs and zero-based UTF-16 positions, and their ends are exclusive. # CSS ordering > See how Stillwind orders CSS from different files, calls, conditions, variants, and utilities, and how the browser decides which value wins. Source: https://stillwind.dev/docs/concepts/css-ordering · Markdown: https://stillwind.dev/docs/concepts/css-ordering/index.md This page explains Stillwind's detailed CSS ordering rules for developers debugging style conflicts or defining custom utilities and variants. It assumes you already use [`style()`](/docs/guides/style) and [reusable styles](/docs/guides/reusable-styles), and know how the CSS cascade works. > **Prefer styles that do not depend on order** > > Avoid relying on which unrelated rule comes last to make your styles work. > When rules do compete, Stillwind tries to put them in the order that authors > are most likely to expect. Stillwind chooses the order of generated CSS at build time. A later declaration can override an earlier one. The browser can still prefer an earlier declaration because of `!important`, cascade layers, or selector specificity. ## What affects CSS order In `style(base, "bg-red-600")`, `base` is the result of another `style()` call. The new call reuses its classes and adds CSS for the new utilities. Stillwind puts the base's CSS first. Read the following table from top to bottom. Stillwind orders files first, then calls within each file, then the rules that each call produces: | Step | What Stillwind orders | Which CSS comes first | | ---- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | Different files | A file that defines a base comes before a file that uses it in `style(base, ...)`. For files without this relationship, Stillwind uses a stable order based on their paths. | | 2 | `style()` calls in one file | The base's CSS comes before CSS added by `style(base, ...)`. Other calls follow the order in which they appear in the file. | | 3 | JavaScript conditions in one call | CSS without a JavaScript condition comes first. CSS for conditions follows in the order the conditions are written. If conditions are nested, CSS for the outer condition comes before CSS for the inner one. | | 4 | CSS variants on one generated class | Rules without variants come first. The stylesheet sets the order of variants, such as `hover:` before `disabled:` or `sm:` before `md:` in the default stylesheet. | | 5 | Utilities with the same variants | By default, utilities follow the order of their definitions in the stylesheet, regardless of their order in `style()`. | For example, the variant order in step 4 only applies to rules for the same class. It does not move a rule ahead of CSS from an earlier file or call. The default `utilityOrder` setting is `"authored"`. In this mode, utilities from plugins come before utilities defined in the stylesheet. Set `utilityOrder` to `"tailwind"` to use Tailwind's property order instead. See [Configuration](/docs/configuration#generated-css). ### A base's rules come before the new call's rules Step 2 comes before step 3. All of the base's CSS comes first, including its conditional rules. The new call's CSS follows, even if it has no condition: ### Source ```tsx import { style } from "stillwind/react"; declare const active: boolean; const base = style("bg-gray-100", active && "bg-blue-600"); export const danger = style(base, "bg-red-600"); ``` ### Compiled code ```ts import { stillwindClassKey, stillwindClsx, stillwindMerge, stillwindToFragment } from "stillwind/runtime/class-name-object"; declare const active: boolean; const base = {className:stillwindClsx("sw_fnaqw",(active) && ("sw_numny"))}; export const danger = stillwindMerge({[stillwindClassKey]:"sw_r65in"},stillwindToFragment((base))); ``` ### Generated CSS ```css .sw_fnaqw { background-color: var(--color-gray-100); } .sw_numny { background-color: var(--color-blue-600); } .sw_r65in { background-color: var(--color-red-600); } ``` The background is red whether `active` is true or false. All three selectors are plain classes, and none of the declarations uses `!important`. The red rule wins because it comes last. The same rule applies when you import `base` from another file. ### Within one call, conditional rules come last In this call, the red utility has a JavaScript condition and the blue utility does not. Stillwind puts the blue rule first, even though it is written last: ```tsx declare const active: boolean; const props = style(active && "bg-red-600", "bg-blue-600"); ``` When `active` is false, the element only has the class for blue. When `active` is true, it also has the class for red, whose rule comes later. With several conditions, a condition written later can override an earlier one when both apply, unless specificity or `!important` decides otherwise. ### Within one class, the stylesheet orders variants The built-in stylesheet defines the `hover` variant before the `disabled` variant. Stillwind follows that definition order when it emits their CSS rules, even when the hover utility is written last: ```tsx const props = style("disabled:bg-gray-300 hover:bg-blue-600"); ``` When the element is both hovered and disabled, the disabled background wins. Both selectors have the same specificity, and the disabled rule comes last. That variant order does not apply across separate calls. Here the new call's hover rule follows the base's disabled rule: ```tsx const base = style("disabled:bg-gray-300"); const props = style(base, "hover:bg-blue-600"); ``` When both states match, the background is blue. The selectors still have the same specificity, but the rule for the second call comes last. ## What does not affect CSS order | What you change | What happens | | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | | Reorder utility strings that have no JavaScript condition | Stillwind uses the configured utility order. Splitting a string into arguments does not make the last argument win. | | Reorder existing bases in `style(first, second)` | The last argument does not become the winning base. Stillwind orders the calls that defined those bases using steps 1 and 2 above. | | Reorder class names on an element | The browser uses the order of declarations in the stylesheet, regardless of the class attribute's order. | | Change JavaScript import order, component render order, or which stylesheet finishes downloading first | For the same set of files, the integration keeps the same CSS order. | For example, with the default stylesheet, both calls below produce the same padding declarations, and `p-4` wins: ```tsx style("p-2 p-4"); style("p-4", "p-2"); ``` [conflicting-utilities](/docs/lints/conflicting-utilities) reports this conflict. Remove the unwanted utility instead of rearranging the input. If two base styles set the same property, do not use their argument order to choose the winner. Define the overriding style by passing the other style as its base: ```tsx const blue = style("bg-blue-600"); const red = style(blue, "bg-red-600"); const props = style(red); ``` [ambiguous-style-base-order](/docs/lints/ambiguous-style-base-order) and [interfering-imported-style-base](/docs/lints/interfering-imported-style-base) report calls that combine conflicting bases without saying which is built on which. ## Specificity and `!important` Putting a rule later does not make its selector more specific. For example, a base's class with `:hover` is more specific than the new call's plain class: ### Source ```tsx import { style } from "stillwind/react"; const base = style("bg-blue-600 hover:bg-blue-800"); export const danger = style(base, "bg-red-600"); ``` ### Compiled code ```ts import { stillwindClassKey, stillwindMerge, stillwindToFragment } from "stillwind/runtime/class-name-object"; const base = {className:"sw_221vg"}; export const danger = stillwindMerge({[stillwindClassKey]:"sw_crw5t"},stillwindToFragment((base))); ``` ### Generated CSS ```css .sw_221vg { background-color: var(--color-blue-600); } @media (hover: hover) { .sw_221vg:hover { background-color: var(--color-blue-800); } } .sw_crw5t { background-color: var(--color-red-600); } ``` The background is red at rest. On a device that supports hover, it becomes blue-800 while hovered: the earlier class with `:hover` beats the later plain class. To make the hovered background red too, give the new call its own hover rule: ```tsx export const danger = style(base, "bg-red-600 hover:bg-red-800"); ``` A declaration with `!important` beats a normal declaration, even if the normal one has a more specific selector. Here the base's blue background wins both at rest and on hover, despite the red rules coming later: ```tsx const base = style("bg-blue-600!"); const danger = style(base, "bg-red-600 hover:bg-red-800"); ``` Layer order also comes before specificity. Among layers, the last layer wins for normal declarations, and the first layer wins for important declarations. The browser uses declaration order only when the earlier rules of the [CSS cascade](https://www.w3.org/TR/css-cascade-5/#cascade-sort) leave a tie. To override a shared style, use `style(base, ...)` with the matching variants instead of adding `!important`. See [no-important](/docs/lints/no-important). ## Stylesheet placement The integration keeps this order during development, in production, and in server-rendered pages. A production build may combine or move CSS rules when doing so does not change which declaration wins. If you combine CSS from separate builds, or place the CSS strings returned by [`style.css()`](/docs/macros/css) yourself, you choose which stylesheet comes first. # How styles compile > See what Stillwind generates at build time, what JavaScript remains at runtime, and how CSS reaches the page. Source: https://stillwind.dev/docs/concepts/how-styles-compile · Markdown: https://stillwind.dev/docs/concepts/how-styles-compile/index.md Stillwind compiles `style()` calls when your bundler builds the application. This page shows what the compiler generates, what JavaScript remains at runtime, and how the CSS reaches the page. ## Static styles A call with only static utilities and CSS variants compiles to an object literal: ### Source ```tsx import { style } from "stillwind/react"; export const props = style("rounded-md p-4 text-blue-600 hover:text-blue-800 md:p-6"); ``` ### Compiled code ```ts export const props = {className:"sw_hk649"}; ``` ### Generated CSS ```css .sw_hk649 { padding: calc(var(--spacing) * 4); color: var(--color-blue-600); border-radius: var(--radius-md); } @media (hover: hover) { .sw_hk649:hover { color: var(--color-blue-800); } } @media (width >= 48rem) { .sw_hk649 { padding: calc(var(--spacing) * 6); } } ``` In this example, the utilities compile to one class. The browser applies `hover:` through a selector and `md:` through a media query. Both stay in CSS, so the compiled JavaScript contains only the class name. ## Conditions and runtime values A condition or a runtime value needs a small amount of JavaScript: ### Source ```tsx import { style } from "stillwind/react"; declare const active: boolean; declare const progress: number; export const props = style("p-4", active && "bg-blue-500", { "--progress": progress }); ``` ### Compiled code ```ts import { stillwindClsx, stillwindCssValue } from "stillwind/runtime/class-name-object"; declare const active: boolean; declare const progress: number; export const props = {className:stillwindClsx("sw_zwy1n",(active) && ("sw_h7hj9")),style:{"--progress":stillwindCssValue(progress)}}; ``` ### Generated CSS ```css .sw_zwy1n { padding: calc(var(--spacing) * 4); } .sw_h7hj9 { background-color: var(--color-blue-500); } ``` The compiled call imports helpers to join the class names and pass the runtime custom property as an inline style. The CSS for both classes is generated at build time. At runtime, the code only chooses class names and sets property values. | You write | JavaScript at runtime | | -------------------------------------- | ------------------------------------------- | | Static utilities and variants | A class name | | A condition, such as `active && "..."` | A helper that joins class names | | A runtime custom property | An inline `style` value | | `style.cva()` | A factory that selects compiled class names | | `style.media()` | A string with the media query | | [`style.css()`](/docs/macros/css) | A CSS string that you place yourself | Development builds import checked versions of the helpers from `stillwind/runtime/class-name-object-development`. These versions report invalid values while you develop. Production builds use the unchecked helpers. ## The generated stylesheet The stylesheet contains utility CSS only for the `style()` calls that the build compiles. Utilities that no compiled call uses produce no CSS. Stillwind fixes the precedence of its generated rules at build time. Class-name order and module-loading order do not change it. Production optimizations can combine or move rules without changing the cascade. See [CSS ordering](/docs/concepts/css-ordering) for base styles, conditions, and variants. ## Delivering CSS to the page The build integration adds the generated CSS to your application. For Vite and the frameworks built on it, the build writes one stylesheet by default. The Astro integration also adds the styles to server-rendered pages and islands. SvelteKit needs a server hook to add them to server-rendered pages in development. See [Framework integrations](/docs/integrations) for the setup of each framework. `style.css()` returns its CSS as a string instead of adding it to this stylesheet. To add the styles of a module to a shadow root, see [Web Components](/docs/guides/web-components). # Stylesheets > See which CSS files the stillwind package provides, how to import them separately, and which optional files change built-in utilities. Source: https://stillwind.dev/docs/concepts/stylesheets · Markdown: https://stillwind.dev/docs/concepts/stylesheets/index.md Most projects need one line: ```css @import "stillwind"; ``` This page lists the CSS files behind that import. Read it when you leave out part of the default stylesheet or add an optional file. ## Stylesheets of the package | Import | Content | | ------------------------- | -------------------------------------------------------- | | `stillwind` | All of the files below except the optional ones | | `stillwind/theme.css` | Theme tokens | | `stillwind/preflight.css` | The reset of default element styles | | `stillwind/utilities.css` | The slot where compiled utility rules go | | `stillwind/builtins.css` | Built-in utilities and variants | | `stillwind/templates.css` | Output templates of `style.css()` | | `stillwind/logical/*.css` | Optional: logical properties for built-in axis utilities | ## Import the files separately `@import "stillwind"` has the same effect as these lines: ```css @layer properties, theme, base, components, utilities; @import "stillwind/theme.css" layer(theme); @import "stillwind/preflight.css" layer(base); @import "stillwind/utilities.css" layer(utilities); @import "stillwind/builtins.css"; @import "stillwind/templates.css"; ``` Write the `@layer` statement and keep the `layer()` assignments when you remove a line. They decide how these files compete with the rest of your CSS. For example, [Migrating from CSS](/docs/guides/migrating-from-css) leaves out `preflight.css` until the old styles are gone. ## Optional stylesheets An optional stylesheet changes built-in utilities, so `stillwind` does not include it. The default definitions stay compatible with Tailwind CSS. Import an optional stylesheet after `stillwind` or `stillwind/builtins.css`. It replaces only the definitions that come before it: ```css @import "stillwind"; @import "stillwind/logical/overflow.css"; ``` ### Logical axis utilities The files in `stillwind/logical/` make built-in axis utilities follow the writing direction, as `px-*` and `py-*` do. Your class names stay the same, and each file changes only its own utilities. Import the files that you need: | File | Utility | Without the file | With the file | | ---------------- | ---------------- | --------------------------------- | -------------------------------------------- | | `overflow.css` | `overflow-x-*` | `overflow-x` | `overflow-inline` | | `overflow.css` | `overflow-y-*` | `overflow-y` | `overflow-block` | | `overscroll.css` | `overscroll-x-*` | `overscroll-behavior-x` | `overscroll-behavior-inline` | | `overscroll.css` | `overscroll-y-*` | `overscroll-behavior-y` | `overscroll-behavior-block` | | `divide.css` | `divide-y-*` | `border-top-*`, `border-bottom-*` | `border-block-start-*`, `border-block-end-*` | | `snap.css` | `snap-x` | `scroll-snap-type: x` | `scroll-snap-type: inline` | | `snap.css` | `snap-y` | `scroll-snap-type: y` | `scroll-snap-type: block` | | `resize.css` | `resize-x` | `resize: horizontal` | `resize: inline` | | `resize.css` | `resize-y` | `resize: vertical` | `resize: block` | In a horizontal writing mode, the result is the same. In a vertical writing mode, the utilities change axes, so `overflow-x-auto` scrolls vertically. [prefer-logical-properties](/docs/lints/prefer-logical-properties) does not report the `overflow`, `overscroll`, and `divide` utilities after you import their files. It still reports arbitrary properties such as `[overflow-x:auto]`, because these files do not change them. Their logical forms, such as `[overflow-inline:auto]`, are available. The utilities keep their cascade order, so `overflow-x-auto` still overrides `overflow-hidden`. # Configuration > Set up projects in stillwind.config.ts, choose lint rules, and look up every project field. Source: https://stillwind.dev/docs/configuration · Markdown: https://stillwind.dev/docs/configuration/index.md `stillwind.config.ts` describes your projects. The build integration, the CLI, and the editor all read the same file. ```ts title="stillwind.config.ts" import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.{ts,tsx,vue,svelte,astro}"], }, ], }); ``` Each entry in `projects` selects a stylesheet and the files that belong to it. Most applications need one project. A monorepo can list one project for each application, or keep a configuration file in each package and reference those files. ## Reference other configuration files A string in `projects` references other configuration files, relative to the file that declares it. Strings and project objects can appear in the same list. ```ts title="stillwind.config.ts" import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: ["apps/*", "!apps/legacy", "packages/ui", "tools/site.stillwind.ts"], }); ``` | Entry | Selects | | ------------------------- | ------------------------------------------------------------------------ | | `tools/site.stillwind.ts` | That configuration file, with any name | | `packages/ui` | The `stillwind.config.*` file in that directory | | `apps/*` | `stillwind.config.*` files that the glob matches or finds in directories | | `!apps/legacy` | Removes a path or a glob from the matches of glob entries | A path or a directory that is missing causes an error. A glob skips directories that have no configuration file, and it does not search `node_modules` or `.git`. Stillwind reports an error only when no project remains in the list. A relative `root` in a referenced file uses the directory of that file, so packages keep working on their own. Referenced files must list project objects because references do not nest. Project names must be unique across all files. Build tools watch the referenced files, but they do not notice a new file that a glob would match. Restart the build after you add a package. ## Where Stillwind finds the file The build integration searches from the application root upward. The CLI and the editor search from the working directory or the workspace upward. The file in the nearest directory wins. The editor also searches from each file that you open. A monorepo therefore works without a configuration file at its root: when no open project includes the file, the editor uses the nearest configuration file inside the workspace. Setting an explicit configuration file turns this search off. Stillwind accepts `stillwind.config.ts`, `.mts`, `.cts`, `.js`, `.mjs`, `.cjs`, and `.json`. In one directory, it uses the first of these that exists. `defineConfig()` also accepts a function or an async function that returns the settings. Stillwind calls it once and uses the result. ## Project fields ### Required | Field | Value | | ------- | ---------------------------------------------------------------------------------- | | `css` | Path to the stylesheet that defines the theme, utilities, and variants | | `files` | Globs for the files that the CLI and the editor check; the build does not use them | ### Scope | Field | Value | | ---------- | ------------------------------------------------------------------------------------ | | `name` | A unique name that selects this project from an integration or the CLI | | `root` | The project directory, relative to the configuration file; defaults to its directory | | `exclude` | Globs that remove files from `files`; `node_modules` and `.git` are always excluded | | `tsconfig` | Path to the TypeScript configuration; defaults to `tsconfig.json` in the root | Paths in `css`, `files`, `exclude`, and `tsconfig` are relative to `root`. ### Checks and sorting | Field | Value | | ------ | ------------------------------------------------------------------------------------------ | | `lint` | Presets and rules; see [Lint](#lint) | | `sort` | Where `stillwind fmt` sorts utilities, or `false` to disable them; see [Sorting](#sorting) | ### Generated CSS | Field | Value | | -------------- | --------------------------------------------------------------------------------------------------------------------------- | | `target` | Minimum browser versions, such as `["chrome111", "safari17"]`; removes fallbacks they do not need | | `utilityOrder` | `"authored"` orders utilities as your stylesheet defines them; `"tailwind"` orders them as Tailwind does | | `important` | `true` makes every utility `!important`; a selector nests utilities under that selector | | `tailwind` | A Tailwind configuration object; see [Migrating from Tailwind CSS](/docs/guides/migrating-from-tailwind#move-configuration) | | `plugins` | Plugin objects for the `@plugin` rules of your stylesheet | | `rootFontSize` | Pixels per `rem`, used by lint fixes and editor previews; defaults to 16 | | `useCssIf` | `true` lets runtime CSS conditions use the native CSS `if()` function; defaults to `false` | ### Generated names | Field | Value | | ---------------- | ----------------------------------------------------------------------------------- | | `classNamespace` | Text added to generated class names; use the same value in client and server builds | | `hashLength` | Number of hash characters in generated names, from 1 to 50; defaults to 5 | | `prefix` | Prefix for Stillwind's custom properties; defaults to `--sw-` | | `tailwindPrefix` | Prefix for Tailwind-compatible internal custom properties; defaults to `--tw-` | | `themePrefix` | Prefix for theme variables in generated CSS, such as `--tw-`; omit it to keep names | | `naming` | Functions that choose generated names yourself | | `namingRevision` | A string that you change whenever you change the `naming` functions | ### Editor | Field | Value | | --------- | --------------------------------------------------------------------------------------------------------------------- | | `preview` | Which comments [CSS previews](/docs/editors#settings) show: `themeValues`, `pixelEquivalents`, and `colorEquivalents` | The [shared configuration API](https://github.com/SegaraRai/stillwind/blob/main/packages/config/src/compiler.ts) describes the remaining fields for custom integrations, such as `sources`, `projection`, `typeFacts`, and `automatic`. ## Lint The `lint` field of a project selects the rules that the CLI and the editor report. [Checks in the editor and CI](/docs/guides/lint-adoption) explains where the rules run. ### Presets and severities ```ts title="stillwind.config.ts" import { defineConfig, lintPresets } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.{ts,tsx,vue,svelte,astro}"], lint: { extends: [lintPresets.recommended, lintPresets.stylistic], rules: { "unknown-utility": "deny", "no-arbitrary-colors": "warn", }, }, }, ], }); ``` | Preset | Contents | | ------------- | ---------------------------------------------------------------------- | | `recommended` | Invalid input, conflicting styles, and other likely mistakes | | `strict` | Every `recommended` rule as an error, and unused suppression comments | | `stylistic` | Preferred spellings and utility sorting; combine it with `recommended` | The [lint rule reference](/docs/lints) shows the severity of each rule in `recommended`. Each rule page shows its severity in every preset. Each rule has one of three severities: | Severity | Effect | | -------- | --------------------------------------------------------- | | `warn` | Reports a warning, which does not fail the CLI by default | | `deny` | Reports an error, which fails the CLI | | `off` | Reports nothing and applies no fixes | ### How the settings combine | `lint` value | Result | | --------------------------- | ------------------------------------ | | Not set | The `recommended` preset | | An object without `extends` | No preset; only the rules in `rules` | | `false` or `{}` | No rules and no fixes | Stillwind applies the presets in `extends` in order, and then `rules`. A later setting replaces the severity of an earlier one. Options in objects merge, and arrays replace earlier arrays. A severity alone, such as `"deny"`, keeps the rule's options from the presets. An unknown rule name is a configuration error. Turning off rules does not turn off [compiler errors](/docs/errors). ### Rule options Some rules accept options. Write the rule as an object with `severity` and the options: ```ts const rules = { "no-restricted-utilities": { severity: "deny", utilities: [{ pattern: "absolute", message: "Use the positioned component." }], }, "no-restricted-declarations": { severity: "deny", rules: [{ properties: ["position"], values: ["absolute"] }], }, "no-restricted-length-units": { severity: "warn", rules: [{ properties: ["width", "height"], disallow: ["px"] }], }, "no-restricted-theme-tokens": { severity: "deny", tokens: ["--color-blue-*"], }, }; ``` | Rule | Options | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `conflicting-utilities` | `allowShorthandOverrides`: allows the supported overrides between shorthand and longhand utilities; defaults to `false` | | `invalid-marker-use` | `ignoredClasses`: marker class names that the rule ignores | | `no-arbitrary-spacing` | `properties`: properties to check in addition to margin, padding, and gap | | `no-restricted-utilities` | `utilities`: utility patterns, each with an optional message | | `no-restricted-declarations` | `rules`: patterns for properties and values; every matching entry applies | | `no-restricted-length-units` | `rules`: property patterns and the units to `disallow`; the last matching entry applies | | `no-restricted-theme-tokens` | `tokens`: patterns for theme custom properties; required when the rule is on | | `prefer-canonical-utilities` | `pxToRem` and `collapse` default to `true`; `logicalToPhysical` defaults to `false`; `preserveSpecificity` defaults to `true`. See the [rule options](/docs/lints/prefer-canonical-utilities#options). | | `prefer-variant-directive` | `pxToRem`: treats `px` and `rem` in media queries as equal at 16px per `rem`; defaults to `false` | Patterns accept `*` for any text and `?` for one character. Utility and theme token patterns are case-sensitive. Property and value patterns are not. ### Definitions of packages and plugins [incompatible-property-value](/docs/lints/incompatible-property-value) checks the declarations that utility definitions write. By default, it checks only your own definitions: your stylesheet, the stylesheets that it imports by a relative or absolute path, and the plugin files that it loads by path. Two project-level fields of `lint` add the definitions of packages and plugins. Presets cannot set them. ```ts lint: { extends: [lintPresets.recommended], checkLibraries: ["@acme/*"], checkPlugins: ["brand"], } ``` | Field | Checks the definitions of | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `checkLibraries` | Stylesheets that you import by package name, and stylesheets under `node_modules`, matched by package name, such as `stillwind` | | `checkPlugins` | Plugins that you load by package name or configure in `plugins`, matched by the name in the `@plugin` rule | Each field accepts `true` for every package or plugin, `false` for none, or a list of case-sensitive patterns. Both fields default to `false`. Use them to check a package of your own, such as a design system in the same repository, or to find problems to report upstream. ## Suppression comments To allow one exception, add a comment that names the rule. Give the reason after `--`: ```ts import { style } from "stillwind/react"; // swlint-disable-next-line no-arbitrary-colors -- External brand color. export const badge = style("bg-[#123456]"); ``` | Comment | Applies to | | ------------------------------------------------ | ---------------------------------- | | `swlint-disable-next-line ` | The next line | | `swlint-disable-line ` | The same line | | `swlint-disable ` … `swlint-enable ` | The lines between the two comments | In stylesheets, write the same text in `/* ... */` comments. A comment hides the finding only. A misspelled utility still produces no CSS. [invalid-lint-suppression](/docs/lints/invalid-lint-suppression) reports a malformed comment, and [unused-lint-suppression](/docs/lints/unused-lint-suppression) reports a comment that suppresses nothing. ## Sorting The `sort` field controls where `stillwind fmt`, the editor, and the [Prettier plugin](/docs/formatters#prettier) sort utilities: | Option | Sorts | Default | | ----------------- | -------------------------------------------------- | ------- | | `stringLiterals` | Utilities inside each string | `true` | | `arrays` | Arguments, array items, and object entries by kind | `true` | | `applyUtilities` | Utilities inside each `@apply` rule | `true` | | `applyStatements` | Adjacent `@apply` rules | `false` | | `variantBlocks` | Adjacent `@variant` blocks | `false` | `sort: false` disables sorting, including `stillwind fmt` and the [prefer-sorted-utilities](/docs/lints/prefer-sorted-utilities) rule. Turning off only that rule keeps `stillwind fmt` working. The CLI checks the files that `files` selects. File patterns on the command line narrow that set; they cannot add files outside it. ## Several projects When the file has several projects, each integration selects one: 1. If the integration has a `project` option, it selects the project with that `name`. 2. Otherwise, it selects the project whose `root` is the deepest directory that contains the application root. If no project or more than one project matches, the build fails with a configuration error. The CLI and the editor check every project. ## Override settings in the integration The integration accepts options that change how it reads the file: ```ts import { stillwind } from "@stillwind/integration/vite"; stillwind({ configFile: "config/stillwind.config.ts", project: "web" }); ``` `configFile` selects another file, relative to the application root. Other options, such as `css` or `lint`, replace the values of the selected project. Objects merge with the file's values, and arrays replace them. # Editors > Install the Stillwind extension, sort and fix on save, and connect other editors to the language server. Source: https://stillwind.dev/docs/editors · Markdown: https://stillwind.dev/docs/editors/index.md The Stillwind language server shows diagnostics, generated CSS, and completion from the active design system while you edit. It reads the same `stillwind.config.ts` as the CLI and the build, and it checks unsaved files too. Create that file with [Getting started](/docs) first. ## VS Code Install **Stillwind** from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=stillwind.stillwind) or [Open VSX](https://open-vsx.org/extension/stillwind/stillwind). An editor that installs extensions from Open VSX uses the same package. The extension needs a trusted workspace, because it runs your project configuration as JavaScript. ### Features - Hover over utilities, `style()` calls, or bound variables to see the generated CSS. - Complete utilities, variants, and theme values. - Complete quoted `@plugin` paths, and follow links to their modules. - See colors, and open color pickers in utilities and theme references. - Apply compiler Quick Fixes. - Run **Stillwind: Sort Utilities**. Stylesheets have completion for Stillwind at-rules, and VS Code's CSS service does not report these at-rules as unknown. ### Language server version Desktop VS Code runs the `@stillwind/cli` version that the workspace installs, so editor diagnostics match the compiler of that version. Install [the CLI](/docs/cli) in the project to get this match. The extension uses its bundled language server when the workspace has no CLI, or when the CLI fails to start. The **Stillwind** output channel shows which server is running. ### Settings | Setting | Value | | ----------------------------------------- | ---------------------------------------------------------------------------------------------- | | `stillwind.configFile` | A configuration path relative to the workspace root; empty searches for `stillwind.config.*` | | `stillwind.completion.registryDirectives` | `true` suggests [registry directives](/docs/at-rules/registry-directives) in utility libraries | Run **Stillwind: Restart Language Server** to reload configuration and dependencies. The `preview` field of each project in `stillwind.config.ts` controls the comments after values in CSS previews: | Option | Comment | | ------------------ | ------------------------------------------------------------ | | `themeValues` | Resolved theme values and evaluated `calc()` results | | `pixelEquivalents` | Pixel values for `rem` | | `colorEquivalents` | Hexadecimal sRGB colors; `"exact"` omits colors outside sRGB | All three are enabled by default. With the default `colorEquivalents` value, `"approximate"`, a `≈` marks colors that the preview maps into sRGB. `false` turns off an option. ### Sort and fix on save Use a [document formatter](/docs/formatters) for layout and whitespace, and add Stillwind's Code Actions: ```json { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll.stillwind": "explicit", "source.sort.stillwind": "explicit" } } ``` `source.fixAll.stillwind` applies the enabled lint fixes and respects suppression comments. `source.sort.stillwind` sorts whatever the lint severity is. `lint: false` in the project disables lint rules and fixes. `sort: false` also disables explicit sorting commands. The [`sort` field](/docs/configuration#sorting) selects what the editor sorts. ### Web VS Code The Web extension always uses the bundled language server, and it needs a host with cross-origin isolation. These limits apply: - Project configuration can import browser-compatible modules. Node built-ins are not available. - The workspace filesystem must be able to read the dependencies. - The default browser provider supports JS, JSX, TS, and TSX. Vue, Svelte, and Astro files need a custom source or projection provider. ## Other editors An editor with a Language Server Protocol client can start the language server from the CLI. Install [the CLI](/docs/cli) in the project, and run this command in the workspace: ```sh stillwind lsp --stdio ``` The server discovers the configuration from each workspace folder. # Compiler error reference > Find a compiler error by its code, and see whether to fix the source, fix the configuration, or report a bug. Source: https://stillwind.dev/docs/errors · Markdown: https://stillwind.dev/docs/errors/index.md A compiler error stops the compilation of a module. Unlike a lint finding, you cannot turn it off or suppress it with a comment. Each error page explains what causes the error and how to fix it. The first digits of a code tell you what to do next: | Codes | What to do | | ----------------- | ---------------------------------------------------- | | SW01xxx – SW03xxx | Fix `stillwind.config.ts` or the integration options | | SW10xxx – SW15xxx | Fix your components, `style()` calls, or stylesheet | | SW30xxx – SW34xxx | Check your integration, or report a bug | | SW90xxx | Report a bug | ## Fix the configuration - [SW01001](/docs/errors/SW01001): Invalid property prefix - [SW01002](/docs/errors/SW01002): Invalid hash length - [SW01003](/docs/errors/SW01003): Invalid lint configuration - [SW02001](/docs/errors/SW02001): Framework constraint conflict - [SW02002](/docs/errors/SW02002): Unresolved root stylesheet - [SW03001](/docs/errors/SW03001): Generated identifier collision - [SW03002](/docs/errors/SW03002): Identifier generator failed - [SW03003](/docs/errors/SW03003): Theme variable collision ## Fix your components and style() calls - [SW10001](/docs/errors/SW10001): JavaScript parse error - [SW10002](/docs/errors/SW10002): Invalid component syntax - [SW10003](/docs/errors/SW10003): Invalid Unicode string - [SW10004](/docs/errors/SW10004): Unconsumed macro use - [SW10005](/docs/errors/SW10005): Untransformed macro reference - [SW11001](/docs/errors/SW11001): Style composition cycle - [SW11002](/docs/errors/SW11002): CSS module ordering cycle - [SW11003](/docs/errors/SW11003): Unavailable style base - [SW12001](/docs/errors/SW12001): Incompatible declaration assignment - [SW12002](/docs/errors/SW12002): Declaration identities unavailable ## Fix your stylesheet - [SW13001](/docs/errors/SW13001): Authored CSS parse error - [SW13002](/docs/errors/SW13002): Unterminated CSS block - [SW13003](/docs/errors/SW13003): Invalid CSS block - [SW13004](/docs/errors/SW13004): CSS nesting limit exceeded - [SW14001](/docs/errors/SW14001): Missing utilities slot - [SW14002](/docs/errors/SW14002): Missing support slot - [SW14003](/docs/errors/SW14003): Malformed property requirement - [SW14004](/docs/errors/SW14004): Malformed utility property requirement - [SW14005](/docs/errors/SW14005): Required property is unregistered - [SW14006](/docs/errors/SW14006): Property-usage parse failure - [SW14007](/docs/errors/SW14007): Malformed theme reference - [SW14008](/docs/errors/SW14008): Circular utility application - [SW15001](/docs/errors/SW15001): CSS offset exceeds source coordinates - [SW15002](/docs/errors/SW15002): Rendered source is too large - [SW15003](/docs/errors/SW15003): Source size or offset is too large ## Check your integration These errors come from the connection between the compiler and the build tool or the editor. With an official integration, update the Stillwind packages first. If the error remains, report it as a bug. If you write your own integration, the error page explains which input to fix. ### Type information - [SW30001](/docs/errors/SW30001): TypeScript checker facts required - [SW30002](/docs/errors/SW30002): Invalid or stale TypeScript facts - [SW30003](/docs/errors/SW30003): Incompatible symbolic fact kinds - [SW30004](/docs/errors/SW30004): Checker expression unavailable ### Vue, Svelte, and Astro components - [SW31001](/docs/errors/SW31001): Invalid projection map - [SW31002](/docs/errors/SW31002): Ambiguous or missing original range - [SW31003](/docs/errors/SW31003): Projection content mismatch - [SW31004](/docs/errors/SW31004): Invalid converter output syntax - [SW31005](/docs/errors/SW31005): Component projection preparation failed - [SW31006](/docs/errors/SW31006): Invalid UTF-16 offset - [SW31007](/docs/errors/SW31007): Invalid UTF-16 position - [SW31008](/docs/errors/SW31008): Invalid UTF-8 offset - [SW31009](/docs/errors/SW31009): Unmapped macro binding read - [SW31010](/docs/errors/SW31010): Edit outside an editable projection region - [SW31011](/docs/errors/SW31011): Attribute quote mismatch ### Stylesheet loading and source files - [SW32001](/docs/errors/SW32001): CSS imports need host resolution - [SW32002](/docs/errors/SW32002): Invalid initial CSS batch - [SW32003](/docs/errors/SW32003): Unknown or duplicate CSS request - [SW32004](/docs/errors/SW32004): Conflicting CSS source - [SW32005](/docs/errors/SW32005): CSS loader already completed - [SW32006](/docs/errors/SW32006): CSS request IDs exhausted - [SW32007](/docs/errors/SW32007): CSS source IDs exhausted - [SW33001](/docs/errors/SW33001): Original map on an internal source - [SW33002](/docs/errors/SW33002): Invalid source byte range - [SW33003](/docs/errors/SW33003): Span belongs to another file - [SW33004](/docs/errors/SW33004): Invalid source span - [SW33005](/docs/errors/SW33005): Invalid source byte offset ### Code transformation - [SW34001](/docs/errors/SW34001): Missing macro import anchor - [SW34002](/docs/errors/SW34002): Missing module CSS target - [SW34003](/docs/errors/SW34003): Transform requested after failed compilation ## Report a bug These errors mean that the compiler produced an inconsistent result. Please [open an issue](https://github.com/SegaraRai/stillwind/issues) with the error message and, if possible, the source that causes it. - [SW90001](/docs/errors/SW90001): Invalid generated CSS block - [SW90002](/docs/errors/SW90002): Missing retained rule - [SW90003](/docs/errors/SW90003): Inconsistent internal CSS graph - [SW90004](/docs/errors/SW90004): Inconsistent macro import metadata - [SW90005](/docs/errors/SW90005): Macro anchor removal edit missing - [SW90006](/docs/errors/SW90006): Invalid transform expression mapping - [SW90007](/docs/errors/SW90007): Overlapping source replacements - [SW90008](/docs/errors/SW90008): Invalid compiler-produced source coordinates # SW01001: Invalid property prefix > The prefix, tailwindPrefix, or themePrefix setting does not start with -- and end with -. Source: https://stillwind.dev/docs/errors/SW01001 · Markdown: https://stillwind.dev/docs/errors/SW01001/index.md This error occurs when `prefix`, `tailwindPrefix`, or `themePrefix` is not a valid prefix for custom properties. A prefix must start with `--`, end with `-`, and contain no whitespace. The message names the setting. ## Reported **stillwind.config.ts** ```ts import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], prefix: "sw", }, ], }); ``` > SW01001: prefix must start with `--` and end with `-` ## Fixed ```ts title="stillwind.config.ts" import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], prefix: "--sw-", }, ], }); ``` ## How to fix - Add `--` at the start and `-` at the end, as in the example. - Use the same value in client and server builds that share styles. # SW01002: Invalid hash length > The hashLength setting is outside the supported range. Source: https://stillwind.dev/docs/errors/SW01002 · Markdown: https://stillwind.dev/docs/errors/SW01002/index.md This error occurs when `hashLength` is not an integer from 1 to 50. The setting chooses the number of hash characters in generated names. The message shows the value and the maximum. ## Reported **stillwind.config.ts** ```ts import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], hashLength: 0, }, ], }); ``` > SW01002: hashLength must be between 1 and 50 base-36 digits, not 0 ## Fixed ```ts title="stillwind.config.ts" import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], hashLength: 5, }, ], }); ``` ## How to fix - Use an integer from 1 to 50. The default is 5. - To shorten names, lower the value. To make collisions less likely, raise it. - Use the same value in client and server builds that share styles. # SW01003: Invalid lint configuration > A lint rule has an option value that the compiler cannot use. Source: https://stillwind.dev/docs/errors/SW01003 · Markdown: https://stillwind.dev/docs/errors/SW01003/index.md This error occurs when a lint rule has an option value that the compiler cannot use, such as an unknown unit or an empty list that the rule requires. The message names the rule and the value. ## Reported **stillwind.config.ts** ```ts import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], lint: { rules: { "no-restricted-length-units": { severity: "warn", rules: [{ properties: ["width"], disallow: ["pixels"] }], }, }, }, }, ], }); ``` > SW01003: invalid `no-restricted-length-units` lint configuration: rules require property patterns and valid CSS length units ## Fixed ```ts title="stillwind.config.ts" import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], lint: { rules: { "no-restricted-length-units": { severity: "warn", rules: [{ properties: ["width"], disallow: ["px"] }], }, }, }, }, ], }); ``` ## How to fix - Correct the value named in the message. In the example, the unit is `px`, not `pixels`. - Give the rules that need a list, such as `no-restricted-theme-tokens`, at least one entry. Each [lint rule page](/docs/lints) describes its options. # SW02001: Framework constraint conflict > The prepared source selects a framework that differs from the component format. Source: https://stillwind.dev/docs/errors/SW02001 · Markdown: https://stillwind.dev/docs/errors/SW02001/index.md This error occurs when a custom integration supplies a source `framework` that conflicts with the component's original format. For example, a Vue component cannot be projected with an explicit Svelte framework constraint. ## How to fix Set the prepared source's `framework` field to the component's framework, or omit it when the integration does not need a source constraint. The converter must also match the component format. Macro imports select each call's output format independently. A file can import several framework macros, including React and vanilla DOM macros. `framework` is not a project option in `stillwind.config.ts`. If an official integration supplies the wrong constraint, report the error with the component and the Stillwind package versions. # SW02002: Unresolved root stylesheet > The stylesheet that the css setting selects cannot be found. Source: https://stillwind.dev/docs/errors/SW02002 · Markdown: https://stillwind.dev/docs/errors/SW02002/index.md This error occurs when the stylesheet that the `css` setting of the project selects cannot be found. Stillwind reads the theme, utilities, and variants from that stylesheet, so it cannot compile without it. ## Reported **stillwind.config.ts** ```ts import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stilwind.css", files: ["src/**/*.tsx"], }, ], }); ``` > SW02002: the root CSS source cannot be unresolved ## Fixed ```ts title="stillwind.config.ts" import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], }, ], }); ``` ## How to fix - Check the path in `css`. It is relative to the `root` of the project, or to the configuration file when `root` is not set. - If you write your own integration, answer the request for the root stylesheet with its source text. Only imported stylesheets may be unresolved. # SW03001: Generated identifier collision > Two different styles would receive the same generated name. Source: https://stillwind.dev/docs/errors/SW03001 · Markdown: https://stillwind.dev/docs/errors/SW03001/index.md This error occurs when two different styles, refs, or animations would receive the same generated name. The usual cause is a custom `naming` function that returns the same name for different requests. A very short `hashLength` can also cause it. ## Reported **stillwind.config.ts** ```ts import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], namingRevision: "1", naming: { utilityClass: () => "button", }, }, ], }); ``` ```tsx style("p-4"); style("p-8"); ``` > SW03001: generated identifier collision: button ## Fixed ```ts title="stillwind.config.ts" import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], namingRevision: "2", naming: { utilityClass: (_context, hash) => `button_${hash.toString("hex")}`, }, }, ], }); ``` ```tsx style("p-4"); style("p-8"); ``` ## How to fix - Include the hash passed as the callback's second argument in the name, as in the example. This keeps the `button` prefix while distinguishing requests. Return `null` to use the default name for a request. - If you use the default names, raise `hashLength`. - Change `namingRevision` whenever you change a `naming` function. # SW03002: Identifier generator failed > A custom naming function threw an error or returned an invalid name. Source: https://stillwind.dev/docs/errors/SW03002 · Markdown: https://stillwind.dev/docs/errors/SW03002/index.md This error occurs when a custom `naming` function fails while it generates a name. The message shows which kind of name it was generating and the error from the function. The compiler does not invent a name in its place. ## Reported **stillwind.config.ts** ```ts import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], namingRevision: "1", naming: { utilityClass(context, hash) { if (!context.module.endsWith("Button.tsx")) { throw new Error("Only Button.tsx is supported"); } return `button_${hash.toString("hex")}`; }, }, }, ], }); ``` ```tsx style("p-4"); ``` > SW03002: identifier generation failed for class: Error: Only Button.tsx is supported ## Fixed ```ts title="stillwind.config.ts" import { defineConfig } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.tsx"], namingRevision: "2", naming: { utilityClass(context, hash) { if (!context.module.endsWith("Button.tsx")) { return null; } return `button_${hash.toString("hex")}`; }, }, }, ], }); ``` ```tsx style("p-4"); ``` The custom name still applies to `Button.tsx`. Other modules use default names. ## How to fix - Reproduce the request from the message, and fix the function so that it returns a name or `null` to keep the default name. - Keep the function pure: the same request must always produce the same name. - Change `namingRevision` whenever you change the function. # SW03003: Theme variable collision > A theme variable takes the name of a custom property that Stillwind uses internally after themePrefix applies. Source: https://stillwind.dev/docs/errors/SW03003 · Markdown: https://stillwind.dev/docs/errors/SW03003/index.md This error occurs when `themePrefix` gives a theme variable the same name as a custom property that Stillwind registers for its utilities. The two values would replace each other, so the utilities that use them would break. ## Reported With `themePrefix: "--tw-"`, the token below becomes `--tw-blur`. The `blur` utility already uses that property for its own value. ```css @theme { --blur: 3px; } ``` ## Fixed ```css @theme { --blur-card: 3px; } ``` ## How to fix - Rename the token so that its output name is free, as in the example. - Or set `tailwindPrefix` to another value, so that the internal properties move out of the theme prefix. Plugins that write `--tw-` directly can stop working when the two prefixes differ, so test them. # SW10001: JavaScript parse error > The JavaScript or TypeScript of a file has a syntax error. Source: https://stillwind.dev/docs/errors/SW10001 · Markdown: https://stillwind.dev/docs/errors/SW10001/index.md This error occurs when the compiler cannot parse the JavaScript or TypeScript of a file. The message shows the location and the explanation from the parser. ## Reported ```tsx export const props = style("p-4"; ``` > SW10001: JavaScript parse error > > Expected `,` or `)` but found `;` ## Fixed ```tsx export const props = style("p-4"); ``` ## How to fix - Fix the syntax at the location in the message, as in the example. - If the file parses in your editor, check that the integration treats it as the right language, such as TypeScript with JSX for a `.tsx` file. Errors in code that a Vue, Svelte, or Astro converter generated are reported as [SW31004](/docs/errors/SW31004) instead. # SW10002: Invalid component syntax > A Vue, Svelte, or Astro component has a syntax error. Source: https://stillwind.dev/docs/errors/SW10002 · Markdown: https://stillwind.dev/docs/errors/SW10002/index.md This error occurs when the parser of the framework cannot read a Vue, Svelte, or Astro component. The message shows the location and the explanation from that parser. ## Reported ```html ``` > SW10002: invalid component syntax: SyntaxError: Element is missing end tag. > > Source format: Vue > > Converter: @vue/language-core@3.3.14 ## Fixed ```html ``` ## How to fix Fix the syntax at the location in the message, as in the example, where the ` ); } ``` When a theme changes, you edit the stylesheet. The components stay the same. To use a breakpoint in JavaScript, pass its name to `style.media()`. See [Responsive styles](/docs/guides/responsive-styles). ## Report values outside the theme Enable these rules in `stillwind.config.ts`: ```ts title="stillwind.config.ts" import { defineConfig, lintPresets } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.{ts,tsx,vue,svelte,astro}"], lint: { extends: [lintPresets.recommended], rules: { "no-arbitrary-colors": "deny", "no-arbitrary-spacing": "deny", "no-arbitrary-breakpoints": "deny", }, }, }, ], }); ``` | Rule | Reports | Example | | ---------------------------------------------------------------- | --------------------------------- | -------------- | | [no-arbitrary-colors](/docs/lints/no-arbitrary-colors) | Colors written as values | `bg-[#123456]` | | [no-arbitrary-spacing](/docs/lints/no-arbitrary-spacing) | Margin, padding, and gap values | `p-[13px]` | | [no-arbitrary-breakpoints](/docs/lints/no-arbitrary-breakpoints) | Viewport widths written as values | `min-[713px]:` | No preset enables these rules, so add them yourself. To enforce them, run the CLI in CI, as described in [Checks in the editor and CI](/docs/guides/lint-adoption). ## Reserve the default palette The default theme also contains palette colors such as `blue-500`. To make components use your role names instead, report the palette tokens: ```ts const rules = { "no-restricted-theme-tokens": { severity: "deny", tokens: ["--color-blue-*", "--color-gray-*"], }, }; ``` The rule reports utilities that use a matching token, such as `bg-blue-500`. Your role tokens can still use palette colors in their definitions. For more restrictions, such as banned utilities or length units, see [Configuration](/docs/configuration#rule-options). # Element relationships > Style an element from another element's state, size, or position through refs that TypeScript checks. Source: https://stillwind.dev/docs/guides/element-relationships · Markdown: https://stillwind.dev/docs/guides/element-relationships/index.md You want to style an element based on another element: a parent's hover state, a sibling's state, the width of a container, or the position of an anchor. Tailwind connects the two elements by name, such as `group/card` and `group-hover/card:`. It reads these names as strings, so it does not check that they match. Nested groups or containers may also need names to target the right ancestor. You must avoid name collisions when nesting components. In Stillwind, you create the name as a variable with `style.ref()`. A misspelled ref is an undefined variable, so TypeScript reports it. ## React to a parent's state Create a ref, attach it to the parent, and use it in the child's style: ```tsx import { style } from "stillwind/react"; const card = style.ref("card"); export function Card() { return (

Project

); } ``` `style(card, ...)` attaches the ref to the `
`. The key `card("hover")` applies `text-blue-600` while the `
` is hovered. Write the variants inside the call, such as `card("hover:active")`, to combine states. The label `"card"` is optional and only helps you read the generated class names. The compiler creates a unique name for each `style.ref()` call, so two components can use the label `"card"` without affecting each other. ## React to a sibling's state Without `.in()`, the ref must be on an ancestor of the styled element. To use a sibling, add `.in()` with a ref on an element that contains both: ```tsx import { style } from "stillwind/react"; const card = style.ref("card"); const trigger = style.ref("trigger"); export function Card() { return (

This text changes color while the button is hovered.

); } ``` `trigger("hover").in(card)` matches while the `
Anchor-positioned content
); } ``` `style.anchorScope([trigger])` limits the anchor name to the `
`, so each rendered `AnchoredHint` positions against its own button. Stillwind generates CSS anchor positioning only. Check that the browsers you support implement it. ## Share refs between components Export a ref from its module and import it where you need it. Keep the call or `.in()` chain, such as `card("hover")`, directly in the key of the `style()` call. You cannot store it in a variable and use it later. ## Report group and peer variants The `stylistic` preset includes [prefer-typed-relationships](/docs/lints/prefer-typed-relationships). The rule reports `group-*` and `peer-*` variants so that you can replace them with refs. The [style.ref() reference](/docs/macros/ref) lists every method and its accepted inputs. # Checks in the editor and CI > Catch misspelled utilities, conflicts, and project policy violations with the CLI, CI, and the editor. Source: https://stillwind.dev/docs/guides/lint-adoption · Markdown: https://stillwind.dev/docs/guides/lint-adoption/index.md You want to catch misspelled utilities, conflicting styles, and values outside your theme before you merge a change. Stillwind checks them with lint rules. The CLI, CI, the editor, and AI agents all use the same rules from `stillwind.config.ts`. ## What the build checks The build stops on compiler errors, such as a JavaScript syntax error or a `style()` call that cannot compile. See [Compiler errors](/docs/errors). The build does not run lint rules. A misspelled utility such as `bg-acent` produces no CSS, and the page still renders without that style. Run the CLI or use the editor to find such mistakes. ## Run the CLI ```sh vp i -D @stillwind/cli ``` Run these commands from the application root: ```sh vp exec stillwind lint vp exec stillwind lint --fix vp exec stillwind fmt ``` `lint` reports findings. `lint --fix` also applies the automatic fixes that the rules provide. `fmt` sorts the utilities in `style()` calls. Review the changes before you commit a large fix. The CLI checks the files that `files` selects in `stillwind.config.ts`, including files that the application does not import. ## Choose rules Without a `lint` setting, the project uses the `recommended` preset. Add a `lint` setting to add presets or change rules: ```ts title="stillwind.config.ts" import { defineConfig, lintPresets } from "stillwind/config"; export default defineConfig({ projects: [ { css: "./src/stillwind.css", files: ["src/**/*.{ts,tsx,vue,svelte,astro}"], lint: { extends: [lintPresets.recommended, lintPresets.stylistic], rules: { "no-arbitrary-colors": "warn" }, }, }, ], }); ``` When you write a `lint` setting, list `lintPresets.recommended` in `extends`. A `lint` setting without `extends` uses no preset. | Preset | Contents | | ------------- | ---------------------------------------------------------------------- | | `recommended` | Invalid input, conflicting styles, and other likely mistakes | | `strict` | Every `recommended` rule as an error, and unused suppression comments | | `stylistic` | Preferred spellings and utility sorting; combine it with `recommended` | Each rule has a severity. `warn` reports a warning, `deny` reports an error, and `off` disables the rule and its fixes. To adopt a new rule, set it to `warn` first, fix the existing code, and then change it to `deny`. [Configuration](/docs/configuration#lint) describes rule options, and the [lint rule reference](/docs/lints) lists every rule. ## Run the checks in CI ```sh vp exec stillwind lint vp exec stillwind fmt --check ``` `lint` fails when an error remains. `fmt --check` fails when utilities need sorting. Warnings do not fail the command. Add `--max-warnings 0` to fail on warnings too. In GitHub Actions, the CLI prints findings as annotations that link to the reported lines. ## Let AI agents run the checks Tell your agent to run `stillwind lint --fix` and `stillwind fmt` after it edits styles. The agent sees the same findings as you. Add `--format json` when the agent or a script reads the results. ## Check while you edit The VS Code extension shows findings as you type, including in unsaved files. It also offers the automatic fixes as Quick Fixes, and it can fix and sort on save. See [Editors](/docs/editors) for the setup. ## Allow an exception When code breaks a rule on purpose, add a comment on the line above it. Name the rule, and give the reason after `--`: ```ts import { style } from "stillwind/react"; // swlint-disable-next-line no-arbitrary-colors -- External brand color. export const badge = style("bg-[#123456]"); ``` A comment hides the finding only. It does not make a misspelled utility produce CSS, and it does not hide compiler errors. # Migrating from CSS > Move styles from plain CSS, CSS Modules, preprocessors, or CSS-in-JS into style() calls and theme tokens. Source: https://stillwind.dev/docs/guides/migrating-from-css · Markdown: https://stillwind.dev/docs/guides/migrating-from-css/index.md You want to move a project that styles elements with plain CSS, CSS Modules, Sass, Less, or CSS-in-JS to Stillwind. Unlike a migration from Tailwind CSS, you translate declarations into utilities. Check the pages in the browser after each step, because the translation can change how elements look. ## Set up Stillwind next to the existing CSS Install Stillwind and register the build integration as described in [Getting started](/docs). Your existing stylesheets keep working while you migrate. `@import "stillwind"` includes the preflight reset, which changes the default styles of elements such as headings and lists. To migrate in steps, leave out the reset until the old styles are gone: ```css @layer properties, theme, base, components, utilities; @import "stillwind/theme.css" layer(theme); @import "stillwind/utilities.css" layer(utilities); @import "stillwind/builtins.css"; @import "stillwind/templates.css"; ``` [Stylesheets](/docs/concepts/stylesheets) describes each of these files. Stillwind's CSS is in cascade layers. A declaration outside a layer wins over a declaration in a layer, whatever their specificity. While an old rule matches an element, it overrides the utilities on that element. Remove the old rule when you move its declarations. ## Move values into the theme Collect the colors, spacing, font sizes, radii, and breakpoints that the old CSS repeats, such as Sass variables or custom properties in `:root`. Define them as `@theme` tokens by role: ```css @theme { --color-surface: oklch(0.98 0 0); --color-accent: oklch(0.6 0.2 250); --breakpoint-tablet: 48rem; } ``` Each token adds utilities, such as `bg-surface` and `tablet:`. Merge values that mean the same thing. Keep values separate when they are equal by chance. [Design systems](/docs/guides/design-systems) describes how to name tokens. ## Move rules into style() Translate the declarations of each rule into utilities, and apply them with `style()` on the element: ```css title="Button.module.css" .button { padding: 0.5rem 1rem; border-radius: 0.375rem; background: var(--accent); } .button:hover { opacity: 0.9; } .button[disabled] { cursor: wait; } ``` ```tsx import { style } from "stillwind/react"; export function Button({ loading }: { loading: boolean }) { return ( ); } ``` Translate selectors into variants: | Old CSS | Stillwind | | -------------------------------------- | ---------------------------------------------------------------------- | | `:hover`, `:focus-visible`, `:checked` | `hover:`, `focus-visible:`, `checked:` | | `@media (min-width: 768px)` | A breakpoint variant, such as `md:` | | `[aria-expanded="true"]`, `[data-x]` | `aria-expanded:`, `data-[x]:` | | `.parent:hover .child` | A ref, see [Element relationships](/docs/guides/element-relationships) | | A toggled modifier class | An attribute and its variant | | A value set from JavaScript | A custom property in `style()` | A declaration without a matching utility can use an arbitrary property, such as `[content-visibility:auto]`. When several elements need it, define a custom utility instead. ## Replace global rules - Keep resets and base element styles until you enable the preflight reset. Then remove the rules that it replaces. - Move `@keyframes` next to the component that uses them with [style.keyframes()](/docs/macros/keyframes). - Move styles for HTML that you do not control, such as rendered Markdown, into a custom utility. See [Recommended practices](/docs/guides/recommended-practices#avoid-separate-css). ## Replace CSS-in-JS Move static styles from `styled()` components or `css` props into `style()` calls. Express props that change styles with attributes and variants, as in [Reusable styles](/docs/guides/reusable-styles). For a continuous value, set a custom property in `style()`. ## Check the migrated code Run the [CLI checks](/docs/guides/lint-adoption) over the migrated files, and compare the pages with the old version in the browser. After you remove the last old stylesheet, switch to `@import "stillwind"` and check the pages again. # Migrating from Tailwind CSS > Move a Tailwind CSS v4 project to Stillwind, from the stylesheet and build to components and configuration. Source: https://stillwind.dev/docs/guides/migrating-from-tailwind · Markdown: https://stillwind.dev/docs/guides/migrating-from-tailwind/index.md You want to move an existing Tailwind CSS v4 project to Stillwind. Your utilities, variants, theme, and plugins keep working. What changes is where you write them: in `style()` calls instead of `className` strings. [Tailwind CSS compatibility](/docs/guides/tailwind-compatibility) lists the tested plugins and the differences. Tailwind scans your files for anything that looks like a class name. Stillwind generates CSS only for the utilities in `style()` calls. A class in a plain `className` string, or in HTML from another source, gets no CSS after the migration. Move every utility that you want to keep into `style()`. ## Replace the stylesheet and the build plugin In your stylesheet, replace `@import "tailwindcss"` with `@import "stillwind"`: ```css @import "stillwind"; @theme { --color-brand: oklch(0.6 0.2 250); } ``` Keep your `@theme`, `@utility`, `@custom-variant`, `@variant`, `@apply`, and `@plugin` rules. Replace the Tailwind build plugin, such as `@tailwindcss/vite`, with the Stillwind integration. Then create `stillwind.config.ts`, as described in [Getting started](/docs#configure-the-project), and set up your framework, as described in [Framework integrations](/docs/integrations). ## Move classes into style() Replace each `className` string with a `style()` call that you spread onto the element: ```tsx import { style } from "stillwind/react"; export function Button() { return ; } ``` Import `style` from the entrypoint of the framework that renders the element. For a class that another stylesheet defines, pass it in a `class` field, such as `style("p-4", { class: "external-card" })`. A large codebase may have hundreds of `className` strings. Your coding agent can write a one-off codemod for your repository that converts them in one pass. ## Replace class helpers `style()` and `style.cva()` replace the libraries that Tailwind projects use to build class strings: | Library | Stillwind | | ----------------------------------- | ----------------------------------------- | | `clsx`, `classnames` | `style()` with conditions | | `tailwind-merge` | Compose a base with new utilities | | `cva` from class-variance-authority | `style.cva()` with the same configuration | ### clsx and classnames Pass conditions as arguments: ```tsx import { style } from "stillwind/react"; export function SaveButton({ loading }: { loading: boolean }) { return ( ); } ``` Do not convert the object form of `clsx`. In `clsx({ "opacity-60": loading })`, each key is a class name. In `style()`, each key of an object is a variant, such as `"hover:"`. Rewrite each entry as `loading && "opacity-60"`. ### tailwind-merge Remove `twMerge()` calls. Store the base style and pass it to another `style()` call. The compiler emits the new utilities after the base's CSS. They override competing declarations when the other cascade criteria, such as specificity and importance, are equal. See [Override a style](/docs/guides/style#override-a-style) and [CSS ordering](/docs/concepts/css-ordering). ### cva Replace `cva(` with `style.cva(`, and import `VariantProps` from your framework's entrypoint: ```tsx import { style, type VariantProps } from "stillwind/react"; const button = style.cva("rounded-md px-4 py-2", { variants: { tone: { neutral: "bg-gray-100", primary: "bg-brand text-white", }, }, defaultVariants: { tone: "neutral", }, }); export function Button({ tone }: VariantProps) { return ; } ``` `variants`, `defaultVariants`, and `compoundVariants` work as in class-variance-authority. Write each option value as a plain utility string, without `&&` or a ternary. The props that the factory returns cannot be passed to `style()` as a base. The [style.cva() reference](/docs/macros/cva) lists the other differences. For new components, consider state variants and `data-*` attributes instead of options. See [Reusable styles](/docs/guides/reusable-styles). ## Move configuration Some Tailwind settings move from the stylesheet to `stillwind.config.ts`: | Tailwind | Stillwind | | --------------------------------- | -------------------------------------------- | | `@config "./tailwind.config.js"` | The `tailwind` field of the project | | `@import "tailwindcss" important` | The `important` field of the project | | `@source` and `source(...)` | Not supported; see below | | `blocklist` | The `no-restricted-utilities` lint rule | | `prefix(tw)` | Write utilities without `tw:`; see below | | `theme(...)` on the import | Not supported; define the tokens in `@theme` | The `tailwind` field accepts a Tailwind configuration object. Its `theme`, `presets`, `plugins`, `darkMode`, and `important` keys work as in Tailwind. Other keys have no effect. This field requires the `tailwindcss` package. Set `important` in one place only. Setting both `important` and `tailwind.important` is an error. Stillwind does not need the `tw:` class prefix, because it generates unique class names. Theme variables can still collide with other CSS in the page. To keep the `--tw-` names of `prefix(tw)`, set `themePrefix: "--tw-"` on the project. The prefix applies to the generated CSS, including `var(--color-brand)` in your stylesheet and in arbitrary values, so you can keep `var(--tw-color-brand)` or use the shorter name. JavaScript that reads or sets a theme variable by name must use the prefixed name. Stillwind does not support `@source`, including `@source inline()`, or a safelist, because only `style()` calls generate utilities. Tailwind classes inside a dependency also get no CSS. Write each utility that you need in a `style()` call in your own code. Stillwind ignores `@config`, `@source`, `@reference`, and import options in the stylesheet, and the CLI reports them with [ignored-tailwind-feature](/docs/lints/ignored-tailwind-feature). Remove them after you move their settings. The same rule reports a v3 path in `theme()`, such as `theme(colors.red.500)`. Use `--theme(--color-red-500)` instead. Keep your `@plugin` rules and the `@import` rules of CSS packages. [Tailwind CSS compatibility](/docs/guides/tailwind-compatibility#plugins-and-css-packages) lists the packages that the test suite covers. ## Check the migrated code Run the [CLI checks](/docs/guides/lint-adoption) over the migrated files. They report utilities that Stillwind cannot resolve and Tailwind features that it ignores. Then test the production build and your pages in the browser. # Recommended practices > Keep styles next to their elements, reuse them through components, and avoid the mistakes that produce no CSS. Source: https://stillwind.dev/docs/guides/recommended-practices · Markdown: https://stillwind.dev/docs/guides/recommended-practices/index.md This page collects the practices that keep a Stillwind project easy to change. It also lists the mistakes that silently produce no CSS. The examples use React. ## Keep styles next to their elements Write the utilities in a `style()` call on the element that they style. When several places need the same styles, reuse them in this order: 1. Extract a component of your framework, and keep the `style()` call on its element. The styles stay next to the markup that they depend on. 2. Define a custom utility in your stylesheet when a component does not fit, such as for links inside other components' text. A utility is available in every file of the project, so its effect is wider than a component's. 3. Export a stored `style()` result. The styles then move away from the element that uses them. When you use an imported style, spread it through `style()`: ```tsx import { style } from "stillwind/react"; import { card } from "./card"; export function Panel() { return
…
; } ``` `{...style(card)}` shows that the element receives Stillwind styles, and you can add utilities in the same call. A plain `{...card}` hides this. ## Spread style() onto the element Spread the result of `style()` onto the element. Do not read `className`, `class`, or `style` out of the result. The result can contain more than one prop, and its shape depends on the framework and on the inputs. If code needs a class string for another purpose, that code does not need `style()`, and a migration can leave it unchanged. Set custom properties in the `style()` call, such as `style("w-(--progress)", { "--progress": value })`. `style()` returns a `style` prop when it needs inline values, so a separate `style` prop on the same element can conflict with it. Set the `style` prop directly only on elements without a `style()` call. You can also use `style()` for an element that has custom properties and no utilities. ## Express states with attributes Keep the same classes on an element, and let its attributes decide which styles apply: - For a state with an attribute, such as `disabled` or `aria-pressed`, use the matching variant. - For an option without a matching attribute, set a `data-*` attribute and use a `data-[…]:` variant. - For a value from a continuous range, set a custom property in the `style()` call, and read it in a utility such as `w-(--progress)`. Use a condition such as `selected && "bg-blue-50"` only when no attribute fits. [Reusable styles](/docs/guides/reusable-styles) shows each approach. Do not track in JavaScript a state that CSS can detect, such as hover, focus, or the width of a container. Use the matching variant, and keep component state for what the application needs. ## Connect elements with refs When an element's style depends on another element, such as a hovered parent, a sibling, a container, or an anchor, connect them with a ref from `style.ref()`. Do not use `group-*`, `peer-*`, named containers, or hand-written parent and sibling selectors: ```tsx import { style } from "stillwind/react"; export function Card() { const card = style.ref("card"); return (

Project

); } ``` A ref is a variable, so TypeScript reports a misspelled ref, and two components cannot collide on the same name. Anchors for anchor positioning use refs too. [Element relationships](/docs/guides/element-relationships) covers siblings, containers, and anchors. ## Define animations in their components Define keyframes with [style.keyframes()](/docs/macros/keyframes) and view transitions with [style.viewTransition()](/docs/macros/view-transition) in the code that uses them. Do not add global `@keyframes` rules or fixed `view-transition-name` values. The macros generate unique names, so you can add, move, or remove a component without checking other code. ## Declare refs and animations in the smallest scope Declare a ref, keyframes, or a view transition in the smallest scope that contains all of its uses, as you would declare any other variable. Place the declaration next to those uses: - A declaration that one component uses belongs in that component's function. - A declaration that a callback uses, such as a `map()` callback, belongs in that callback. - A declaration that several components of one module use belongs at the top level of that module. - When other modules use a declaration, export it from one module, and import it where you need it. Each declaration creates one name wherever you write it. A smaller scope limits where the variable is visible, and it does not create a new name for each render or call. ## Express options with one of a fixed set of values For a prop that takes one of several values, such as a `type` or a `tone`, use one of these forms. Set the value as a `data-*` attribute, and write one variant group for each value: ```tsx import { style } from "stillwind/react"; export function Alert({ type }: { type: "info" | "warning" | "error" }) { return (
); } ``` Or select the utilities with an object lookup: ```tsx import { style } from "stillwind/react"; export function Alert({ type }: { type: "info" | "warning" | "error" }) { return (
); } ``` The attribute form keeps the classes static and changes a `data-*` attribute. The attribute also shows the value in the browser's developer tools and in test queries. The lookup form changes the classes and adds no attribute. In both forms, each value has its utilities in one place. The two forms produce different CSS: - The attribute form compiles into one class. The element always has this class, and the attribute selectors decide which declarations apply. - The lookup form generates one class for each value. The lookup selects one of these classes at runtime. In this example, the common styles add one more class, which the element always has. In both forms, the styles for a value usually win over the common styles. In the attribute form, the attribute selector adds specificity, and the variant order places the rules after the common styles. In the lookup form, the compiler places the utilities that a condition selects after the unconditional utilities. More specific selectors and `!important` can still change the result. [CSS ordering](/docs/concepts/css-ordering) describes these rules. ## Group utilities by variant When several utilities share a variant, group them under one key instead of repeating the prefix: ```tsx style({ "": "rounded-md bg-gray-100 px-4 py-2", "hover:": "bg-gray-200 text-gray-900", "focus-visible:": "outline-2 outline-offset-2 outline-accent", }); // Or write the base utilities as a separate string. style("rounded-md bg-gray-100 px-4 py-2", { "hover:": "bg-gray-200 text-gray-900", "focus-visible:": "outline-2 outline-offset-2 outline-accent", }); ``` The key `""` groups utilities without adding a variant. Each group shows the styles of one state, so readers do not need to collect the prefixed utilities from a long string. > **The key "" accepts the same inputs as a variant** > > The value of `""` accepts only the inputs that a variant group accepts. Write ref attachments, > `class` values, custom properties, and bases directly in the `style()` arguments, outside the > object. Otherwise the CLI reports > [invalid-rule-group-input](/docs/lints/invalid-rule-group-input), and the compiler omits the whole > group. Ref projections, keyframes uses, and view-transition uses can appear in the group. ## Keep values in the theme Define colors, spacing, and breakpoints as `@theme` tokens, and use the utilities that they add. Avoid arbitrary values such as `text-[#abcdef]` and `max-[768px]:`. [Design systems](/docs/guides/design-systems) enables lint rules that report them. ## Choose how to define themes When the application has only a light and a dark theme, define each color token with `light-dark()`, and let `color-scheme` select the value. [Design systems](/docs/guides/design-systems) shows this setup. When the application can have more themes, `light-dark()` cannot express them. Define the tokens as ordinary values, and override them for each theme with an attribute selector. Set `color-scheme` in each theme, so that form controls and scroll bars match: ```css @theme { --color-surface: #ffffff; --color-foreground: #202b45; } [data-theme="dark"] { color-scheme: dark; --color-surface: #1b263b; --color-foreground: #e4edff; } [data-theme="sepia"] { color-scheme: light; --color-surface: #f4ecd8; --color-foreground: #433422; } ``` Set `data-theme` on the root element, or on a container to theme part of the page. The utilities, such as `bg-surface`, stay the same in every theme. When a theme needs styles beyond its tokens, define a variant for it with [@custom-variant](/docs/at-rules/custom-variant), and use the variant instead of a hand-written selector: ```css @custom-variant theme-sepia (&:where([data-theme="sepia"], [data-theme="sepia"] *)); ``` ## Use breakpoints from the theme in JavaScript When JavaScript needs a breakpoint, get its media query with [style.media()](/docs/macros/media) instead of repeating the value: ```ts import { style } from "stillwind/react"; const sidebar = window.matchMedia(style.media("sidebar")); ``` A query such as `matchMedia("(width >= 52.5rem)")` does not follow a later change of the breakpoint in the theme. Prefer a variant when CSS alone can make the change. [Responsive styles](/docs/guides/responsive-styles) shows more uses. ## Define utilities with @apply Write the body of a custom utility with `@apply`, and write its conditions as variants: ```css @utility link { @apply text-accent underline-offset-4 hover:underline; } ``` Do not write selectors or media queries by hand. The variants follow your theme, and they avoid differences that are easy to miss. For example, the `hover:` variant applies only on devices that can hover, while a plain `:hover` rule stays active after a tap on a touch screen. Inside a style rule, use [@variant](/docs/at-rules/variant) instead of a condition. ## Avoid separate CSS Write styles in `style()` calls and custom utilities, not in CSS files with your own class names. A custom utility works with every variant, such as `hover:link`, and a fixed class name does not. For a reusable condition, define a variant with [@custom-variant](/docs/at-rules/custom-variant) instead of writing its selector in several rules. For HTML that you do not control, such as rendered Markdown, define the styles in a custom utility and apply it to the container. You can also use a Tailwind plugin such as `@tailwindcss/typography`. ## Run the checks after each change Run `stillwind lint --fix` and `stillwind fmt` after you change styles, and fix the remaining findings. Add a suppression comment only for a deliberate exception, and give the reason. See [Checks in the editor and CI](/docs/guides/lint-adoption). ## Mistakes that produce no CSS The build succeeds in each of these cases, but the styles do not apply. Run `stillwind lint` to find most of them. - A class name outside `style()`, such as in a `className` string, gets no CSS. - A utility name that your code builds at runtime, such as `` `bg-${color}-600` ``, gets no CSS. Write complete names, such as `color === "red" ? "bg-red-600" : "bg-blue-600"`. - A `style()` call written directly inside another `style()` call contributes nothing. Store the inner style in a variable first. - Custom properties in a base do not carry over to the styles that extend it. Set them in the `style()` call that is spread onto the element. - Classes in the `class` field pass through without generated CSS. - A suppression comment hides a finding only. A misspelled utility still produces no CSS. The compiler can evaluate the arguments of `style()` in any order, and it can skip arguments whose result is not used. Calculate values and perform effects before you call `style()`. # Responsive styles > Name breakpoints once, and use them in utilities, authored CSS, and JavaScript. Source: https://stillwind.dev/docs/guides/responsive-styles · Markdown: https://stillwind.dev/docs/guides/responsive-styles/index.md You want components to change layout at the same widths. Tailwind defines breakpoints in CSS, so a JavaScript check such as `matchMedia("(width >= 52.5rem)")` repeats the value. In Stillwind, you name each breakpoint once and use the name in both CSS and JavaScript. ## Name breakpoints by layout role Name breakpoints after the layout change that they cause, instead of a device size: ```css @import "stillwind"; @theme { --breakpoint-inline: 40rem; --breakpoint-sidebar: 52.5rem; --breakpoint-outline: 70rem; } .navigation { @apply flex flex-col gap-3; @variant inline { @apply flex-row; } } ``` Each `--breakpoint-*` token adds a variant, such as `sidebar:`. The default breakpoints, such as `md:`, remain available unless you remove them. ## Use breakpoints in components ```tsx import { style } from "stillwind/react"; export function Layout() { return (
Content
); } ``` ## Use breakpoints in authored CSS In a CSS rule, nest `@variant` with the breakpoint name, as in the `.navigation` rule above. In an `@utility` definition, use the variant in `@apply` instead, such as `@apply inline:flex-row`. ## Use breakpoints in JavaScript `style.media()` returns the media query for a breakpoint name: ```ts import { style } from "stillwind/react"; const sidebar = window.matchMedia(style.media("sidebar")); const update = () => { document.documentElement.dataset.sidebar = String(sidebar.matches); }; sidebar.addEventListener("change", update); update(); export function dispose() { sidebar.removeEventListener("change", update); } ``` The compiler replaces `style.media("sidebar")` with the query string, so the JavaScript always matches the CSS. `matchMedia()` is a browser API, so call it on the client. Remove the listener when its component unmounts. To combine conditions, write them in one argument. To accept alternatives, pass several arguments: ```ts import { style } from "stillwind/react"; const enhanced = window.matchMedia(style.media("outline:motion-safe", "print")); ``` This query matches a viewport at least as wide as `outline` without a reduced-motion preference, or a printed page. `style.media()` accepts media conditions only. Element states such as `hover` are not media conditions. ## Report widths outside your breakpoints The [no-arbitrary-breakpoints](/docs/lints/no-arbitrary-breakpoints) rule reports widths written as values, such as `min-[713px]:`. See [Design systems](/docs/guides/design-systems#report-values-outside-the-theme) to enable it. To respond to the width of a component instead of the viewport, use a container query. See [Element relationships](/docs/guides/element-relationships#react-to-a-containers-size). # Reusable styles > Share a base style, and express states and options with variants instead of toggled classes. Source: https://stillwind.dev/docs/guides/reusable-styles · Markdown: https://stillwind.dev/docs/guides/reusable-styles/index.md You want components to share styles and to change their look by state or by option. In Stillwind, you share a base style, and you express states and options with variants. The element keeps the same classes, and its attributes decide which styles apply. ## Share a base style Store a `style()` result, and pass it to other `style()` calls. You can export the base and import it in other modules: ```tsx import { style } from "stillwind/react"; export const button = style( "inline-flex rounded-md bg-gray-100 px-4 py-2 font-medium text-gray-900", ); const primaryButton = style(button, "bg-blue-600 text-white"); const dangerButton = style(button, "bg-red-600 text-white"); export function Actions() { return (
); } ``` In this example, the derived styles replace the base's background and text colors. Their CSS comes after the base's CSS, and their selectors have equal specificity. The order of the class names does not affect the result. A base's `hover:` selector can be more specific than a plain class in the derived style. [CSS ordering](/docs/concepts/css-ordering) explains variants, conditions, and multiple bases. A base passes on its classes only. Custom properties such as `{ "--progress": value }` in the base do not carry over, and the CLI reports them. Set custom properties in the `style()` call that is spread onto the element. ## Express states with variants Set the state as an attribute, and style it with a variant: ```tsx import { style } from "stillwind/react"; export function ToggleButton({ pressed }: { pressed: boolean }) { return ( ); } ``` The `aria-pressed` attribute is needed for accessibility anyway. Use variants such as `disabled:`, `aria-expanded:`, and `open:` in the same way. The element always has the same classes, so you do not need to think about which toggled class wins. ## Express options with data attributes For an option without a matching ARIA attribute, set a `data-*` attribute: ```tsx import { style } from "stillwind/react"; export function Badge({ tone, label }: { tone: "neutral" | "danger"; label: string }) { return ( {label} ); } ``` The attribute also shows the option in the browser's developer tools and in test queries. For a value from a continuous range, such as a width or a progress percentage, set a custom property instead. See [Use runtime values](/docs/guides/style#use-runtime-values). ## Migrate CVA components `style.cva()` accepts the configuration of `cva` from class-variance-authority. Use it to migrate existing CVA components. See [Replace class helpers](/docs/guides/migrating-from-tailwind#replace-class-helpers) and the [style.cva() reference](/docs/macros/cva). # The basics of style() > Apply utilities, variants, conditions, overrides, and runtime values to an element. Source: https://stillwind.dev/docs/guides/style · Markdown: https://stillwind.dev/docs/guides/style/index.md `style()` takes Tailwind CSS v4 utilities and returns props for an element. You spread the props onto the element instead of writing a `className` string. This page uses React. For other frameworks, import `style` from the entrypoint listed in [Framework integrations](/docs/integrations). ## Apply utilities ```tsx import { style } from "stillwind/react"; export function Workspace() { return (

Your workspace

Keep your component styles together.

); } ``` The compiler reads the utilities at build time and generates their CSS. In React, `style()` returns a `className` prop. Utilities and variants come from the stylesheet that `stillwind.config.ts` selects. A token that you add to `@theme` adds utilities, such as `bg-accent` for `--color-accent`. ## Add variants Write variants as in Tailwind: ```tsx import { style } from "stillwind/react"; export function SaveButton() { return ( ); } ``` To apply several utilities under the same variant, use an object. Each key is a variant followed by a colon: ```tsx import { style } from "stillwind/react"; export function Link() { return ( Documentation ); } ``` ## Style states When the element has an attribute for the state, use a variant: ```tsx import { style } from "stillwind/react"; export function SaveButton({ loading }: { loading: boolean }) { return ( ); } ``` The element keeps the same classes, and the browser applies the `disabled:` styles while the button is disabled. [Reusable styles](/docs/guides/reusable-styles) shows the same approach with `aria-*` and `data-*` attributes. When no attribute represents the state, pass a condition and a string, as with `clsx`: ```tsx import { style } from "stillwind/react"; export function Row({ selected }: { selected: boolean }) { return
  • Inbox
  • ; } ``` The compiler generates CSS for both strings. At runtime, the condition decides whether the element gets `bg-blue-50 font-medium`. Write complete utility names in the source. The compiler cannot generate CSS for a name that your code builds at runtime, such as `` `bg-${color}-600` ``. Choose between complete names instead, such as `color === "red" ? "bg-red-600" : "bg-blue-600"`. ## Override a style Store a `style()` result in a variable, and pass it to another `style()` call. The compiler places CSS for the new utilities after the base's CSS: ```tsx import { style } from "stillwind/react"; const button = style("rounded-md bg-blue-600 px-4 py-2 text-white"); const dangerButton = style(button, "bg-red-600"); export function DeleteButton() { return ; } ``` In this example, `dangerButton` has a red background because both background rules use equally specific selectors. The order of the class names does not affect the result. You can export a stored style and use it in other modules. More specific selectors and `!important` can change which declaration wins. See [CSS ordering](/docs/concepts/css-ordering) for variants, conditions, and multiple bases. Store the inner style in a variable first. A `style()` call written directly inside another, such as `style(style("p-4"), "text-sm")`, is not supported. [Reusable styles](/docs/guides/reusable-styles) covers shared styles and component options in more detail. ## Use runtime values For a value that is known only at runtime, set a CSS custom property and read it in a utility: ```tsx import { style } from "stillwind/react"; export function Progress({ value }: { value: number }) { return (
    ); } ``` The utility `w-(--progress)` stays the same, so its CSS is generated at build time. Only the value of `--progress` changes at runtime. ## Add classes from other stylesheets To add a class that another stylesheet defines, pass it in a `class` field: ```tsx import { style } from "stillwind/react"; export function Card({ className }: { className?: string }) { return
    ; } ``` Stillwind passes these classes through and does not generate CSS for them. ## Keep arguments free of side effects The compiler can evaluate the arguments of `style()` in any order, and it can skip arguments whose result is not used. Calculate values and perform effects, such as state updates or I/O, before you call `style()`. ## Next steps - [Reusable styles](/docs/guides/reusable-styles): how to share styles and define component options - [Responsive styles](/docs/guides/responsive-styles): how to name breakpoints and use them in JavaScript - [Checks in the editor and CI](/docs/guides/lint-adoption): how to find misspelled utilities and other mistakes - [The style() reference](/docs/macros/style): every accepted input # Tailwind CSS compatibility > Check which Tailwind CSS v4 features, plugins, and CSS packages work in Stillwind, and what Stillwind does differently by design. Source: https://stillwind.dev/docs/guides/tailwind-compatibility · Markdown: https://stillwind.dev/docs/guides/tailwind-compatibility/index.md Stillwind supports the utilities, variants, theme, CSS directives, and plugins of Tailwind CSS v4. To move a project, follow [Migrating from Tailwind CSS](/docs/guides/migrating-from-tailwind). This page lists what works, how Stillwind checks it, and where Stillwind differs. ## What works | Tailwind feature | Support | | --------------------------------------- | -------------------------------------------------------------------- | | Default theme and preflight | Included in `@import "stillwind"` | | Utilities and variants | Supported, including arbitrary values, modifiers, and `!` | | `@theme`, `@utility`, `@custom-variant` | Supported | | `@apply`, `@variant` | Supported | | `--theme()`, `--spacing()`, `--alpha()` | Supported | | `theme(--color-red-500)` | Supported; v3 paths such as `theme(colors.red.500)` are not | | `@plugin` with JavaScript plugins | Supported; see [Plugins and CSS packages](#plugins-and-css-packages) | | `@import` of CSS packages | Supported; see [Plugins and CSS packages](#plugins-and-css-packages) | | Tailwind configuration | Set the `tailwind` field of the project in `stillwind.config.ts` | ## How Stillwind checks compatibility The test suite compares Stillwind with the Tailwind compiler. It runs both compilers on the same input and fails when their results differ: - For utilities and variants, it compares the generated CSS for each class in Tailwind's public class list and in its variant list. - For plugins and CSS packages, it builds the same stylesheet with both compilers and compares the computed styles in Chrome. The declarations inside a rule can stand in a different order from Tailwind's, as [CSS ordering](/docs/concepts/css-ordering) describes. The order of two declarations that can affect each other stays the same, so the computed styles do not change. ## Plugins and CSS packages Tailwind CSS v4 plugins work in Stillwind in principle. The test suite runs the following packages in Vite development and production builds and in Rspack production builds. It also compares their output with Tailwind. Load these packages with `@plugin`: | Package | Example | | ----------------------------------- | ------------------------------------------- | | `@tailwindcss/typography` | `prose`, `prose-lg`, `prose-headings:` | | `@tailwindcss/forms` | `form-input`, and the base styles of forms | | `daisyui` | `btn`, `card`, `badge`, themes | | `tailwindcss-animate` | `animate-in`, `fade-in-50` | | `tailwindcss-motion` | `motion-preset-fade`, `motion-duration-700` | | `tailwind-scrollbar` | `scrollbar-thin`, `scrollbar-thumb-brand` | | `tailwindcss-radix` | `radix-state-open:` | | `tailwindcss-react-aria-components` | `selected:` | | `@headlessui/tailwindcss` | `ui-open:` | | `@iconify/tailwind4` | `icon-[lucide--home]` | For Iconify icons, Stillwind also provides the [`@stillwind/plugin-iconify`](/docs/plugins/iconify) plugin. Load these packages with `@import`: | Package | Example | | ---------------------------- | -------------------------------------- | | `tw-animate-css` | `animate-in`, `fade-in-50` | | `tailwindcss-animated` | `animate-fade`, `animate-duration-700` | | `tailwindcss-intersect` | `intersect:` | | `tailwindcss-safe-area` | `pt-safe-or-4` | | `tailwind-scrollbar-hide/v4` | `scrollbar-hide` | | `preline/variants.css` | `hs-dropdown-open:` | Other plugins can use the same features. [@plugin](/docs/at-rules/plugin#tailwind-css-plugins) lists the supported plugin API. JavaScript plugins need the `tailwindcss` package, and CSS packages do not. The tests cover CSS. They do not run the JavaScript of a package, such as the components of Preline. Plugins have no browser test with webpack or Next.js. Test these combinations in your application. ### Class names that a plugin's selectors use A plugin can select an element by a fixed class name. For example, the rules of `prose` skip elements inside `not-prose`. `style()` keeps such a class in the `class` attribute, so `style("not-prose")` adds the class `not-prose`. The class keeps its name only without variants, so [invalid-marker-use](/docs/lints/invalid-marker-use) reports `hover:not-prose`. ## What Stillwind does not support These differences come from the design of Stillwind. No setting changes them. ### Classes outside style() Only `style()` calls generate utilities. Stillwind does not scan files, so these features have no replacement: - `@source`, `@source inline()`, and `source(...)` on `@import` - A safelist of class names - Tailwind classes inside a dependency - Utility classes that JavaScript adds at runtime, such as `` `bg-${color}-600` `` Write each utility that you need in a `style()` call in your own code. For HTML that you do not control, see [Avoid separate CSS](/docs/guides/recommended-practices#avoid-separate-css). ### The tw: class prefix Stillwind generates unique class names, so utilities do not need a prefix. Write `p-4` instead of `tw:p-4`. Theme variables can still collide with other CSS in the page. To prefix them, set `themePrefix` on the project, as [Migrating from Tailwind CSS](/docs/guides/migrating-from-tailwind#move-configuration) describes. In a plugin, `prefix()` returns the class name unchanged. ### Other differences | Tailwind | Stillwind | | ------------------------------------------ | --------------------------------------------------------------------------------- | | `@reference` | Ignored; `@import` the stylesheet from the stylesheet of the project | | `theme(colors.red.500)` | Stays in the CSS, where browsers reject it; use `--theme(--color-red-500)` | | `blocklist` | Has no effect; use the `no-restricted-utilities` lint rule | | Utilities that generate invalid CSS | Rejected, such as `justify-baseline`, because `justify-content` has no such value | | `addVariant()` with the v3 `:merge()` form | Skipped, as in Tailwind CSS v4 | [ignored-tailwind-feature](/docs/lints/ignored-tailwind-feature) reports the features in your stylesheet that have no effect. # View transitions > Style view transition snapshots in the component, and give each item a stable name. Source: https://stillwind.dev/docs/guides/view-transitions · Markdown: https://stillwind.dev/docs/guides/view-transitions/index.md You want an item to animate between two states of the page with the browser's View Transition API. In plain CSS, you write `view-transition-name` and the `::view-transition-*` rules in a global stylesheet, and you keep every name unique by hand. In Stillwind, you define the transition in the component and name each item with a key. ## Define the transition and name each item ```tsx import { style } from "stillwind/react"; const productTransition = style.viewTransition({ old: "duration-150 motion-reduce:duration-0", new: "delay-150 duration-150 motion-reduce:delay-0 motion-reduce:duration-0", }); export function Product({ id, name }: { id: string; name: string }) { return
    {name}
    ; } ``` `style.viewTransition()` defines the styles of the snapshots. Each field styles one pseudo-element: | Field | Pseudo-element | | ----------- | -------------------------------- | | `group` | `::view-transition-group()` | | `imagePair` | `::view-transition-image-pair()` | | `old` | `::view-transition-old()` | | `new` | `::view-transition-new()` | Timing utilities in these fields set animation properties. For example, `duration-150` sets `animation-duration`. `.for(id)` gives the element a view transition name from the key. Use a key that stays the same for the same item before and after the update, such as a database ID. Two items on the page at the same time need different keys. ## Start the transition Stillwind generates the styles and names. Your code starts the transition: ```ts export function navigate(update: () => void) { if (document.startViewTransition) { document.startViewTransition(update); } else { update(); } } ``` Call `update` the way your framework normally renders a change. Browsers without the View Transition API run the update without animation. ## Give each element one name Use `.for()` once for each element, in one `style()` call. To combine several transitions on one element, pass the others without `.for()`: ```tsx import { style } from "stillwind/react"; const fade = style.viewTransition({ old: "duration-150" }); const slide = style.viewTransition({ new: "duration-300" }); export function Card({ id }: { id: string }) { return
    Card
    ; } ``` The CLI reports a `style()` call that applies a transition without `.for()`, a call with two `.for()` calls, and transitions split across separate `style()` calls. It cannot check that keys are unique across the whole page. The [style.viewTransition() reference](/docs/macros/view-transition) covers dynamic keys and the generated CSS. # Web Components > Add the compiled styles of a component module to its shadow root. Source: https://stillwind.dev/docs/guides/web-components · Markdown: https://stillwind.dev/docs/guides/web-components/index.md You want to style the elements inside the shadow root of a Web Component. The build integration adds compiled CSS to the page's stylesheet, but the page's stylesheet does not reach into a shadow root. Import the compiled CSS of the component's module from `stillwind/module-css`, and add it to the shadow root. ## Keep the styles out of the page ```ts title="vite.config.ts" import { stillwind } from "@stillwind/integration/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [ stillwind({ automatic: false, }), ], }); ``` With `automatic: false`, modules do not add their styles to the page's stylesheet. Each component adds its styles to its own shadow root instead. The webpack and Rspack plugins accept the same option. Keep the default when the application also styles elements outside shadow roots. A module that imports `stillwind/module-css` never adds its styles to the page. With the default, a module that only exports shared styles still adds them to the page. ## Import the CSS of a module ```ts import { css, parts } from "stillwind/module-css"; ``` The compiler replaces this import in each module that contains it. The CSS contains the styles of the `style()` calls in that module and the styles that those calls compose from other modules. It also contains the theme values, the preflight reset, and the other rules of your stylesheet. The import provides the CSS in two forms: - `css` is one string. Use it with an API that accepts stylesheet text, such as Lit's `static styles`. The default export is the same string. - `parts` lists the pieces of the CSS in order. Pass it to `styleSheets()` from `stillwind/css`, which returns constructed stylesheets. Shadow roots in the same document share the stylesheets for equal pieces, such as the preflight reset. ## Add the CSS to the shadow root Add the CSS where your framework sets up the shadow root. ### Lit ```ts import { html, LitElement, unsafeCSS } from "lit"; import { applyStyle, style } from "stillwind/lit"; import { css } from "stillwind/module-css"; class SaveButton extends LitElement { static styles = unsafeCSS(css); render() { return html``; } } customElements.define("save-button", SaveButton); ``` ### Vue Export `styles` from a plain ` ``` ```ts import { defineCustomElement } from "vue"; import SaveButton from "./SaveButton.ce.vue"; customElements.define("save-button", defineCustomElement(SaveButton)); ``` ### Svelte Set `compilerOptions.customElement` to `true` in `svelte.config.js`. Then adopt the stylesheets after the component mounts: ```svelte ``` ### Solid With `solid-element`, adopt the stylesheets in the component function: ```tsx import { customElement } from "solid-element"; import { styleSheets } from "stillwind/css"; import { parts } from "stillwind/module-css"; import { style } from "stillwind/solid"; customElement("save-button", {}, (_props, { element }) => { const root = element.renderRoot as ShadowRoot; root.adoptedStyleSheets = styleSheets(parts, element.ownerDocument); return ; }); ``` ### Vanilla JavaScript ```ts import { styleSheets } from "stillwind/css"; import { parts } from "stillwind/module-css"; import { applyStyle, clearStyle, style } from "stillwind/vanilla"; class SaveButton extends HTMLElement { readonly #root = this.attachShadow({ mode: "open", }); readonly #button = document.createElement("button"); constructor() { super(); this.#button.textContent = "Save"; this.#root.append(this.#button); } connectedCallback(): void { this.#root.adoptedStyleSheets = styleSheets(parts, this.ownerDocument); applyStyle(this.#button, style("px-4 py-2")); } disconnectedCallback(): void { clearStyle(this.#button); } } customElements.define("save-button", SaveButton); ``` ## Compile a stylesheet for a few utilities `style.css()` compiles the utilities that you pass into a CSS string. Use it for a small stylesheet, such as styles for `:host`, when you do not need the preflight reset or the other rules of your stylesheet: ```ts import { style } from "stillwind/vanilla"; export const hostCss = style.css("block p-4", { selector: ":host", }); ``` The `selector` option chooses the element that the utilities style. The string contains the theme values and keyframes that its utilities need, and it adds nothing to the page's stylesheet. Add it to the shadow root in the same way as the `css` string from `stillwind/module-css`, or create a stylesheet from it with `CSSStyleSheet.replaceSync()`. The [style.css() reference](/docs/macros/css) describes variants that choose their own element and the `template` option. ## Constraints - The CSS of a module contains only the styles of that module and the styles that it composes from other modules. The `style()` calls of a component in another module belong to the CSS of that module, even when the component renders into the same shadow root. Keep the `style()` calls for one shadow root in the module that imports the CSS, or compose styles from other modules into those calls. - Shadow roots share the stylesheets that `styleSheets()` returns. Do not change them: a change appears in every shadow root that adopts the same stylesheet. - If a browser that you support lacks `adoptedStyleSheets`, add `css` in a `