Utilities and native styles
Register on-demand utilities and author native defaults and components in CSS layers.
Use native CSS for broad defaults and product components. Register only classes that need on-demand generation and Master suffixes with @utilities.
Native defaults
Write broad styles in @layer defaults. The removed @defaults directive produces an error with a migration hint.
@layer defaults { .prose { color: var(--color-text-body); line-height: 1.6; }}Native components
Write product styles in @layer components. The removed @components directive produces an error with a migration hint.
@layer components { .button { display: inline-flex; align-items: center; justify-content: center; }}Native rules are emitted even when unused unless native pruning is explicitly enabled. Rules in the same layer follow CSS source order. A native .button is not registered as a Master utility and cannot derive button:hover@sm. Write the selector or condition in CSS. The scanner recognizes native class names without reporting them as invalid.
The layer order remains theme, base, defaults, components, utilities. Removing authoring directives does not remove layer suffixes from markup or layer execution from the engine.
@utilities
Registers on-demand Master utilities in the utilities layer. The first level contains bare utility names, not class selectors.
@utilities { content-auto { content-visibility: auto; }}All four forms—static names, hyphen enums, named tokens and raw parameters—accept native declarations, nesting, conditions, and @variant blocks. Names determine the style intent before values are parsed. CSS support data never selects an overload.
Names follow CSS identifier and escape rules, preserving case and Unicode. Decoded names cannot contain HTML whitespace or Master class delimiters. These are Master definition names, not element selectors. @utilities and its patterns are custom at-rule grammar, as permitted by CSS Syntax.
Named and enumerated classes
Exact static names and enum patterns use the semantic priority tier. Named token patterns use the shorthand or longhand tier inferred from their declarations, just like colon patterns; a hyphen alone does not make a token a static utility. Within the same layer, condition, selector, and property tier, token rules precede direct-value rules.
Enum patterns require exactly one <...> segment with at least two distinct pipe-separated keys. Duplicate keys, including keys with different mapped values, are errors. Use token=value when the class suffix should emit a different CSS value:
@utilities { origin-<border=border-box|padding=padding-box> { background-origin: --value(); }}@safelist "origin-padding";@layer utilities { .origin-padding { background-origin: padding-box }}Unmapped entries emit the class token itself, so text-<left|right> behaves like text-<left=left|right=right>.
@utilities { text-<left|center|right> { text-align: --value(); } bg-<cover|contain|auto> { background-size: --value(); }}Named token patterns
Use prefix-<~namespace> for named tokens. The former =namespace spelling is removed. A source list may contain ordered namespace references, but cannot mix token sources with raw kinds, enum values, or *.
@utilities { font-<~font-family> { font-family: --value(); } outline-<~color-line|~color> { outline-color: --value(); } box-<~container> { width: --value(); height: --value(); }}These patterns match names such as font-mono, outline-base, and box-sm. A single utility retains its declared namespace fallback order. Different utilities matching the same name with different intent produce an ambiguity diagnostic; use an explicit name such as font-family-brand or font-size-brand.
Exact static and enum names are reserved: bg-cover remains background size. Resolution selects the longest registered prefix before looking up the token, preserving internal hyphens and never retrying shorter prefixes for a missing token. Use background-color-cover to reference a color token named cover.
Numeric tokens accept a leading negative sign only on properties supporting negative values, for example -m-sm. Color tokens accept opacity in 0..1, for example fg-red/0.5. No automatic numeric scale is inferred from names such as p-4.
Dynamic values
Raw patterns use only key:<*> and accept a complete CSS value. Typed matchers and colon enums are removed; named options use a hyphen enum. Master does not redefine the CSS types <number> and <length>.
@utilities { box:<*> { width: --value(); height: --value(); } accent:<*> { accent-color: --value(); }}box:20px emits width:20px;height:20px; box:var(--size) emits the same properties with var(--size). box:red still emits both declarations and tooling reports invalid CSS values. Unknown functions and environment-dependent values are preserved and reported as unknown when they cannot be verified. Values never change the selected utility.
Raw patterns do not look up tokens. Use var(--name) explicitly, including in functions and multi-value declarations; $name is removed. Named colors require a separate token pattern such as accent-<~color>.
A native property or property alias must retain its native declaration intent. For example, font:16px emits a native font value (invalid by itself), bg:#fff emits background:#fff, and outline:2px emits outline:2px. Use font-size, background-color, or outline-width for partial changes. A managed native-property definition may add equal-value vendor compatibility declarations, but cannot redirect to a subproperty or add unrelated effects. The combined truncation utility is clamp-lines:3; line-clamp:3 is native.
Old colon namespace patterns are rejected with a migration diagnostic. See Migrating from Master CSS v2 RC for preserving RC declaration intent and converting length x using the original settings.
Fixed names and raw values
Native properties and registered property aliases retain native intent. Registered raw keys own their values. Exact static names take priority over enum names; both take priority over token names. Overlapping enum patterns are registration errors after whole-definition replacement. A raw key and a fixed name cannot claim the same entry because name:value and name:pseudo would be ambiguous.
Only an unclaimed, structurally valid property:value enters native fallback. Unknown native properties are preserved and diagnosed. Static names accept structurally valid pseudo-class suffixes such as block:open, block:state(open) and future pseudo-classes without a support whitelist.
text-stroke: always sets the -webkit-text-stroke shorthand. Use text-stroke-width: or text-stroke-color: for partial changes; text-stroke-red remains a named color token.
Reusing a matched value
--value() accepts no arguments. In a parameterized utility's declaration value, it substitutes the complete matched value while preserving CSS token boundaries. It works inside functions such as repeat(--value(), minmax(0, 1fr)). Strings, comments and identifier fragments are not placeholders, and inserted arguments are not expanded again. Ordinary stylesheet calls remain native CSS.
Parameters cannot be inserted into property names, selectors, queries or definition names. Use enum mapping for named alternatives. This is compile-time substitution, not var(--value) or the parameter scope and evaluation model in the CSS Functions and Mixins draft.
@utilities { grid-cols:<*> { display: grid; grid-template-columns: repeat(--value(), minmax(0, 1fr)); } grid-col-span:<*> { grid-column: span --value()/span --value(); }}@utilities { tile-size:<*> { width: --value(); height: --value(); }}@safelist "tile-size:1rem";@layer utilities { .tile-size\:1rem { width: 1rem; height: 1rem }}Whole-definition replacement
Within one layer, a later definition replaces the entire earlier definition with the same identity:
| Form | Identity |
|---|---|
| Static | Decoded name |
| Raw | Decoded key |
| Token | Prefix and ordered namespace list |
| Enum | Prefix and key set, independent of key order and mapped values |
The winning definition occupies its last source position. Old nested rules and resource references disappear. Empty definitions remain registered and match without generating rules. Imports, references, preset overrides and manifest merges follow the same replacement rule.
@utilities { card { color: red; &:hover { color: blue; } } card { padding: 1rem; } disabled-card {} box:<*> { display: block; width: --value(); height: --value(); }}Here card emits only padding. Inside one definition, declaration order, repeated declarations, vendor fallbacks remain ordered.
Compiler inspection retains original definition sources and the source that replaces each overridden definition. Runtime manifests contain only effective definitions.
Definition scope and imports
@settings, @theme, @mode, @custom-variant, and @utilities declare definitions for the whole stylesheet, so they must be written at the top level. Writing one inside a style rule or inside a native at-rule such as @layer, @media, or @supports is reported as @<name> must be top-level; it does not make the definition conditional. A resolved import qualified with layer(), supports(), or a media query must not contain global Master definitions, including definitions in its local import graph. Compilation reports the source and rejects the import. Move the definitions to an unqualified import or a separate @reference input; qualified pure native CSS retains native semantics.
An import Master cannot resolve, such as one addressing a remote stylesheet, stays an @import and moves to the top of the expanded stylesheet, because CSS requires every @import to precede the other rules. Moving it also moves the first appearance of any layer it names, and first appearance is what orders layers, so expansion leads with the authored order:
/* authored */@import "./local.css" layer(a);@import "https://cdn.example/remote.css" layer(b);/* expanded */@layer a, b;@import "https://cdn.example/remote.css" layer(b);@layer a { /* ./local.css */}The statement is added only when the stylesheet names more than one layer and does not already declare an order itself; anything else is emitted unchanged. Two orderings cannot be preserved this way: content sharing one layer with a hoisted import, and unlayered content, both of which are decided by source order alone.
A stylesheet's kind and its compiled output are both decided on the import graph, with every file kept apart. Importing a stylesheet that carries its own remote @import under a layer(), supports() or media qualifier is therefore supported: the qualifier applies to the imported rules and the remote import stays in the file that wrote it. Only an expansion that has to collapse the graph into one file rejects that shape, because CSS does not allow @import inside a block.
For abstraction guidance, see Global styles.