bodyでページ全体のテキストの既定値を設定し、継承されるプロパティをコンポーネントへ伝え、レイアウトルールを意図的に適用しましょう。このガイドでは、すべての要素に一律のルールを打ち消させることなく、MacでローカルのHTMLページを作成して確認する方法を説明します。

全称ルールはなぜコンポーネントの既定値に干渉するのですか?

全称セレクター*はすべての要素にマッチします。そのため、このルール内の宣言はマッチした各要素に直接適用されます。子孫が継承するための値を提供するだけではありません。また、疑似要素には直接マッチしません。疑似要素には*::beforeのようなセレクターが必要です。全称セレクター。

段落と、その中にネストされたテキスト用のspanを含むコンポーネントのCSS断片を考えてみましょう。

/* Illustrative fragment: assumes a .tile with text descendants. */
* {
  color: #303840;
}

.tile {
  color: #245d75;
}

タイル自体には青い文字色が適用されます。その段落とspanには、全称ルールの暗い色が直接適用されます。通常の継承では、それらの宣言があるためコンポーネントの色は引き継がれません。CSSの既定値決定ルールでは、カスケードによって要素にそのプロパティの値が指定されていない場合、継承されるプロパティは親の計算値を取得します。CSSカスケーディングと継承レベル4、既定値の決定。

*の詳細度はゼロなので、混乱することがあります。詳細度は、適用されるカスケードのオリジンとレイヤー内で、同じ要素に対する競合する宣言を比較します。親に対する宣言は、子にマッチするセレクターではありません。そのため、親セレクターの詳細度を上げても、この継承の問題は解決しません。詳細度。

有効な変更は、全称セレクターのテキスト宣言を削除し、bodyに既定値を設定することです。そうすれば、コンポーネントや要素が別の値を指定するまで、子孫にその値が継承されます。MDNはcolorを継承されるプロパティとして挙げ、継承では親の計算値が引き継がれると説明しています。継承。

既定値はページ、コンポーネント、すべての要素のどこに設定すべきですか?

広範囲に適用するセレクターを書く前に、そのプロパティがテキストのサブツリーに伝わるべきか、単一のボックスを表すものか、あるいはすべてのボックスに意図的なルールを設定するものかを決めましょう。次の判断表は、小規模な自作ページでその違いを示します。

実現したいこと 推奨する設定場所 理由
通常のページテキストで基本の色とタイポグラフィを共有する body 継承によって、テキストに共通の出発点を与える
タイルと通常のテキストで別の色を共有する .tile コンポーネント内に新しい継承値を設定する
1つのキャプションだけ別の色にする .caption 例外を必要とする要素に適用する
1つのパネルにパディング、枠線、背景を設定する パネルのクラス ボックスの装飾をその所有要素に結び付ける
ボックスで一貫したサイズ計算を使う 意図的なbox-sizingルール 明示的なサイズ計算方式を定める
見出しのマージンの既定値を簡単に上書きできるようにする 対象を絞った見出しセレクター。必要に応じて:where()で囲む 既定値を意図した要素に限定する

継承されるかどうかはプロパティごとに異なります。body上の宣言がすべて子孫に届くと思い込まず、プロパティリファレンスのInherited項目を確認しましょう。たとえば、枠線は既定では継承されません。継承。

全称セレクターを使ったサイズ指定が適切な場合もあります。border-boxでは、要素に指定した幅にパディングと枠線が含まれますが、マージンは幅の外側に残ります。これはボックスモデルに関する具体的な選択であり、ページの既定値すべてを全称セレクターで指定する理由にはなりません。box-sizing。

Macで完全なローカルデモを作成する

TextEditをプレーンテキストモードで使うか、既存のコードエディターを使います。TextEditではファイル > 新規を選び、続けてフォーマット > 標準テキストにするを選択します。以下の完全な文書を貼り付け、ファイル > 保存を選び、page-defaults.htmlという名前で保存します。確認画面が表示されたら「.html」を使用を選びます。AppleのTextEdit HTMLガイドに、この手順が説明されています。

