Directives & settings

Rule-local composition

Turn an unquoted class list into declarations for the current CSS selector.

@compose

Use native declarations by default. Use @compose when an existing Master utility encapsulates behavior worth maintaining in one place. It expands an unquoted utility list at the statement's position.

CSS
@utilities {  focus-ring {    &:focus-visible {      outline: 2px solid var(--color-accent);      outline-offset: 2px;    }  }}@layer components {  .button {    display: inline-flex;    @compose focus-ring;  }}

One statement uses Master rule priority; reversing its class list does not change conflict resolution. Separate statements retain stylesheet order:

CSS
.example {  @compose color:red;  display: block;  display: made-up-value;  @compose color:blue;}

The final color is blue. Both display declarations remain, so a browser that rejects made-up-value keeps block. Repeated properties, shorthand/longhand interleaving, nested selectors and conditions retain their authored positions. The browser decides the cascade, including !important.

Composed selectors, conditions and importance are preserved. The expansion belongs to the destination rule's layer; it does not import the utility's layer. A composed variant that explicitly changes layer is rejected: put the destination rule inside native @layer instead. Composition does not promise identical cascade behavior to markup in a different layer.

Only resolvable Master utilities may be composed. Native selectors, native classes and native mixins are not targets. Unknown utilities, cycles and failed expansions reject the complete stylesheet; there is no partial successful output. @compose is supported in native rules and @utilities definitions, including static, enum, token and dynamic patterns and their supported nested contexts. Inside a pattern, compose complete, fixed class names; use --value() in declarations, not in the composed class list. It does not introduce parameters, slots or component inheritance.

Fixed composition targets can refer to definitions later in the stylesheet:

CSS
@utilities {  frame:<*> {    @compose frame-base;    width: --value();  }  frame-base {    display: block;  }}

Here frame:20px emits display: block followed by width: 20px. A pattern containing @compose width:--value(); is rejected; composition does not substitute the matched value into another class name.

Compiler inspection reports show statement expansions, referenced utilities, source locations and token/animation dependencies. This provenance stays in compiler/tooling output, outside the runtime manifest.

Selectors and class lists

Selector aliases preserve attribute values and escaped identifiers in the surrounding rule. For example, [data-state=":first"]:first { @compose block; } becomes [data-state=\:first]:first-child { display: block; }. Existing ::before stays unchanged, while :before expands to ::before; aliases inside :is() expand normally.

@compose accepts only unquoted class lists:

CSS
.card {  @compose p-md r-lg;}.overlay {  @compose bg:transparent ! bg-blue-60 !@dark;}

The language-service formatter keeps ! important markers attached to their class token, so bg:transparent ! is formatted as bg:transparent!.

Do not put quotes, grouped class syntax, or class tokens that need CSS quoted strings inside @compose. Use ordinary CSS declarations for declaration-like values, and use nested selectors or @variant blocks for grouped selector behavior:

CSS
.empty-state::before {  @compose block width:3rem height:3rem round;  content: '';}.btn {  @compose text-center;  contain: content;  &:hover {    @variant sm {      @compose bg-blue-60;    }  }}

Order and stylesheet delivery

Native declarations keep their position
Source
.card {  padding: 2rem;  @compose p-md;  padding-inline: 3rem;}
Result
.card {  padding: 2rem;  padding: var(--spacing-md);  padding-inline: 3rem}@layer theme {  :root,  :host {    --spacing-md: 1rem  }}

Native style rules inside @media, @supports, @container, @layer, and @starting-style can also use @compose. Direct compilation and the prepared-files compileStylesheets() API keep composed and native rules in their authored order, including within anonymous layers. Use the final css result: nativeCSS and generatedCSS describe separate parts and cannot be concatenated to recover that order. Quoted content remains literal even when it resembles an internal compose marker. Qualified imports can contain native rules with @compose, but must not contain global Master definitions. Imports that require separate CSS assets still need the stylesheet delivery options. When using those options with compileRenderedStylesheet(), publish every returned stylesheet and resource at its assigned URL, including the entry stylesheet. Calls without delivery options retain the single-output limitations described in CSS imports; they do not publish those separate assets.

Supported contexts

Top-level @compose is invalid because there is no selector context to receive the lowered declarations.

Use @compose in these contexts:

  • Static and pattern definitions inside @utilities, using fixed composition targets.

  • Native style rules such as .card { ... }.

  • Nested selectors and nested native at-rules inside those style rules.

  • CSS Modules and SFC <style> blocks handled by build integrations.

The Vite integration preserves CSS Modules scoped names and exports when lowering @compose. A local stylesheet that contains @compose, @reference, or rule-local @variant does not need @master entry;, does not import @master/css, does not add globally generated utility CSS, and does not become a native CSS pruning root.

Utilities within one @compose statement follow Master CSS rule priority. Separate statements and native declarations retain source order. The final result follows the CSS cascade, including layers, selector specificity, conditions, and !important.


© 2026 Aoyue Design LLC.MIT License
Trademark Policy