Navigation

Syntax

Extraction

Static candidate extraction, source boundaries, classification and shared source ownership.

On this page

Extraction finds possible class names in source without running the application. These candidates, also called latent classes, are inputs to matching and candidate policy. Finding a string does not establish that it generates a rule, has a valid CSS value or works in a particular browser.

This page defines source boundaries, static extraction, candidate classification and source ownership. Scanning class names explains how to author discoverable classes and diagnose missing styles. Scanning directives owns the path and class-pattern grammar.

Complete static strings

Extractors use the source language's structure. JavaScript and TypeScript strings can contribute candidates even outside className: a class passed to a component as display text may still be discovered. The scanner does not infer which branch runs or what a function returns at runtime.

Source formWhat extraction can see
HTML class="p-md shadow-md"Both complete classes
JSX className="p-md"The complete class
error ? 'bg-red-60' : 'bg-green-60'Both static branches
{ compact: 'p-sm', roomy: 'p-lg' }The complete values in the object
A template literal with complete static class portionsThose complete portions
'bg-' + tone or a template interpolation inside a class nameNo inferred bg-red-60 or other completed result
A class returned only by an API at runtimeNo source string to extract

For example, the following source exposes both colors and the fixed padding and radius. The tone interpolation does not need to be evaluated to discover the complete classes already present in the object:

const tones = {  neutral: 'bg-surface-base fg-text-neutral',  destructive: 'bg-red-70 fg-white'}const className = `px-md r-md ${tones.destructive}`

The extracted class set is bg-surface-base, fg-text-neutral, bg-red-70, fg-white, px-md and r-md. The scanner does not limit output to the destructive branch just because this example selects it.

Candidate classification

After extraction, the active manifest and stylesheet registrations determine a candidate's role. Unknown ordinary application classes are not automatically Master CSS errors.

CandidateResult
Registered preset utility such as p-mdGenerates its matched utility rule and dependencies
Project-defined @utility classGenerates the rule registered by the project's compiled manifest
Registered native class such as .cardRecords native usage; it does not generate a new utility
Unmatched application classNo Master-generated rule
Malformed Master classNo rule; inspection can report a syntax diagnostic
Matched class with an invalid or unknown CSS valueCan emit CSS; matching alone does not prove value validity or browser support
<article class="card p-md shadow-md">Release notes</article>

Here p-md and shadow-md resolve through the preset. card retains a native rule only if the project actually supplies one and its pruning policy considers usage. A native stylesheet is preserved by default; scanning does not opt it into pruning.

Generated CSS
@layer theme {  :root,  :host {    --spacing-md: 1rem  }  :root,  :host {    --shadow-md: 0 0 0 1px light-dark(oklch(0% 0 none / .05), oklch(100% 0 none / .06)), 0 1px 1px -.5px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .3)), 0 2px 2px -1px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .26)), 0 4px 4px -2px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .24)), 0 8px 8px -4px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .26))  }}@layer utilities {  .shadow-md {    box-shadow: var(--shadow-md)  }  .p-md {    padding: var(--spacing-md)  }}

A compiler match, CSS syntax validation, CSS value validation and browser support are distinct checks. Use class inspection to distinguish them; a matching class still enters the browser's cascade and computed-value rules.

Source boundaries

Build integrations and CLI flows discover common source-like project files, including HTML, JavaScript, TypeScript, JSX, TSX, supported framework sources, Markdown, MDX and PHP. Ordinary application files do not need a broad @source directive.

Automatic discovery respects project and ancestor workspace .gitignore files, not global Git ignores. Dependencies, declaration files, binary files, VCS data and generated output are excluded. Master output and the current bundler output are not scan inputs; Next.js uses its configured distDir.

DirectiveEffect on discovery
@sourceExplicitly includes additional paths, including files excluded by automatic discovery or .gitignore
@source notExcludes paths; takes precedence over inclusion

Use these exceptions for shared workspace source, external content or a scoped pruning root. Their relative-path rules and supported forms belong to Scanning directives.

Framework integrations prefer original MDX, Vue and Svelte source to scanning compiled display strings a second time. An extractor's language kind matters: intentional plain-text input can use kind: 'raw', but that is not a fallback for a source that failed to parse.

Markdown and MDX