この例では、仮のラベルとCSSで描画した図形を使います。スタイルシートを埋め込んだ完全な文書なので、追加で用意するファイルはありません。コードはここで確認済みですが、Macのブラウザーでの表示確認は行っていません。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Page defaults demonstration</title>
  <style>
    *, *::before, *::after {
      box-sizing: border-box;
    }

    body {
      margin: 0;
      font-family: system-ui, sans-serif;
      font-size: 1rem;
      line-height: 1.5;
      color: #303840;
      background-color: #f4f5f6;
    }

    main {
      max-width: 44rem;
      margin: 0 auto;
      padding: 2rem 1rem;
    }

    h1 {
      line-height: 1.2;
    }

    .tile {
      margin: 1.5rem 0;
      padding: 1.25rem;
      border: 2px solid currentColor;
      border-radius: 0.75rem;
      color: #245d75;
      background-color: #ffffff;
    }

    .tile h2 {
      margin-top: 0;
    }

    .shape {
      width: 4rem;
      height: 4rem;
      background-color: currentColor;
    }

    .shape--round {
      border-radius: 50%;
    }

    .caption {
      color: #66523c;
    }
  </style>
</head>
<body>
  <main>
    <h1>Shape notes</h1>
    <p>Page sample: <span>Label A</span>.</p>

    <section class="tile">
      <h2>Tile B</h2>
      <div class="shape shape--round" aria-hidden="true"></div>
      <p>Component sample: <span>Label C</span>.</p>
      <p class="caption">Caption D has its own text color.</p>
    </section>

    <p>Page sample: <span>Label E</span>.</p>
  </main>
</body>
</html>

ブラウザーでpage-defaults.htmlを開きます。ソースをTextEditで再度開くときに、マークアップを一貫して表示したい場合は、TextEdit > 設定 > 開く/保存を選び、HTMLファイルをフォーマットされたテキストではなくHTMLコードとして表示を有効にします。MacのTextEditでHTML書類を操作する。

3つの編集で継承の境界を確認する

