zfb
GitHub リポジトリ

検索したい単語を入力

いつでも検索バーを開ける

カスケードとリセット

リセットの選択、生成レイヤー、レイヤーのないユーティリティ、決定的なルール順序を説明します。

ページに合うリセットを選ぶ

ドキュメントの chrome は引き続き Tailwind CSS を使います。分離サンプルに入るのはコンパイル済み zudo-wind CSS とブラウザー標準のスタイルだけです。

リセット選ぶ場面残るブラウザー標準のスタイル
noneアプリがすでに基準スタイルを持つ見出しサイズ、リストマーカー、body の余白、ネイティブコントロール
minimal-v1ボックスサイズ、body の余白、コントロールのフォントを揃える見出しサイズ、リストマーカー、ほかの UA スタイル
owned-v1zudo-wind が管理する小さなリセットを使うフォーカス枠とネイティブ外観

同じ HTML を 3 種類の設定でコンパイルします。補助 CSS は加えず、見出し、リスト、余白、コントロールの差を見えるようにします。プロジェクトでは設定済みのエントリーをコンパイルし、build のソースプランを次のように監査します。

zfb css --input src/styles.css --output dist/app.css --project-root .
zfb wind audit --plan build
リセットなし: ブラウザー標準
HTML
<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>
CSS
@layer zw-reset, zw-tokens, zfb-hi, base, components;
@layer zw-tokens {
  :root {
    --zw-spacing-unit: .25rem;
  }
}

.underline {
  text-decoration-line: underline;
}
Head
<base href="about:srcdoc">
この例の設定
{
  "wind": {
    "spec": 1,
    "reset": "none",
    "strict": true,
    "tokens": {
      "spacingUnit": "0.25rem"
    },
    "breakpoints": {},
    "defaultTransitionTimingFunction": "ease",
    "authoredClasses": {},
    "safelist": {
      "docs-preview": [
        "underline"
      ]
    }
  }
}
表示用の補助 CSS
最小リセット: 管理対象の基本
HTML
<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>
CSS
@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;
}
Head
<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
所有リセット: 見出しとリストを正規化
HTML
<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>
CSS
@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;
}
Head
<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 では手書きルールが後になります。

レイヤーなしのユーティリティは通常のレイヤールールに勝つ
HTML
<div class="wind-demo-stage"><div class="wind-demo-box p-4"><p>Padding from an unlayered utility</p></div></div>
CSS
@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; }
既定の配置: 同じ詳細度ではユーティリティが勝つ
HTML
<div class="wind-demo-stage"><div class="wind-demo-box p-4"><p>Padding from the later rule</p></div></div>
CSS
@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; }
before-authored: 同じ詳細度では手書きルールが勝つ
HTML
<div class="wind-demo-stage"><div class="wind-demo-box p-4"><p>Padding from the later rule</p></div></div>
CSS
@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 レイヤーは有効です。

統合されたスタイルシートの順序は次のとおりです。

  1. 生成されるレイヤーの前置き。

  2. 巻き上げられた外部 import。

  3. @layer zfb-hi 内のハイライトブロック。

  4. 生成されるリセットとトークンのレイヤー。

  5. 必要な場合の生成される @property 登録。

  6. 手書きのバンドル CSS。import はバンドラーが処理済みです。

  7. レイヤーのない生成ユーティリティルール。

  8. パイプラインの追加位置に置かれる 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 は本番時に別途圧縮されることがあります。

関連項目

Revision History

作成更新