層疊層
Understand how theme, base, defaults, components, and utilities layers control style priority.
How layers control the cascade
CSS cascade layers let a stylesheet decide which groups of rules should win before selector specificity is compared. Master CSS uses that native model as the backbone of its output, so utilities, project styles, presets, variables, and resets can coexist without turning every override into a specificity contest.
The default @master/css/base.css stylesheet declares the layer order:
@layer theme, base, defaults, components, utilities;Layers declared later have higher priority for normal declarations. In Master CSS, the practical order is:
utilities > components > defaults > base > theme| Layer | Description | CSS |
|---|---|---|
| Base | Where the styles with @base are generated. | @layer base { … } |
| Theme | Where the used theme tokens are generated. | @layer theme { … } |
| Defaults | Where the styles with @default are generated. | @layer defaults { … } |
| Components | Where the used component classes are generated. | @layer components { … } |
| Utilities | Where the utility styles are generated. | @layer utilities { … } |
Generated CSS emits rules into those layer blocks. Native style rules that use @compose or @variant lower to ordinary CSS in the stylesheet that contains them. This order is intentional: foundation rules go early, product vocabulary goes in the middle, and one-off utility decisions stay late enough to override project styles when markup asks for it.
The five layers
Base
Use base for low-level resets and normalization. This layer should make the browser environment predictable, not express a brand or component design.
Rules enter the base layer through @base syntax or regular CSS written inside @layer base.
Theme
Use theme for generated CSS custom properties. Tokens defined in @theme are emitted here only when a generated rule references them.
Theme rules are intentionally early: they provide values that later layers consume, but they should not compete with layout, color, or component declarations directly.
Defaults
Use defaults for broad element defaults such as typography inside an article, code styling, or vendor defaults you want to keep below app-level styles.
Rules enter the defaults layer through @default syntax or @defaults managed definitions. Because defaults sit before components and utilities, components and utilities can still override it without !important.
Components
Use components for project styles that carry product meaning: .btn, .card, .field, .toolbar, .app-shell. In project CSS, write these managed class definitions in @components.
@components { btn { @compose inline-flex; background-color: var(--color-primary); }}The compiler converts these definitions into static manifest utilities with layer: 'components'. They remain on-demand: defining .btn does not emit CSS until btn is used or scanned from source.
Utilities
Use utilities for built-in utilities, animation utilities, and custom static utilities written in @utilities. Custom utilities can use native declarations, @compose, nested selectors, and @variant blocks.
@utilities { flow { @compose grid gap:md; } content-auto { content-visibility: auto; contain-intrinsic-size: auto 32rem; }}Utilities is the final layer, so utility classes can intentionally override component styles from markup.
A complete flow
This example touches every generated layer. The project CSS entry defines theme variables and a component, while the markup uses base, defaults, and utilities.
@theme light { --color-primary: #000000;}@theme dark { --color-primary: #ffffff;}@components { btn { @compose inline-flex; background-color: var(--color-primary); }}<body class="list-style:none_ul@base"> <button class="btn flex">Submit</button> <ul class="animation:fade|1s">…</ul> <article class="text:1rem_p@default"> <p>…</p> </article></body>Generated CSS
@layer base { .list-style\:none_ul\@base ul { list-style: none }}@layer theme { .light, :root { --color-primary: rgb(0 0 0) } .dark { --color-primary: rgb(255 255 255) }}@layer defaults { .text\:1rem_p\@default p { font-size: 1rem; line-height: max(1.8em - max(0rem, 1rem - 1rem) * 1.12, 1rem); letter-spacing: clamp(-0.072em, calc((1rem - 1rem) * -0.048), 0em) }}@layer components { .btn { display: inline-flex } .btn { background-color: var(--color-primary) }}@layer utilities { .flex { display: flex } .animation\:fade\|1s { animation: fade 1s }}@keyframes fade { 0% { opacity: 0 } to { opacity: 1 }}Notice the shape of the output:
@master/css/base.cssdefines the global layer order once.- Theme variables are emitted in
@layer theme. - The
.btndefinition is emitted in@layer components. flexandanimation:fade|1sare emitted in@layer utilities.@keyframesare emitted at the top level, outside cascade layers.
Base is for normalization
Put reset-style rules in the base layer when they should sit below every design decision.
Add base rules in the stylesheet your app loads when the reset is part of the application shell.
@layer base { ul { list-style: none; }}You can also use @base from markup:
<body class="list-style:none_ul@base">…</body>Generated CSS
@layer base { .list-style\:none_ul\@base ul { list-style: none }}In most projects, import the default @master/css stylesheet; it includes base styles in the base layer.
Defaults are for broad defaults
Put brand or content defaults in the defaults layer when they should apply across selected descendants but still remain easy to override.
<body class="font:mono_:is(code,pre)@default">…</body>Generated CSS
@layer defaults { .font\:mono_\:is\(code\,pre\)\@default :is(code, pre) { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace }}Do not put brand defaults into the base layer.
<body class="font:mono_:is(code,pre)@base">…</body>Base should normalize. Defaults should express broad design defaults.
Utilities can override components
Layer order is checked before selector specificity. That means a utility can override a component style even when the component style comes from a named component.
For example, .btn defines display: inline-flex in the components layer. Adding flex in markup moves the final display decision to the utilities layer.
<button class="btn flex">Submit</button>Generated CSS
@layer components { .btn { display: inline-flex; … }}@layer utilities { .flex { display: flex; }}Use this as the normal override path: keep reusable product styles in components, then use utilities in utilities for local adjustments.
Defaults stay below local decisions
Defaults rules are useful for descendants because they can establish defaults without blocking local classes.
For example, set paragraph text to 1rem across an article, then override one paragraph with text:1.5rem.
<article class="text:1rem_p@default"> <p class="text:1.5rem">24</p> <p>16</p></article>Generated CSS
@layer defaults { .text\:1rem_p\@default p { font-size: 1rem; line-height: max(1.8em - max(0rem, 1rem - 1rem) * 1.12, 1rem); letter-spacing: clamp(-0.072em, calc((1rem - 1rem) * -0.048), 0em) }}@layer utilities { .text\:1\.5rem { font-size: 1.5rem; line-height: max(1.8em - max(0rem, 1.5rem - 1rem) * 1.12, 1.5rem); letter-spacing: clamp(-0.072em, calc((1.5rem - 1rem) * -0.048), 0em) }}This is the reason defaults sit before components and utilities: defaults should be broad, but final element-level decisions should stay close to the element.
Writing regular CSS
Regular CSS outside @layer follows the native cascade outside Master CSS's layer model. If you want a rule to participate in the same priority system, place it in the layer that matches its responsibility.
@layer defaults { article :is(h1, h2, h3) { font-weight: 700; }}@layer components { .card { border-radius: .75rem; }}Native @layer is never compiler-managed. For on-demand managed definitions, use the explicit directive forms:
@utilities { content-auto { content-visibility: auto; }}@components { card { @compose block; border-radius: .75rem; }}Native style rules can still compose Master CSS utilities when the rule should remain ordinary stylesheet output:
.card { @compose block p:md; @dark { @compose bg:neutral-90; }}Summary
Cascade layers are the contract that makes Master CSS predictable:
basenormalizes the platform.themeprovides generated variables.defaultssets broad defaults.componentsholds project vocabulary from CSS-defined component classes.utilitiesholds utilities and final local adjustments.
When declarations conflict, solve the problem by choosing the correct layer first. Reach for specificity or !important only when you are intentionally integrating with CSS that sits outside this layer model.