カスケードとリセット
リセットの選択、生成レイヤー、レイヤーのないユーティリティ、決定的なルール順序を説明します。
ページに合うリセットを選ぶ
ドキュメントの chrome は引き続き Tailwind CSS を使います。分離サンプルに入るのはコンパイル済み zudo-wind CSS とブラウザー標準のスタイルだけです。
| リセット | 選ぶ場面 | 残るブラウザー標準のスタイル |
|---|---|---|
none | アプリがすでに基準スタイルを持つ | 見出しサイズ、リストマーカー、body の余白、ネイティブコントロール |
minimal-v1 | ボックスサイズ、body の余白、コントロールのフォントを揃える | 見出しサイズ、リストマーカー、ほかの UA スタイル |
owned-v1 | zudo-wind が管理する小さなリセットを使う | フォーカス枠とネイティブ外観 |
同じ HTML を 3 種類の設定でコンパイルします。補助 CSS は加えず、見出し、リスト、余白、コントロールの差を見えるようにします。プロジェクトでは設定済みのエントリーをコンパイルし、build のソースプランを次のように監査します。
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">この例の設定
{
"wind": {
"spec": 1,
"reset": "none",
"strict": true,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"authoredClasses": {},
"safelist": {
"docs-preview": [
"underline"
]
}
}
}表示用の補助 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">この例の設定
{
"wind": {
"spec": 1,
"reset": "minimal-v1",
"strict": true,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"authoredClasses": {},
"safelist": {
"docs-preview": [
"underline"
]
}
}
}表示用の補助 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">この例の設定
{
"wind": {
"spec": 1,
"reset": "owned-v1",
"strict": true,
"tokens": {
"spacingUnit": "0.25rem"
},
"breakpoints": {},
"defaultTransitionTimingFunction": "ease",
"authoredClasses": {},
"safelist": {
"docs-preview": [
"underline"
]
}
}
}表示用の補助 CSS
カスケードの同点を解決する
ユーティリティはレイヤーなしで出力され、通常の宣言を持つ名前付きレイヤーに勝ちます。レイヤーなしの手書きセレクタとユーティリティの詳細度が同じなら、後に来るルールが配置で決まります。
wind: {
utilities: { placement: "before-authored" }
}レコードでは実際の p-4 と手書きの padding 宣言を比較します。既定ではユーティリティが手書き CSS の後に置かれ、before-authored では手書きルールが後になります。
<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;
}この例の設定
{
"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"
]
}
}
}表示用の補助 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;
}この例の設定
{
"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"
]
}
}
}表示用の補助 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;
}この例の設定
{
"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"
]
}
}
}表示用の補助 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; }配置が変えるのはソース順であり、セレクタの詳細度ではありません。group-* と peer-* の詳細度は zudo-wind のままです。関係バリアントの詳細度を参照してください。
技術リファレンス
レイヤーの順序
wind がリセット、トークン、ユーティリティのいずれかのルールを出力するとき、生成されるレイヤーの前置きは次のとおりです。
@layer zw-reset, zw-tokens, zfb-hi, base, components;この前置きの後に、手書きのレイヤー順序宣言が追加されます。列挙された 5 つのレイヤーの前後関係は変更できません。ほかのレイヤー名は通常の CSS の最初の宣言順に従います。import したスタイルシートが暗黙にレイヤーで包まれることはなく、明示的な import レイヤーは有効です。
統合されたスタイルシートの順序は次のとおりです。
生成されるレイヤーの前置き。
巻き上げられた外部 import。
@layer zfb-hi内のハイライトブロック。生成されるリセットとトークンのレイヤー。
必要な場合の生成される
@property登録。手書きのバンドル CSS。import はバンドラーが処理済みです。
レイヤーのない生成ユーティリティルール。
パイプラインの追加位置に置かれる CSS Modules の出力。
wind.utilities.placement: "before-authored" では 6 と 7 が入れ替わります。ユーティリティの配置を参照してください。
前置きは手書きのレイヤー接頭辞より前に置かれるため、手書きの CSS は所有された順序を置き換えられません。wind のユーティリティにはレイヤーがなく、既定では手書きのグローバルルールの後に続きます。そのため、名前付きレイヤーの通常の宣言より優先されます。ただし、レイヤーのない手書きのルールで詳細度が高ければそちらが優先されます。レイヤーのない手書きのルールとユーティリティの詳細度が同じなら、後にあるほうが優先されます。既定ではユーティリティ、before-authored の配置では手書きのルールです。important 宣言には通常の CSS レイヤー規則が適用されますが、wind 言語に important 修飾子はありません。
wind の結果が完全に空なら、前置きも出力しません。リセットを選ぶと、候補が解決されなくても前置きとリセットを出力できます。
ユーティリティの配置
wind.utilities.placement は、レイヤーのないユーティリティ規則を手書きのグローバル CSS に対してどこに置くかを選びます。
wind: {
utilities: { placement: "before-authored" }
}| 値 | 6 と 7 の順序 | 詳細度が同じ場合 |
|---|---|---|
"after-authored"(既定) | 手書きのグローバル CSS、次にユーティリティ | ユーティリティが優先 |
"before-authored" | ユーティリティ、次に手書きのグローバル CSS | 手書きのルールが優先 |
ほかは何も移動しません。前置き、外部 import、ハイライトブロック、リセット、トークン、@property 登録、CSS Modules の出力は同じ位置のままで、ユーティリティがレイヤーで包まれることもありません。外部の @import はどちらの場合も両方の段階より上に巻き上げられます。この設定は zfb build、zfb dev、zfb css のすべてに適用され、dev で変更すると配信中のスタイルシートが再生成されます。
配置が決めるのは詳細度が同じ場合の勝敗だけです。セレクターは変わらないため、Tailwind の group-* と peer-* のセレクターの詳細度は戻りません。関係バリアントを参照してください。
リセットの選択
Tailwind 4.2.0 preflight との測定済み差分と手書き CSS の補完例は、Tailwind preflight との違いを参照してください。
デフォルトの none はリセットのバイト列を出力しません。minimal-v1 に含まれる宣言は、正確に次のものだけです。
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
margin: 0;
}
button,
input,
optgroup,
select,
textarea {
font: inherit;
}owned-v1 は完全なリセットで、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;
}どちらのリセットも、ブラウザ標準のフォーカスアウトラインと appearance は変更しません。[hidden] に表示ルールを強制することもありません。接尾辞のない border ユーティリティは、どのリセットを選んでも自身で実線のボーダーを設定します。
決定的な順序
ルールは次のタプルの昇順に並びます。
(responsive_rank, dark_rank, pointer_rank, relation_rank, state_rank, pseudo_rank,
conflict_group_rank, scope_rank, catalog_rank, raw_candidate_utf8_bytes)基本のレスポンシブ順位は 0 です。設定されたブレークポイントを幅の昇順に並べ、各ブレークポイントの最小幅の順位を 2*i + 1、最大幅の順位を 2*i + 2 とします。i はゼロから始まります。ダークの順位は 0 または 1 です。pointer の順位は指定なしが 0、coarse が 1、fine が 2 です。関係、状態、疑似要素の順位はバリアントの順序に従います。競合グループはカタログ表の順序で 1 から始まります。スコープ順位は広域が 0、軸が 1、辺や角が 2 です。単一プロパティの群は 0 を使います。カタログの下位項目は記載順、次に基部のバイト列で並び、最後に候補の生の UTF-8 バイト列で同順位を解決します。これは出力ソースの順序であり、セレクタの詳細度を置き換えるものではありません。
公開 Rust SortKey は pointer_rank を持ち、explain JSON は sortTuple.pointerRank、テキストの sort 行は pointer= を表示します。
HTML のクラス順は影響しません。spacingUnit が 0.25rem なら、p-4 px-2 pl-1 のどの並びでも、上下は 1rem、右は 0.5rem、左は 0.25rem になります。margin にも同じ規則が適用されます。gap-4 gap-x-2 gap-y-1 は列間隔 0.5rem と行間隔 0.25rem を生成します。同じスコープの順位は候補の生のバイト列で決まり、p-2 は p-10 より後に並びます。
出力バイト列
v1 の生成 CSS は開発時も本番時も圧縮されません。最終的なスタイルシートのミニファイもありません。プリンターは LF、2 スペースのインデント、1 行 1 宣言、最後の LF を使います。入力が同じなら、どちらのモードでも wind CSS は同一です。CSS Modules は本番時に別途圧縮されることがあります。