Markdown and MDX extraction uses the syntax tree. Actual HTML/JSX, ESM and expressions participate. Prose, inline code, ordinary code fences and frontmatter do not. A documentation fence showing a class is therefore not an implicit live example.

A host can register an executable example as a virtual source with parentSource, or explicitly safelist its bounded classes. Virtual inputs make the ownership of executable content explicit; the host supplies the actual example source and language kind.

A malformed source reports SOURCE_PARSE_ERROR. Development retains the previous successful contribution, while a production build fails. Parsing or reading failure is not equivalent to an empty-source update.

Source locations

Extraction can report candidate occurrences as well as strings: original UTF-16 ranges, extractor, content kind, owner and inclusion or exclusion reasons. Encoded attributes retain ranges in the original source. Complete URL literals are excluded; an unknown native declaration can remain a candidate for later validation.

Source ownership

A scanner session tracks current source contributions, not every class it has ever encountered. Updating a source replaces its contribution. Removing one source does not remove a class that another current source or safelist still retains.

Removing one source does not remove a shared rule

With unchanged definitions and policy, removing page A leaves page B’s reference to p-md. Source ownership changes even though the generated CSS can stay the same.

Text description
  1. Initially page-a.tsx and page-b.tsx both contain the complete p-md class.
  2. Removing page-a.tsx removes only that source contribution. The live connection from page-b.tsx keeps the shared utility and its --spacing-md dependency.
  3. The utility can leave when no current source or safelist retains it. Updating a source replaces its contribution rather than accumulating historical classes.
  4. The diagram shows the retained utility declaration; the theme dependency is described separately rather than reproduced as a complete stylesheet.

These session operations are available through MasterCSSScanner from @master/css-tooling/scanner/node. They describe source ownership; integration users normally rely on their build plugin to perform them.

OperationOwnership effect
collectCandidatesPure extraction; does not retain source contributions
scanSourceReplaces one source's content, including an empty string
removeSourceRemoves that source and its virtual descendants
reconcileSourcesSupplies the complete next source set for one owner
registerNativeClassesRegisters native class identities for a stylesheet source
removeOwnerRemoves that owner's sources and native registrations
await scanner.scanSource('page.mdx', content, { owner: 'pages' })await scanner.scanSource('page.mdx#live', html, {  owner: 'pages', parentSource: 'page.mdx', kind: 'html'})scanner.removeSource('page.mdx', { owner: 'pages' })

Removing page.mdx also removes its virtual example. References from another owner remain independent. A reconciliation retains only the inputs supplied for that owner; explicitly retained virtual inputs remain in that set even if their parent is managed externally.

changed indicates a change to generated output or pruning inputs. sourceChanged also captures provenance-only updates: a second source can add ownership without changing CSS. The sources view exposes the current successful source set, including empty inputs and virtual relationships.

Updating a source replaces its contribution

The repeated filename identifies one source. This example has no other sources or safelists retaining the old class.

Text description
  1. Initially card.html contributes p-sm and r-md. Its next content contributes p-lg and r-md.
  2. Rescanning the same source replaces its contribution: r-md remains, p-lg is added and p-sm is released.
  3. Another current source or safelist could still retain p-sm. Source updates do not accumulate all historically seen classes.

Runtime values

A value can change without changing its class name. A complete native declaration class can refer to a custom property supplied by markup or script:

<h1 class="(font-size:var(--headline-size))" style="--headline-size: 2rem">Launch notes</h1>

The scanner sees one complete class. Updating --headline-size changes the browser's value without a new utility rule. See the working value example.

The same contract applies to (width:var(--progress)): script can call element.style.setProperty('--progress', '40%') while the class stays unchanged.

If the class name itself first exists after startup, a static build cannot discover it. Supply a bounded safelist or use runtime or progressive rendering.

Candidate policy

DirectivePurposeLimit
@safelistRetains bounded external classes, such as CMS states or widget classesDoes not bypass matching or define missing utilities
@blocklistPrevents false-positive candidates from generating CSSDoes not rewrite the source or remove arbitrary native CSS

For class patterns, precedence and native usage, see Scanning directives. For removal of native rules and keyframes, see Native CSS pruning. Candidate discovery and native preservation are separate policies.

For a missing style, inspect the complete class, the file's discovery boundary and its candidate policy before changing authoring syntax. The Guide diagnosis checklist walks through those checks in context.