CSS 2.1の処理モデル

目次

このページではCSS 2.1の仕様でも特に重要なCSSの処理モデル、カスケーディング、値の算出について説明します。

  1. CSSの処理モデル
  2. カスケーディングと継承
  3. 詳細度
  4. CSS処理の具体例
  5. 値の算出
    1. 指定値
    2. 算出値
    3. 利用値
    4. 実効値
  6. 継承

CSSの処理モデル

CSS 2.1ではユーザーエージェントが以下のようにしてCSSを処理すると想定しています。(実際にユーザーエージェントの実装がそうなっているとは限りません。)

  1. ソース文書をパースし、文書ツリーを生成する。
  2. 対象のメディアタイプを特定する。
  3. 2で特定したメディタタイプに一致するスタイルシートを得る。
  4. カスケーディングのルールに従い、文書ツリーの各要素に値を割り当てる。(= 注解(annotate)する。)
  5. 注解された文書ツリーから整形構造体(formatting structure)を生成する。
  6. 整形構造体を対象のメディアに転送する。

重要なのは4番目のカスケード処理と値の算出です。次にそれらを説明します。

カスケーディングと継承

スタイルシートには製作者のスタイルシート、利用者のスタイルシート、ユーザーエージェントのスタイルシートの三つがあります。利用者のスタイルシートは通常ユーザースタイルシート、ユーザーエージェントのスタイルシートはユーザーエジェントのデフォルトスタイルシートと呼ばれます。ユーザースタイルシートは利用者が製作者の作ったスタイルシートを自分の好みに合わせて上書きしたいときなどに利用されます。また、製作者、利用者がスタイルシートを用意しなくても、ユーザーエージェントのデフォルトスタイルシートが適用されるので、ソース文書は利用者に伝わるような形式になってレンダリングされるのです。(例えば、Internet Explorerのデフォルトスタイルシートが適用されると、HTMLで見出しを表すh1要素は、見出しと分かるよう太い大きな文字で表示されます。)

CSSを書くと、大抵は特定の要素にマッチする規則が複数存在することになります。例えば

<p>文章…</p>
p { color: red }
p.clsX { color: blue }

上の二つの規則はどちらもソース文書中のp要素にマッチします。ことのきどちらの規則が適用されるのかが問題となります。さらに、スタイルシートは製作者、利用者、ユーザーエージェントの三者が用意している可能性があるため、この問題はより複雑になります。どの規則のどの宣言が適用されるのかを決めるのがカスケーディング(cascading)です。

カスケーディングは次の順序で行われます。

  1. 対象のメディアタイプに対し、当該の要素とプロパティに適用できる、つまりセレクタが要素にマッチする宣言を全て見つける。
  2. 次の順に宣言を並べる。下にあるものが優先される。
    1. ユーザーエージェントの宣言
    2. 利用者の通常の宣言
    3. 製作者の通常の宣言
    4. 製作者の!importantがある宣言
    5. 利用者の!importantがある宣言
  3. 重要度が最も高くて重複のあった規則を、セレクタの詳細度によって並べる。詳細度の高い規則が優先される。
  4. それでも同じになる規則を、書かれている順序によって並べる。後に書かれている規則が優先される。

詳細度

セレクタは詳細度を持ち、上述のプロセスの3番目ではそれが高い規則が優先されます。詳細度は以下のように計算します。

以下はその例です。

セレクタの詳細度計算の例
セレクタ a b c d 詳細度
* 0 0 0 0 0,0,0,0
li 0 0 0 1 0,0,0,1
li:first-line 0 0 0 2 0,0,0,2
ul li 0 0 0 2 0,0,0,2
ul > li 0 0 0 2 0,0,0,2
h1 + p[title=hoo] 0 0 1 2 0,0,1,2
h1 p.clsX 0 0 1 2 0,0,1,2
p.clsX.clxY 0 0 2 1 0,0,2,1
*#myid 0 1 0 0 0,1,0,0
style=""(セレクタではありませんが) 1 0 0 0 1,0,0,0

この計算はややこしいので要点を挙げると、

となります。ul liul > liでは何となく後者の方が詳細度が高いような気がしますが、実際は同じです。よって後に書かれている規則が優先されます。結合子は詳細度の計算には関係ないことに注意してください。

