Cascade and reset
Understand reset choices, generated layers, unlayered utilities, and deterministic rule ordering.
Choose a reset for the page
The documentation chrome still uses Tailwind CSS. These isolated samples contain compiled zudo-wind CSS and browser-native styles only.
| Reset | Choose it when | Browser defaults that remain |
|---|---|---|
none | Your app already owns a baseline | Heading sizes, list markers, body margin, native controls |
minimal-v1 | You need box sizing, a flush body, and inherited control fonts | Heading sizes, list markers, other UA styles |
owned-v1 | You want the compact reset zudo-wind owns | Focus outlines and native appearance |
The same HTML is compiled three times. No scaffold CSS is added, so heading, list, margin, and control differences remain visible. In your project, compile the configured entry and audit the build plan with:
zfb css --input src/styles.css --output dist/app.css --project-root .
zfb wind audit --plan build<main id="task-list"><h2>Task list</h2><ul><li>Review source roots</li><li>Check the manifest</li></ul><form><label><input type="checkbox"> Notify the team</label><button type="button">Save</button></form><p><a href="#task-list" class="underline">Open task details</a></p></main>@layer zw-reset, zw-tokens, zfb-hi, base, components;
@layer zw-tokens {
:root {
--zw-spacing-unit: .25rem;
}
}
.underline {
text-decoration-line: underline;
}<base href="about:srcdoc">Configuration for this example
{
"wind": {
"spec": 1,
"reset": "none",
"strict": true,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"authoredClasses": {},
"safelist": {
"docs-preview": [
"underline"
]
}
}
}Authored demonstration CSS
<main id="task-list"><h2>Task list</h2><ul><li>Review source roots</li><li>Check the manifest</li></ul><form><label><input type="checkbox"> Notify the team</label><button type="button">Save</button></form><p><a href="#task-list" class="underline">Open task details</a></p></main>@layer zw-reset, zw-tokens, zfb-hi, base, components;
@layer zw-reset {
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
}
button,
input,
optgroup,
select,
textarea {
font: inherit;
}
}
@layer zw-tokens {
:root {
--zw-spacing-unit: .25rem;
}
}
.underline {
text-decoration-line: underline;
}<base href="about:srcdoc">Configuration for this example
{
"wind": {
"spec": 1,
"reset": "minimal-v1",
"strict": true,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"authoredClasses": {},
"safelist": {
"docs-preview": [
"underline"
]
}
}
}Authored demonstration CSS
<main id="task-list"><h2>Task list</h2><ul><li>Review source roots</li><li>Check the manifest</li></ul><form><label><input type="checkbox"> Notify the team</label><button type="button">Save</button></form><p><a href="#task-list" class="underline">Open task details</a></p></main>@layer zw-reset, zw-tokens, zfb-hi, base, components;
@layer zw-reset {
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
border-width: 0;
border-style: solid;
border-color: currentColor;
}
html {
line-height: 1.5;
font-family: ui-sans-serif, system-ui, sans-serif;
-webkit-text-size-adjust: 100%;
tab-size: 4;
}
body {
line-height: inherit;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
a {
color: inherit;
text-decoration: inherit;
}
b,
strong {
font-weight: bolder;
}
code,
kbd,
samp,
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 1em;
}
ol,
ul,
menu {
list-style: none;
}
button,
input,
optgroup,
select,
textarea {
font: inherit;
letter-spacing: inherit;
color: inherit;
}
button,
input:where([type="button"], [type="reset"], [type="submit"]) {
background-color: transparent;
background-image: none;
}
textarea {
resize: vertical;
}
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block;
vertical-align: middle;
}
img,
video {
max-width: 100%;
height: auto;
}
table {
border-collapse: collapse;
border-color: inherit;
text-indent: 0;
}
summary {
display: list-item;
}
hr {
height: 0;
border-top-width: 1px;
}
}
@layer zw-tokens {
:root {
--zw-spacing-unit: .25rem;
}
}
.underline {
text-decoration-line: underline;
}<base href="about:srcdoc">Configuration for this example
{
"wind": {
"spec": 1,
"reset": "owned-v1",
"strict": true,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"authoredClasses": {},
"safelist": {
"docs-preview": [
"underline"
]
}
}
}Authored demonstration CSS
Resolve a cascade tie
Utilities are unlayered and beat normal declarations in named layers. For an unlayered authored selector and utility with equal specificity, placement decides which rule comes later:
wind: {
utilities: { placement: "before-authored" }
}The records compare an actual p-4 rule with an authored padding declaration. The default puts the utility after authored global CSS; before-authored puts the authored rule last:
<div class="wind-demo-stage"><div class="wind-demo-box p-4"><p>Padding from an unlayered utility</p></div></div>@layer zw-reset, zw-tokens, zfb-hi, base, components;
@layer zw-tokens {
:root {
--zw-spacing-unit: .25rem;
}
}
.wind-demo-stage {
max-width: 24rem;
padding: 1rem;
background: #f1f5f9;
}
@layer components {
.wind-demo-box {
padding: .5rem;
border: 1px solid #94a3b8;
background: #fff;
}
}
.wind-demo-box p {
margin: 0;
}
.p-4 {
padding-top: 1rem;
padding-right: 1rem;
padding-bottom: 1rem;
padding-left: 1rem;
}Configuration for this example
{
"wind": {
"spec": 1,
"reset": "none",
"strict": true,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"utilities": {
"placement": "after-authored"
},
"authoredClasses": {
"wind-demo-box": true,
"wind-demo-stage": true
},
"safelist": {
"docs-preview": [
"p-4"
]
}
}
}Authored demonstration CSS
.wind-demo-stage { max-width: 24rem; padding: 1rem; background: #f1f5f9; }
@layer components { .wind-demo-box { padding: 0.5rem; border: 1px solid #94a3b8; background: #ffffff; } }
.wind-demo-box p { margin: 0; }<div class="wind-demo-stage"><div class="wind-demo-box p-4"><p>Padding from the later rule</p></div></div>@layer zw-reset, zw-tokens, zfb-hi, base, components;
@layer zw-tokens {
:root {
--zw-spacing-unit: .25rem;
}
}
.wind-demo-stage {
max-width: 24rem;
padding: 1rem;
background: #f1f5f9;
}
.wind-demo-box {
padding: .5rem;
border: 1px solid #94a3b8;
background: #fff;
}
.wind-demo-box p {
margin: 0;
}
.p-4 {
padding-top: 1rem;
padding-right: 1rem;
padding-bottom: 1rem;
padding-left: 1rem;
}Configuration for this example
{
"wind": {
"spec": 1,
"reset": "none",
"strict": true,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"utilities": {
"placement": "after-authored"
},
"authoredClasses": {
"wind-demo-box": true,
"wind-demo-stage": true
},
"safelist": {
"docs-preview": [
"p-4"
]
}
}
}Authored demonstration CSS
.wind-demo-stage { max-width: 24rem; padding: 1rem; background: #f1f5f9; }
.wind-demo-box { padding: 0.5rem; border: 1px solid #94a3b8; background: #ffffff; }
.wind-demo-box p { margin: 0; }<div class="wind-demo-stage"><div class="wind-demo-box p-4"><p>Padding from the later rule</p></div></div>@layer zw-reset, zw-tokens, zfb-hi, base, components;
@layer zw-tokens {
:root {
--zw-spacing-unit: .25rem;
}
}
.p-4 {
padding-top: 1rem;
padding-right: 1rem;
padding-bottom: 1rem;
padding-left: 1rem;
}
.wind-demo-stage {
max-width: 24rem;
padding: 1rem;
background: #f1f5f9;
}
.wind-demo-box {
padding: .5rem;
border: 1px solid #94a3b8;
background: #fff;
}
.wind-demo-box p {
margin: 0;
}Configuration for this example
{
"wind": {
"spec": 1,
"reset": "none",
"strict": true,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"utilities": {
"placement": "before-authored"
},
"authoredClasses": {
"wind-demo-box": true,
"wind-demo-stage": true
},
"safelist": {
"docs-preview": [
"p-4"
]
}
}
}Authored demonstration CSS
.wind-demo-stage { max-width: 24rem; padding: 1rem; background: #f1f5f9; }
.wind-demo-box { padding: 0.5rem; border: 1px solid #94a3b8; background: #ffffff; }
.wind-demo-box p { margin: 0; }Placement changes source order, not selector specificity. group-* and peer-* keep their zudo-wind specificity; see relation variant specificity.
Technical reference
Layer order
Whenever wind emits a reset, token, or utility rule, the generated layer prelude is:
@layer zw-reset, zw-tokens, zfb-hi, base, components;Wind appends authored layer-order statements after this prelude. They cannot move the five listed layers ahead of or behind one another. Other layer names follow ordinary CSS first-declaration order. Imported stylesheets are not wrapped in a layer implicitly; an explicit import layer remains valid.
The integrated stylesheet order is:
The generated layer prelude.
Hoisted external imports.
The highlight block in
@layer zfb-hi.Generated reset and token layers.
Generated
@propertyregistrations when needed.Authored bundled CSS, with imports already handled by the bundler.
Unlayered generated utility rules.
CSS Modules output at its pipeline append position.
Items 6 and 7 trade places under wind.utilities.placement: "before-authored"; see Utility placement.
The prelude comes before any authored layer prefix, so authored CSS cannot replace the owned order. Wind utilities are unlayered and, by default, follow authored global rules. They therefore beat normal declarations in named layers. An unlayered authored rule with greater specificity still wins. Between an unlayered authored rule and a utility of equal specificity, the one that comes later wins: the utility by default, the authored rule under before-authored placement. Important declarations follow normal CSS layer rules, but there is no important modifier in the wind language.
An entirely empty wind result emits no prelude. A selected reset can emit the prelude and reset even when no candidates resolve.
Utility placement
wind.utilities.placement chooses where the unlayered utility rules sit relative to authored global CSS:
wind: {
utilities: { placement: "before-authored" }
}| Value | Order of items 6 and 7 | Equal-specificity tie |
|---|---|---|
"after-authored" (default) | Authored global CSS, then utilities | The utility wins |
"before-authored" | Utilities, then authored global CSS | The authored rule wins |
Nothing else moves. The prelude, external imports, highlight block, reset, tokens, @property registrations, and CSS Modules output keep their places, and no utility is wrapped in a layer. External @import rules are hoisted above both stages either way. The setting applies to zfb build, zfb dev, and zfb css alike; editing it in dev rebuilds the served stylesheet.
Placement only settles ties of equal specificity. It does not change any selector, so it does not restore the specificity of Tailwind's group-* and peer-* selectors; see Relation variants.
Reset choices
For measured differences from Tailwind 4.2.0 preflight and an authored parity block, see Differences from Tailwind preflight.
The default is none, which emits no reset bytes. minimal-v1 contains exactly these declarations:
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
}
button,
input,
optgroup,
select,
textarea {
font: inherit;
}owned-v1 is a complete reset, not an extension of minimal-v1:
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
border-width: 0;
border-style: solid;
border-color: currentColor;
}
html {
line-height: 1.5;
font-family: ui-sans-serif, system-ui, sans-serif;
-webkit-text-size-adjust: 100%;
tab-size: 4;
}
body {
line-height: inherit;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
a {
color: inherit;
text-decoration: inherit;
}
b,
strong {
font-weight: bolder;
}
code,
kbd,
samp,
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 1em;
}
ol,
ul,
menu {
list-style: none;
}
button,
input,
optgroup,
select,
textarea {
font: inherit;
letter-spacing: inherit;
color: inherit;
}
button,
input:where([type="button"], [type="reset"], [type="submit"]) {
background-color: transparent;
background-image: none;
}
textarea {
resize: vertical;
}
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block;
vertical-align: middle;
}
img,
video {
max-width: 100%;
height: auto;
}
table {
border-collapse: collapse;
border-color: inherit;
text-indent: 0;
}
summary {
display: list-item;
}
hr {
height: 0;
border-top-width: 1px;
}Both resets leave native focus outlines and appearance alone. Neither forces a display rule for [hidden]. Bare border utilities set a solid border themselves under every reset choice.
Deterministic ordering
Rules sort by this ascending tuple:
(responsive_rank, dark_rank, pointer_rank, relation_rank, state_rank, pseudo_rank,
conflict_group_rank, scope_rank, catalog_rank, raw_candidate_utf8_bytes)The base responsive rank is 0. For each configured breakpoint in ascending width order, minimum ranks are 2*i + 1 and maximum ranks are 2*i + 2, with i starting at zero. Dark has rank 0 or 1. Pointer rank is 0 absent, 1 coarse, or 2 fine. Relation, state, and pseudo ranks follow the orders in Variants. Conflict groups use catalog table order starting at 1. Scope ranks are broad 0, axis 1, and side or corner 2; single-property families use 0. Catalog subentries use their listed order, then root bytes; raw candidate UTF-8 bytes break the final tie. This is emitted source order; it does not replace selector specificity.
The public Rust SortKey carries pointer_rank; explain JSON exposes sortTuple.pointerRank, and the text sort line includes pointer=.
HTML class order has no effect. With spacingUnit set to 0.25rem, every permutation of p-4 px-2 pl-1 resolves to top and bottom 1rem, right 0.5rem, and left 0.25rem. The same rule applies to margins. gap-4 gap-x-2 gap-y-1 produces a column gap of 0.5rem and row gap of 0.25rem. Same-scope ties use raw candidate bytes, so p-2 sorts after p-10.
Output bytes
V1 generated CSS is not minified in development or production. There is no final stylesheet minifier. The printer uses LF, two-space indentation, one declaration per line, and a final LF. Identical inputs produce identical wind CSS in either mode; CSS Modules may still be minified separately in production.