Syntax & rules

Variables & modes

Variable resolution, namespaces, modes and derived values.

Overview

Variables are the bridge between project vocabulary and generated CSS. Master CSS reads @theme blocks as manifest input, resolves token names into namespaces, and emits regular CSS custom properties when a generated rule needs them. Inline and static declarations follow the distinct rules described below.

Modes define activation selectors and conditions; named theme blocks supply alternate variable values. They let the same class string adapt to light, dark, high-contrast, or product-specific themes without duplicating markup. Start with the working spacing and mode examples when learning the authoring flow.

index.css
@import '@master/css';@theme {  --color-brand: #4f46e5;  --spacing-card: 1.5rem;}@theme light {  --color-surface-card: #ffffff;}@theme dark {  --color-surface-card: #111827;}
HTML
<article class="bg-surface-card p-card">  <button type="button" class="fg-white bg-brand">Save</button></article>

Variable lifecycle

Check a custom spacing token

Compile the theme together with the class that uses it. This example adds a project token to the current preset; changing its value changes the generated theme variable.

CSS
@theme {  --spacing-card: 1.5rem;}
HTML
<div class="p-card">Example</div>
CSS
@layer theme {  :root,  :host {    --spacing-card: 1.5rem  }}@layer utilities {  .p-card {    padding: var(--spacing-card)  }}

@theme, @mode, @settings, and @custom-variant directives are manifest inputs. Master CSS reads them before class generation, then generates the required rules and their variable or keyframe dependencies. Static theme resources are an explicit exception to on-demand emission.

index.css
@theme {  --color-brand: #4f46e5;}
HTML
<button type="button" class="fg-white bg-brand">Save</button>

When bg-brand is generated, Master CSS resolves brand through the background color namespace and emits a declaration that references --color-brand. The variable itself is emitted in the theme layer.

Regular CSS custom property behavior still applies. Values inherit through the DOM, cascade by selector, and compute where the browser sees the declaration.

Namespace resolution

Write theme custom properties as --<namespace>-<key>. Master CSS resolves the namespace by longest prefix, so color-line-divider belongs to color-line, not color.

index.css
@theme {  --color-brand: #4f46e5;  --color-text-action: var(--color-brand);  --color-line-divider: rgb(0 0 0 / 12%);  --spacing-card: 1.5rem;  --radius-card: 0.75rem;}

The utility decides which namespace it reads. The same key can mean different values in different contexts:

HTML
<article class="r-card b:1px|solid|var(--color-divider) p-card">  <a class="text-action" href="/settings">Settings</a></article>

Full property names do not become token namespaces automatically. A namespace is available only when the preset registers it and a utility or named token namespace references it.

Default namespace sources

The following registry-backed table lists condition sources and every namespace consumed by default utilities or named token namespaces. It shows complete public keys, including aliases. These are consumers, not a list of defined token values; see the token reference for the preset inventory.

NamespaceConsumers
breakpoint-*
  • @md
  • @media((width<64rem))
  • @sm&<lg
animate-*
  • animate-
color-*
  • -webkit-text-fill-color-
  • -webkit-text-stroke-color-
  • accent-color-
  • b-
  • backdrop-filter-
  • background-color-
Show 45 more keys
  • bb-
  • bg-
  • bl-
  • border-
  • border-block-
  • border-block-color-
  • border-block-end-
  • border-block-end-color-
  • border-block-start-
  • border-block-start-color-
  • border-bottom-
  • border-bottom-color-
  • border-color-
  • border-inline-
  • border-inline-color-
  • border-inline-end-
  • border-inline-end-color-
  • border-inline-start-
  • border-inline-start-color-
  • border-left-
  • border-left-color-
  • border-right-
  • border-right-color-
  • border-top-
  • border-top-color-
  • box-shadow-
  • br-
  • bt-
  • bx-
  • by-
  • caret-color-
  • color-
  • fg-
  • fill-
  • filter-
  • outline-
  • outline-color-
  • shadow-
  • stroke-
  • text-decoration-
  • text-decoration-color-
  • text-fill-color-
  • text-shadow-
  • text-stroke-
  • text-stroke-color-
color-line-*
  • b-
  • bb-
  • bl-
  • border-
  • border-block-
  • border-block-color-
Show 26 more keys
  • border-block-end-
  • border-block-end-color-
  • border-block-start-
  • border-block-start-color-
  • border-bottom-
  • border-bottom-color-
  • border-color-
  • border-inline-
  • border-inline-color-
  • border-inline-end-
  • border-inline-end-color-
  • border-inline-start-
  • border-inline-start-color-
  • border-left-
  • border-left-color-
  • border-right-
  • border-right-color-
  • border-top-
  • border-top-color-
  • br-
  • bt-
  • bx-
  • by-
  • outline-
  • outline-color-
  • stroke-
color-surface-*
  • surface-
color-text-*
  • -webkit-text-fill-color-
  • caret-color-
  • color-
  • fg-
  • text-
  • text-decoration-
Show 2 more keys
  • text-decoration-color-
  • text-fill-color-
container-*
  • background-size-
  • block-size-
  • contain-intrinsic-block-size-
  • contain-intrinsic-inline-size-
  • flex-basis-
  • h-
Show 24 more keys
  • height-
  • inline-size-
  • mask-size-
  • max-block-size-
  • max-h-
  • max-height-
  • max-inline-size-
  • max-size-x-
  • max-size-y-
  • max-w-
  • max-width-
  • min-block-size-
  • min-h-
  • min-height-
  • min-inline-size-
  • min-size-x-
  • min-size-y-
  • min-w-
  • min-width-
  • size-x-
  • size-y-
  • w-
  • width-
  • @container((width>=28rem))
content-*
  • content-
duration-*
  • animation-
  • animation-delay-
  • animation-duration-
  • transition-
  • transition-delay-
  • transition-duration-
easing-*
  • animation-
  • animation-timing-function-
  • transition-
  • transition-timing-function-
font-family-*
  • font-
  • font-family-
font-feature-*
  • font-feature-settings-
font-size-*
  • font-
  • font-size-
  • text-
font-weight-*
  • font-
  • font-weight-
leading-*
  • leading-
  • line-height-
order-*
  • order-
radius-*
  • border-bottom-left-radius-
  • border-bottom-right-radius-
  • border-end-end-radius-
  • border-end-start-radius-
  • border-radius-
  • border-start-end-radius-
Show 8 more keys
  • border-start-start-radius-
  • border-top-left-radius-
  • border-top-right-radius-
  • r-
  • rbl-
  • rbr-
  • rtl-
  • rtr-
shadow-*
  • box-shadow-
  • shadow-
spacing-*
  • background-position-
  • border-spacing-
  • bottom-
  • column-gap-
  • cx-
  • cy-
Show 123 more keys
  • gap-
  • gap-x-
  • gap-y-
  • inset-
  • inset-block-
  • inset-block-end-
  • inset-block-start-
  • inset-inline-
  • inset-inline-end-
  • inset-inline-start-
  • ix-
  • ixe-
  • ixs-
  • iy-
  • iye-
  • iys-
  • left-
  • m-
  • margin-
  • margin-block-
  • margin-block-end-
  • margin-block-start-
  • margin-bottom-
  • margin-inline-
  • margin-inline-end-
  • margin-inline-start-
  • margin-left-
  • margin-right-
  • margin-top-
  • mask-position-
  • mb-
  • ml-
  • mr-
  • mt-
  • mx-
  • mxe-
  • mxs-
  • my-
  • mye-
  • mys-
  • object-position-
  • outline-offset-
  • p-
  • padding-
  • padding-block-
  • padding-block-end-
  • padding-block-start-
  • padding-bottom-
  • padding-inline-
  • padding-inline-end-
  • padding-inline-start-
  • padding-left-
  • padding-right-
  • padding-top-
  • pb-
  • perspective-
  • perspective-origin-
  • pl-
  • pr-
  • pt-
  • px-
  • pxe-
  • pxs-
  • py-
  • pye-
  • pys-
  • right-
  • row-gap-
  • scroll-m-
  • scroll-margin-
  • scroll-margin-block-
  • scroll-margin-block-end-
  • scroll-margin-block-start-
  • scroll-margin-bottom-
  • scroll-margin-inline-
  • scroll-margin-inline-end-
  • scroll-margin-inline-start-
  • scroll-margin-left-
  • scroll-margin-right-
  • scroll-margin-top-
  • scroll-mb-
  • scroll-ml-
  • scroll-mr-
  • scroll-mt-
  • scroll-mx-
  • scroll-mxe-
  • scroll-mxs-
  • scroll-my-
  • scroll-mye-
  • scroll-mys-
  • scroll-p-
  • scroll-padding-
  • scroll-padding-block-
  • scroll-padding-block-end-
  • scroll-padding-block-start-
  • scroll-padding-bottom-
  • scroll-padding-inline-
  • scroll-padding-inline-end-
  • scroll-padding-inline-start-
  • scroll-padding-left-
  • scroll-padding-right-
  • scroll-padding-top-
  • scroll-pb-
  • scroll-pl-
  • scroll-pr-
  • scroll-pt-
  • scroll-px-
  • scroll-pxe-
  • scroll-pxs-
  • scroll-py-
  • scroll-pye-
  • scroll-pys-
  • shape-margin-
  • stroke-dashoffset-
  • text-indent-
  • text-underline-
  • text-underline-offset-
  • top-
  • transform-origin-
  • translate-
  • word-spacing-
  • x-
  • y-
tracking-*
  • letter-spacing-
  • tracking-

Contextual token lookup

Contextual utilities keep class strings short by searching the namespace they already understand.

ClassNamespace searched
p-mdspacing-md
r-mdradius-md
max-w-mdcontainer-md
grid-cols:2@mdbreakpoint-md
text-bodycolor-text-body

This is why token keys should be named for their namespace. md can work across spacing, radius, container, and breakpoint scales because the utility supplies the missing context.

Use native CSS var() when a token should reference another theme token:

index.css
@theme {  --color-brand: #4f46e5;  --color-text-action: var(--color-brand);}

Inline and static variables

Regular theme variables emit CSS custom properties when generated rules need them. Inline variables behave like utility shorthands: Master CSS resolves the value and writes it directly into generated declarations.

CSS
@theme inline {  --color-brand: #4f46e5;  --spacing-feature: 1.5rem;}
HTML
<div class="bg-brand p-feature">Inline token values</div>
CSS
@layer utilities {  .p-feature {    padding: 1.5rem  }  .bg-brand {    background-color: #4f46e5  }}

Inline variables can reference other inline variables. If an inline variable references a regular theme variable, Master CSS keeps the regular var(--*) reference and emits that dependency when needed. Inline variables cannot be mode-specific.

Use @theme static when a variable or managed keyframes definition should be emitted as an initial CSS resource instead of waiting for a matching class.

index.css
@theme static {  --color-brand: #4f46e5;  @keyframes fade-in {    to {      opacity: 1;    }  }}

inline and static cannot be combined.

Mode buckets

The preset explicitly defines light and dark. Every named theme must resolve an existing mode; declare activation separately for a custom mode.

index.css
@theme light {  --color-surface-card: #ffffff;  --color-text-card: #111827;}@theme dark {  --color-surface-card: #111827;  --color-text-card: #f9fafb;}@mode contrast {  [data-theme="contrast"] {    @slot;  }}@theme contrast {  --color-surface-card: #000000;  --color-text-card: #ffffff;}
HTML
<article class="bg-surface-card text-card">...</article>

When a token has mode-specific values, generated utilities reference the same CSS custom property. The active mode changes the variable value, not the class name.

Derived mode-aware values

Base theme variables are emitted on :root,:host, supporting a Document or a ShadowRoot. If a default variable references a mode-specific variable, the browser computes the derived custom property where it is declared. A local .dark or :host(.dark) island can override the referenced token, but it does not recompute an inherited derived token.

index.css
@theme {  --stripe: linear-gradient(135deg, var(--color-line-muted) 4.5%, var(--color-surface-raised) 0);}

Define derived tokens inside each mode bucket when they must follow local mode islands:

index.css
@theme light {  --stripe: linear-gradient(135deg, var(--color-line-muted) 4.5%, var(--color-surface-raised) 0);}@theme dark {  --stripe: linear-gradient(135deg, var(--color-line-muted) 4.5%, var(--color-surface-raised) 0);}

This is CSS custom property computed-value behavior, not a Master CSS selector issue.

Mode activation

@mode defines where a mode activates. Each branch ends at an element selector containing @slot. Variables are declared on that selector. A class such as bg-surface@ocean applies to the activation element and its descendants through a zero-specificity guard.

CSS
@mode ocean {  [data-theme="ocean"] {    @slot;  }}@theme {  --color-surface: white;}@theme ocean {  --color-surface: #082f49;}
HTML
<div class="bg-surface bg-surface@ocean">Mode activation and token values</div>
CSS
@layer theme {  :root,  :host {    --color-surface: white  }  [data-theme=ocean] {    --color-surface: #082f49  }}@layer utilities {  .bg-surface {    background-color: var(--color-surface)  }  .bg-surface\@ocean:where([data-theme=ocean], [data-theme=ocean] *) {    background-color: var(--color-surface)  }}

Multiple branches are alternatives. Media and supports conditions can wrap activation selectors. Redefining a mode replaces its entire activation definition; later theme blocks instead merge token values, with the last value for each token winning. Names cannot collide with breakpoints or custom variants.

Mode definitions belong at the top level. They cannot contain ordinary declarations, pseudo-element activation selectors, layers, containers or recursive mode references. Use @custom-variant for utility selector transformations and general conditional combinations.

System preference with a manual override

Override both preset modes together so a manual choice excludes the opposite system branch:

index.css
@import '@master/css';@mode light {  @media (prefers-color-scheme: light) {    :root:not([data-theme]) {      @slot;    }  }  [data-theme="light"] {    @slot;  }}@mode dark {  @media (prefers-color-scheme: dark) {    :root:not([data-theme]) {      @slot;    }  }  [data-theme="dark"] {    @slot;  }}@theme {  --color-surface: white;  --color-content: #222;}@theme dark {  --color-surface: #082f49;  --color-content: #e0f2fe;}:root {  color-scheme: light dark;}[data-theme="light"] {  color-scheme: light;}[data-theme="dark"] {  color-scheme: dark;}
HTML
<html data-theme="dark">  <body class="bg-surface fg-content">Page content</body></html>
JS
// Choose a manual mode, or remove the attribute to follow the system.document.documentElement.dataset.theme = 'dark';document.documentElement.removeAttribute('data-theme');

The engine never adds color-scheme; the preset and project CSS declare it explicitly. Base @theme values replace the need for a global default mode.

Local themes and overlap

CSS variables inherit normally. A nested theme overrides variables on its own activation element. If different modes match simultaneously, ordinary CSS cascade rules decide the result. Ancestor mode guards still match descendants inside another mode; there is no “nearest mode wins” utility exclusion or JavaScript mode state machine.

Shadow DOM

Define a shadow host branch explicitly inside the stylesheet compiled for the shadow root:

CSS
@mode ocean {  :host([data-theme="ocean"]) {    @slot;  }}@theme {  --color-surface: white;}@theme ocean {  --color-surface: #082f49;}

Use bg-surface inside the shadow tree, or bg-surface@ocean for a conditional utility. Styles do not implicitly cross a shadow boundary or style assigned slot content. A document mode selector does not replace a shadow host branch.

Project settings

Settings are separate from design tokens. scope prefixes generated selectors when Master CSS should style a specific application root:

CSS
@settings {  scope: #app;}

There is no root-size, mode-trigger or default-mode setting in the v2 language contract. Keep CSS units explicit and use @mode for activation. See the settings directive, theme directive, and v2 RC upgrade guide.


© 2026 Aoyue Design LLC.MIT License
Trademark Policy