CSS処理の具体例

それでは具体的に、CSSが処理される過程を見てみましょう。ここではサンプルとして、ソース文書となるHTML、製作者のスタイルシート、利用者のスタイルシート、ユーザーエージェントのデフォルトスタイルシートを用意しました。デフォルトスタイルシートはユーザーエージェントの実装によって異なるので、ここでは仕様書のDefault style sheet for HTML 4より必要な箇所を抜粋しました。出力メディアはコンピュータの画面とします。

ソース文書(HTML)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
 "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <link rel="stylesheet" href="style.css" type="text/css" media="screen,print">
    <title>サンプルHTML</title>
  </head>
  <body id="foo">
    <h1>サンプルHTML</h1>
    <p>簡単なHTMLのサンプルです。</p>
    <address>
      Last-modified: 2007-01-01
    </address>
  </body>
</html>
製作者のスタイルシート(CSS)
body {
 font-size: 12px;
 color: black;
 background: silver
}
利用者のスタイルシート(CSS)
body {
 font-size: 16px !important;
}
body#foo {
 color: black !important;
 color: maroon !important
}
ユーザーエージェントのデフォルトスタイルシート
html, address, body, h1,  p {
display: block
}
body { margin: 8px }
h1 {
 font-size: 2em;
 margin: .67em 0;
 font-weight: bolder
}
address { font-style: italic }

それではCSSの処理モデルを順に追ってみます。

1. ソース文書をパースし、文書ツリーを生成する。
まず、上記のソース文書から図のような文書ツリーが生成されます。
(図)上記HTMLの文書ツリー
2. 対象のメディアタイプを特定する。
対象のメディタタイプはscreenです。
3. 2で特定したメディタタイプに一致するスタイルシートを得る。
ここで、製作者、利用者、ユーザーエージェントのスタイルシートが読み込まれます。
4. カスケーディングのルールに従い、文書ツリーの各要素に値を割り当てる。(= 注解(annotate)する。)
さて、カスケーディングです。
4.1. 対象のメディアタイプに対し、当該の要素とプロパティに適用できる、つまりセレクタが要素にマッチする宣言を全て見つける。
文書ツリー中のそれぞれの要素について、マッチする規則を見つけます。ここではbody要素に注目してみます。すると次のような規則が見つかります。
製作者のスタイルシート(CSS)
body {
 font-size: 12px;
 color: black;
 background: #eeeeee
}
利用者のスタイルシート(CSS)
body {
 font-size: 16px !important;
}
body#foo {
 color: black !important;
 color: maroon !important
}
ユーザーエージェントのデフォルトスタイルシート
html, address,
body, h1,  p, { display: block }
body { margin: 8px }
上記三つのスタイルシートの中で以下の宣言は重複がありません。よってそのまま適用されます。
body要素に適用される宣言
宣言 出所
background:#eeeeee 製作者
display:block ユーザーエージェント
margin:8px ユーザーエージェント
残りは次のプロセスで扱います。
4.2. 次の順に宣言を並べる。下にあるものが優先される。
  1. ユーザーエージェントの宣言
  2. 利用者の通常の宣言
  3. 製作者の通常の宣言
  4. 製作者の!importantがある宣言
  5. 利用者の!importantがある宣言
それでは残った宣言をその通りに並べてみます。まずはfont-sizeプロパティについてです。
body要素のfont-sizeプロパティについて
宣言 出所
font-size:12px 製作者
font-size:16px !important 利用者(!important付)
font-sizeプロパティの指定値は利用者が!important付きで指定した16pxに決定しました。次はcolorプロパティに注目してみましょう。
body要素のcolorプロパティについて
宣言 出所
color:black 製作者
color:black !important 利用者(!important付)
color:maroon !important 利用者(!important付)
colorプロパティは最も重要度の高い規則が二つ残ってしまいました。よってこれは次のプロセスに持ち越されます。
4.3. 上で、重要度が最も高くて重複のあった規則を、セレクタの詳細度によって並べる。詳細度の高い規則が優先される。
では、それぞれの規則の詳細度を計算してみましょう。
body要素のcolorプロパティについて(2)
宣言 セレクタ 詳細度
color:black !important body#foo 1,0,0,0
color:maroon !important body#foo 1,0,0,0
セレクタが同じなので、当然詳細度も同じです。よって最後のプロセスに移ります。
4.4. それでも同じになる規則を、書かれている順序によって並べる。後に書かれている規則が優先される。
書かれている順序によって並べると次のようになります。
  1. color:black !important
  2. color:maroon !important
