Diagnostics and tools
Inspect candidate outcomes, source-aware diagnostics, audits, and the standalone CSS command.
Triage a finding
The CLI diagnostics described here come from zudo-wind.
Use the build plan to audit the files and virtual sources compiled by zfb build and zfb dev. Add an exit threshold when the audit is part of a migration check:
zfb wind audit --plan build --fail-on warning
zfb wind explain --config zfb.config.ts bg-panelAudit is informational by default: reported errors do not change the exit status unless you pass --fail-on error or --fail-on warning. explain follows one candidate through parsing, token lookup, selector, and ordering.
ZW014 depends on where a candidate came from. A foreign utility in a class attribute warns by default and becomes an error at that position with strict mode. The same text in an unrelated literal remains audit information; a safelist or manifest entry is always strict.
wind: {
strict: true
}These typed source records show the warning-to-error change:
<p class="line-clamp-2">Two-line label</p>Expected diagnostics: ZW014 (warning)
Configuration for this example
{
"wind": {
"spec": 1,
"reset": "none",
"strict": false,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"authoredClasses": {},
"safelist": {}
}
}Authored demonstration CSS
<p class="line-clamp-2">Two-line label</p>Expected diagnostics: ZW014 (error)
Configuration for this example
{
"wind": {
"spec": 1,
"reset": "none",
"strict": true,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"authoredClasses": {},
"safelist": {}
}
}Authored demonstration CSS
Correct a missing token or spacing unit, or reserve the exact full spelling when authored CSS owns it. For example, p-4 needs tokens.spacingUnit; text-link needs a matching color or font-size token unless it is authored:
<div class="p-4">p-4 requires tokens.spacingUnit</div>Expected diagnostics: ZW006 (error)
Configuration for this example
{
"wind": {
"spec": 1,
"reset": "none",
"strict": true,
"tokens": {},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"authoredClasses": {},
"safelist": {
"docs-preview": [
"p-4"
]
}
}
}Authored demonstration CSS
An ordinary authored name such as site-header needs no reservation because it does not start with a catalog utility root. These diagnostic records show source and expected severity, not runnable previews.
Technical reference
Four candidate outcomes
A candidate has one of four results:
| Outcome | Result |
|---|---|
| Resolved utility | Emit its generated rule. |
| Recognized but invalid candidate | Report a diagnostic. Strict origins fail the pass; lower-confidence source text is audit information. |
| Ordinary class or marker | Emit no utility rule. An accepted group or peer marker carries metadata but has no CSS. |
| Malformed, unsupported, or unknown explicit utility input | Fail the pass with a diagnostic. |
An unrelated name such as site-header in a class attribute remains ordinary CSS-class text because it has no utility root. When a name starts with a catalog root, resolution chooses the longest matching root before checking its suffix. For example, text-link is checked as a text utility and receives ZW006 when neither a matching color nor font-size token is configured; similarly, authored names such as bg-panel, border-soft, or font-title need to be reserved if CSS owns them. This strict classification keeps misspelled or unconfigured utilities visible instead of guessing that every unknown name is an ordinary class. Low-confidence complete literals can contribute valid rules, but invalid ones do not fail a build. Explicit safelist and manifest entries are strict.
Diagnostic codes
| Code | Meaning and severity |
|---|---|
| ZW001 | Candidate syntax. Error for class, safelist, or strict manifest origins; audit-only information for other literals. An underscore in an authored name such as card__title or card_title is valid; a single _ directly after a utility root, such as p_4, stays ZW001 and names wind.authoredClasses. |
| ZW002 | Unknown or unconfigured variant. Uses the same origin-aware severity as ZW001. |
| ZW003 | Duplicate or noncanonical variant chain. A sortable unique chain may include a suggestion. |
| ZW004 | Deliberately unsupported language feature. |
| ZW005 | Invalid value, modifier, or negative. |
| ZW006 | Missing token, spacing unit, or default. |
| ZW007 | Invalid config, token schema, or token value; validation fails. |
| ZW008 | Unknown explicit utility; safelist and manifest entries fail, while ordinary source classes are not errors. |
| ZW009 | Leftover stylesheet directive or legacy value function; always an error. |
| ZW010 | Required source or manifest is missing, unreadable, or invalid; error. |
| ZW011 | Scanned source is not UTF-8; warning, skip the file, and remove its old contribution. |
| ZW012 | Dynamic candidate construction; audit-only information, no utility is invented. |
| ZW013 | Property conflict inside one class-list literal; audit-only information explains the deterministic winner. |
| ZW014 | Unsupported foreign utility from the versioned migration vocabulary, such as line-clamp-2 or container. No CSS is generated. Warning at a class position, error there under wind.strict: true, audit-only information for other literals, and an error from a safelist or manifest. |
| ZW015 | Audit-only information when a host color or other token value replaces a different preset value. Build remains silent. The config key path and changed old/new values identify the origin. |
| ZW016 | Audit-only information from zfb wind audit when a fallback-less var() references a Tailwind default-theme variable that no scanned stylesheet declares; a warning under wind.strict: true. This heuristic scans the entry stylesheet and discovered imports only; CSS Modules, linked stylesheets, inline styles, and unresolved imports are outside its scan, and wind: false does not report ZW016. Any declaration in the scanned set suppresses the row. Never emitted by zfb css or build. |
Diagnostic records retain the candidate where available, severity, code, origin, message, optional suggestion, and optional rejection ID. Origins are structured by kind:
| Origin | Location data |
|---|---|
| Source | Stable source ID, UTF-8 byte offset and length, one-based line and byte-column, enclosing literal span, and whether the position is a class or literal. |
| Safelist | Owner ID and array index. |
| Manifest | Producer ID, path, and candidate index. |
| Config | Configuration key path. |
| Role class | Role key. |
| Stylesheet | Path and source span. |
Occurrences are retained and ordered by origin, offset or key/index, code, and candidate bytes.
Reading diagnostic output
zfb build, zfb dev, and zfb css print one diagnostic per line as source:line:column: CODE candidate: message. The line is one-based, and the column is a one-based byte column, so a non-ASCII character earlier on the line counts by its UTF-8 bytes; a CRLF line ending counts as one line break. A candidate without a source position names its owner instead, such as node_ or wind.safelist.app[0]. When errors stop a pass, every distinct error is listed: the same candidate at two places is reported twice, and an exact repeat of one place once. Every leftover directive in the entry stylesheet and its imports is reported in the same run.
zfb wind audit uses the same source:line:column locations; the byte offset appears only as byteOffset in --json output. A configuration error names the failing field and shows its value, bounded, instead of the whole evaluated configuration; set ZFB_DEBUG_CONFIG=1 to print all of it.
Candidate extraction skips string literals that cannot be classes, for build and audit alike: module specifiers in import, export … from, import(), and require(); literals that are only a URL or a relative path; the values of href, src, content, name, rel, and type attributes; and the CSS text of a <style> element. Class attributes and class helpers are read; other eligible literals can also contribute complete candidates, including template constants not provably CSS.
Speculative literal positions discard tokens containing =, quotes, comma, +, *, @, %, or # outside balanced […] or (…). Unicode text and punctuation inside balanced arbitrary CSS values remain eligible. An explicit class position still reports malformed candidates. When a literal parses as embedded markup, its recognized class attributes are extracted once and its raw whitespace-token pass is skipped. Pure URL or relative-path fragments beside concatenation or interpolation remain skipped.
ZW001 through ZW006 are errors at class, safelist, and strict manifest origins. At lower-confidence source positions they are audit-only information; a valid complete candidate found there still emits. If a utility-root name is intended as an authored class, add its exact complete spelling to wind.authoredClasses, for example authoredClasses: { "text-link": true }. This reservation suppresses utility validation, candidate diagnostics, and generated CSS for that full candidate. Otherwise, correct the utility spelling or declare the missing token. A name with no matching utility root, such as site-header, remains ordinary class text.
ZW013 omits catalog-declared implicit defaults overridden in the same variant bucket: border width plus border style, outline width plus outline style, and transition plus duration or ease. Genuine style/style, duration/duration, transition-property/property, sr-only/padding, and side/shorthand width conflicts still appear. Compatible translate-x-* and translate-y-* utilities in the same variant bucket also share their catalog composition declaration without a conflict. Different values on the same axis and any other overlapping properties still produce ZW013.
For an undeclared color with a valid /–/ modifier, text-<name>, border-<name>, border-x-<name>, outline-<name>, and placeholder:text-<name> report ZW006/R17 for the missing token. Invalid opacity, negative, and unsupported slash forms retain ZW005.
A declared color used as bare shadow-<name> or valid shadow-<name>/<opacity> (/–/) reports ZW004/R20 with a targeted hint because v1 does not compose colors into shadow values. The shadow root accepts complete values: none, arbitrary box-shadow values, or shadows tokens. Fold the tint into a shadows token, or author the complete box-shadow in CSS and reserve the class with wind.authoredClasses. A bare same-name shadows token still resolves; slash modifiers remain unsupported.
Well-formed has-[…]:, not-[…]:, nth-[…]:, nth-last-[…]:, and supports-[…]: report ZW004/R09. min-[…]: and max-[…]: report ZW004/R24 and require configured wind.breakpoints; group-aria-[…]:, group-data-[…]:, peer-aria-[…]:, and peer-data-[…]: report ZW004/R08. Empty, trailing, sequential, malformed, unbalanced, and unknown bracketed heads remain ZW001. A rejected functional variant wrapping a ZW014 utility can report both diagnostics for the same complete candidate and source span in explain and audit, without emitting CSS; for example min-[56rem]:table-cell or has-[:focus-visible]:filter-[blur(2px)]. Explain remains invalid. Strict mode raises a source class ZW014 warning to error; lower-confidence literals stay audit information and exact authored classes suppress both diagnostics.
wind explain --json optionally adds tokenResolutions[*].hostOverride with presetIndex, previousValue, and finalValue only for changed preset bindings. wind audit --json emits ZW015 as a standard diagnostic with severity: "auditInfo", category/name candidate, config key-path origin, and old/new values in the message. Both CLI envelopes retain schemaVersion: 1.
Migration warnings and strict mode
zudo-wind does not implement every Tailwind utility. A bounded, versioned migration vocabulary names the common Tailwind utilities it deliberately leaves out: table and the other table display values, contents, flow-root, list-item, container, not-sr-only, line-clamp-*, order-*, basis-*, fill-*, stroke-*, backdrop-blur-*, appearance-*, will-change-*, and content-*. Such a name is reported as ZW014, an unsupported foreign utility, rather than silently passing as ordinary class text. The diagnostic names the declaration to author instead; no guessed CSS is ever emitted. A name that only resembles a foreign utility, such as table-wrapper or order-summary, stays ordinary.
Migration vocabulary v4 also recognizes logical ms-*/me-*, ps-*/pe-*, start-*/end-*, and border-s/border-e (including valid arbitrary suffixes), plus isolation, float/clear, break/word-break, transform origin, filter/backdrop-filter, and text-shadow. Author the corresponding logical margin, padding, inset, and border declarations or other CSS yourself; no composed CSS is generated. A supported configured token still resolves first. border-s-[3px] and border-e-[red] report ZW014, while unknown extra suffixes such as ml-zzz-[3px] report ZW005.
The v4 gradient vocabulary includes v3 bg-gradient-to-*; bg-linear-to-*, integer-angle bg-linear-*, bg-radial and bg-radial-[<value>], bg-conic and integer-angle bg-conic-*; and from-*, via-*, to-* stops using a declared color, current, transparent, or a bracketed arbitrary color or position. These produce no CSS: author background-image and reserve each complete class in wind.authoredClasses. Bare from-10%, via-30%, and to-90% are currently rejected by the candidate parser; bracketed from-[60%] reaches ZW014.
ZW014 is a warning by default, so a migrating project still builds while it lists what lost its styling. Set wind.strict: true to make it an error at class positions. Strict mode never promotes a lower-confidence literal: an eligible complete candidate in a constant that never reaches a class attribute or in a test title stays audit-only information. Pure URLs are skipped. To keep a name because CSS you wrote styles it, reserve the exact spelling with wind.authoredClasses.
zfb wind explain reports such a name with the outcome unsupported foreign utility.
The public Rust migration classifier is foreign_family(candidate, tokens): it needs validated tokens to let a configured utility resolve before ZW014. The Rust catalog metadata also exposes DefaultOverride and CatalogEntry.default_overrides for the ZW013 implicit-default exceptions. The catalog and language remain at spec revision 12; serialized CLI schema versions remain 1. CSS output is unchanged by this metadata addition.
Explain and audit
Run zfb wind explain to inspect how a candidate is parsed and resolved. Explain exposes the catalog entry, language version and revision, token bindings, any category-unverified value, declarations, selector, conditions, and complete sort tuple. With wind: false, it returns a disabled outcome without generating CSS.
Run zfb wind audit to inspect ordinary class-position names, missing or dead utilities, dynamic fragments, property conflicts within an enclosing class-list literal, and fallback-less references to undeclared Tailwind default-theme variables (ZW016; see Default theme variables). It excludes markers and exact authored-class allowlist entries. Conflicts are compared within one literal, not between unrelated elements. With wind: false, audit reports generation disabled and an empty candidate report.
--plan standalone (the default) audits the same roots as zfb css without --source: the conventional project roots plus configured wind.sources.roots, package roots, and exclusions. --plan build audits the plan zfb build and zfb dev scan. It runs plugin setup to discover package-owned routes, sibling mirror roots, and plugin virtual modules, which are audited as plugin/<specifier>. It runs no preBuild or postBuild hook and writes no build output. Either mode first prints wind audit plan: <mode> followed by every scanned root, virtual module, manifest, and exclusion with its reason.
By default, a completed audit exits with status 0 even when it reports error-severity findings; stderr then recommends --fail-on error. --fail-on error fails on error-severity diagnostics, while --fail-on warning also fails on warnings. auditInfo findings do not affect either threshold. Invalid configuration and source scan failures always exit nonzero.
Use --group for a compact text audit. It groups unrecognized names by candidate and dead-class/diagnostic occurrences by candidate, code, and severity, showing a count and deterministic first origin. Diagnostic summaries keep the message and optional suggestion from that first origin. The flag leaves default text and exit policy unchanged; with --json it has no effect, preserving JSON occurrence arrays. See the CLI reference.
codeHighlight.roleClasses is deliberately tolerant: a valid utility emits; an invalid or unknown role value remains an ordinary class without a build diagnostic. Audit may report that role as ignored. An exact match in authoredClasses suppresses utility validation, generation, and utility diagnostics at every candidate origin. It cannot suppress configuration errors or stylesheet checks.
Leftover directives and CSS command
The compiler rejects legacy engine imports and directives, including @tailwind, @theme with any modifier, @source, @custom-variant, @apply, @utility, @variant, @plugin, @config, and @reference. Imports of tailwindcss and its subpaths are rejected too. The old value functions theme(), --spacing(), --alpha(), and --value() in declaration values are also errors. Diagnostics name the spelling and stylesheet file and span and link to Coming from Tailwind.
The scanner ignores comments and string contents, decodes escaped CSS identifiers and import strings, and checks the authored stylesheet and every imported stylesheet before trying to resolve a forbidden import. This check still runs when wind is false. Ordinary @layer and imports that explicitly name a layer remain valid.
The standalone zfb css command remains the wind compiler. When generation is enabled, --source and --no-auto-source retain their source-discovery meaning, and highlight flags retain their highlighting role. With wind: false, the command processes authored CSS and highlighting without wind generation. The separate zfb wind explain and zfb wind audit commands provide candidate inspection and source audit.
Generated identity
Every generated reset, token, registration, and utility rule uses the virtual source zudo-. The source record identifies language version 1 and revision 12. V1 creates no generated source map and promises no line mapping. Candidate-to-source locations are retained in rule metadata and diagnostics instead.