この確認手順は、文書を小さな診断演習に変えます。各編集の後にソースを保存し、ブラウザーを再読み込みします。次の編集に進む前に元の文書のコピーを取るか、変更を一つずつ取り消してください。

  1. bodyの色だけを#4b465fに変更します。Label AとLabel Eの周りのテキストが変わるはずです。タイルは独自の色を保ちます。
  2. ページの色を元に戻し、.tileだけをcolor: #526637に変更します。Label Cを含め、通常の見出しと段落のテキストがその変更に従うはずです。Caption Dは明示的に指定された色を保ちます。
  3. タイルの色を元に戻し、スタイルシートの先頭に* { color: #303840; }を一時的に追加します。Label Cを含む段落に、その宣言が直接適用されます。その後、一時的なルールを削除します。

この確認では、コンポーネントが継承値を設定することと、子孫が独自の宣言を受け取ることの違いが分かります。期待されるテキストの挙動は、仕様の継承と既定値の決定ルールに従います。

HTMLページとスタイルシートの間にはどんな関係がありますか?

リソースの読み込みとセレクターの挙動は分けて考えましょう。この例では、CSSをstyle要素内に埋め込んでいます。別のスタイルシートを使う場合は、その要素の中身だけを取り出し、HTMLファイルと同じ場所にpage-defaults.cssというプレーンテキストファイル名で保存します。style要素全体を、head内の次の1行に置き換えます。これは置き換え用の断片であり、別の完全な文書ではありません。

<link rel="stylesheet" href="page-defaults.css">

stylesheetリンクタイプは、文書の表示に使う外部CSSリソースを示します。href内のファイル名は、実際に保存したファイルを指している必要があります。この宣言によってファイルが作成されるわけではありません。HTML標準では、この関係をリンクタイプ: stylesheetで定義しています。

この例での関係は単純です。HTMLは要素とクラスを提供し、CSSはその表示方法を定めます。スタイルシートで.tileの名前を変更する場合は、HTML内の対応するクラスも更新する必要があります。CSSファイルを移動する場合は、そのリソース参照を更新します。どちらの変更でも、セレクターの詳細度を上げる必要はありません。

外部ファイル版でスタイルが適用されない場合は、まずCSSファイルが想定した名前で存在することと、その中身が周囲のHTML文書ではなくCSSであることを確認します。カスケードを変更する前に、埋め込み版に戻してリソース読み込みの間違いを切り分けましょう。

:where()やカスケードレイヤーはいつ使うべきですか?

追加の問題を解決するときに使いましょう。上記の完全な例は、どちらにも依存していません。

:where()を使って詳細度ゼロの対象指定型の既定値を設定する

ページに複数レベルの見出しが増えたとします。次の任意のスタイルシート断片では、通常の見出しセレクターで簡単に上書きできるようにしながら、共通の間隔を設定します。

/* Optional fragment: add to the example stylesheet. */
:where(h1, h2, h3) {
  margin-bottom: 0.75rem;
}

h2 {
  margin-bottom: 1rem;
}

引数を含む:where()セレクター全体の詳細度はゼロです。しかし、*を:where(*)に置き換えても、先ほどの色の問題は解決しません。引き続きすべての要素にマッチし、直接宣言を適用するためです。その問題には継承を使いましょう。MDNによると、:where()は2021年1月以降、幅広いブラウザーで利用できます。古いブラウザーをサポートする場合は、互換性表を確認してください。:where()疑似クラス。

:where()に対応していないブラウザーでは、間隔の既定値にh1, h2, h3のような通常のセレクターリストを使い、同じスタイルシートとレイヤー内で同じ詳細度の例外を後に配置します。これなら対象を絞った既定値を維持できますが、詳細度はゼロではなくなります。詳細度。

ルール群に明示的な順序が必要な場合はレイヤーを使う

大規模なスタイルシートでは、次の任意の断片でレイヤーの順序を宣言する方法を示します。これは完全な例のCSSを置き換えるものではなく、別に使うものです。

/* Optional fragment for browsers supporting cascade layers. */
@layer defaults, components;

@layer defaults {
  p {
    margin-bottom: 1rem;
  }
}

@layer components {
  .tile p {
    margin-bottom: 0.5rem;
  }
}

通常の作成者宣言では、後のレイヤーが前のレイヤーより優先されます。レイヤーに属さない通常の宣言は、レイヤー内の宣言より優先されます。重要宣言ではレイヤーの優先順序が逆になるため、!importantを追加するとその関係が変わります。レイヤーは競合するルールを整理しますが、スタイルを直接指定された子要素に親の値を継承させるものではありません。@layerアットルール。

MDNによると、@layerは2022年3月以降、複数のブラウザーで利用できます。対象となる古いブラウザーが対応していない場合は、重要なスタイルをレイヤーブロックの外に置き、完全な例で示したレイヤーを使わない方法を採用します。必要なスタイルを、未対応の機能の中だけに置かないでください。カスケードレイヤーの対応状況。

既存のスタイルシートを上書きルールを増やさずに修正するには?

プロパティを1つずつ確認します。テキストの色に問題がある場合は、子孫に色を直接指定している広範なルールを探します。全称ルールからその宣言を削除し、ページの値をbodyに設定して、コンポーネントの色はコンポーネントのルートに置きます。デザイン上、例外が必要な場所に限り、要素単位の宣言を残します。

次に、デモの3つの編集をもう一度行います。通常のコンポーネント内テキストがまだ親の値に従わない場合は、その子孫を対象とする宣言を探します。コンポーネントの枠線を変更しても、子孫の枠線が変化しないのは想定どおりです。枠線はテキストの色とは異なり、継承されません。継承。

保守しやすい既定値には、明確な設定場所があります。ページのタイポグラフィはページの境界に、コンポーネントの表示はコンポーネントの境界に、意図した例外はその対象となる要素に設定します。全称宣言は、マッチするすべての要素に本当に適用したいものに限って使い、ローカルページでその境界を確認しましょう。

参考資料