後にある宣言が優先なので、body要素のcolorプロパティの値は"maroon"に決定しました。(ああ長かった…。)これを全ての要素について行います。
5. 注解された文書ツリーから整形構造体を生成する。
整形構造体は文書ツリーとよく似た概念的なモデルです。カスケーディングの手順で注解された文書ツリーから生成されるとされています。整形構造体がどんな形をしているかはユーザーエージェントの実装によりけりで、必ずしもツリーの形をしているとは限りません。
6. 整形構造体を対象のメディアに転送する。
最後に対象のメディアであるコンピュータの画面に転送して終わりです。

値の算出

上記のようにして各要素の各プロパティに値が割り当てられるのですが、以下では値の算出のプロセスを説明します。値の算出には指定値、算出値、利用値、実効値という四つのプロセスを辿ります。

指定値

指定値(specified value)は以下のように割り当てられます。

  1. カスケーディングによって値が定まれば、それを使う。
  2. そうでない場合、プロパティが継承を行うなら、親要素の算出値を使う。(ルート要素を除く。)
  3. そうでない場合、プロパティの初期値を使う。

算出値

特定の値はカスケーディングの最中に変換され、算出値(computed value)となります。例えば、単位が"em"や"ex"である長さはピクセル値や絶対的な長さに、相対URIは絶対URIになります。

<p>指定値は<dfn>算出値</dfn>に変換されます。</p>
p { font-size: 12px }
dfn { font-size: 1.2em }

この例ではp要素のfont-sizeプロパティの指定値は"12px"で、これはこのまま算出値になります。一方、dfn要素のfont-sizeプロパティで使われる"em"は親要素のfont-sizeプロパティの算出値を参照するので、dfn要素のfont-sizeプロパティの算出値は12px * 1.2 = 14.4pxとなります。

指定値と算出値
要素 指定値 算出値
p要素 12px 12px
dfn要素 1.2em 14.4px

利用値

算出値は通常文書を整形することなく扱われますが、中には文書が整形されないと値を決定できないものがあります。例えば、widthプロパティが<パーセント値>で指定されている場合、包含ブロックの幅が決まらないとその値を決められません。そのように、残っている全ての値を絶対的な値へ解決したものが利用値(used value)となります。

実効値

文書のレンダリングには原則として利用値が使用されますが、場合によってはユーザーエージェントがその値を利用できないことがあります。例えば、上の14.4pxというフォントサイズは、そのサイズのフォントが存在しなければ14pxに丸められるかもしれません。また、画面の色数が少なければ、<色>は適当な値に変換されるでしょう。そうして実際に使われる値が実効値(actual value)です。

継承

プロパティの中には継承(inheritanhce)を行うものがあります。継承を行うプロパティは、値が指定されなかったとき代わりに親の算出値を用います。

<body><p>継承するときは親の指定値ではなく<em>算出値</em>を使います。</p></body>
body { font-size: 12px }
p { font-size: 1.2em }

この例ではp要素のfont-sizeプロパティの指定値は"1.2em"であり、算出値は12px * 1.2 = 14.4pxとなります。また、em要素のfont-sizeプロパティの指定値は親の算出値を継承して、"14.4px"となります。(指定値"1.2em"を継承するのではありません。)

指定値と算出値
要素 指定値 算出値
body要素 12px 12px
p要素 1.2em 14.4px
em要素 14.4px (継承) 14.4px

継承するかしないかはプロパティごとに定められています。例えば、colorプロパティ(文字色)は継承し、border-styleプロパティ(ボーダーのスタイル)は継承しません。継承しないプロパティの値が指定されなかったときは、指定値で述べたようにそのプロパティの初期値が使われます。

値にキーワード"inherit"を指定すると、継承しないプロパティでも強制的に親の算出値を継承させることができます。

Information

現在の位置