先日、以下の 2 つの Bookmarklet を公開しました。

これらのツールを作成するために、CSS の値の処理について、改めて仕様を読んで学習したのですが、あまり意識しないで書いてきた CSS の値について、いくつか新たな発見がありました。

本記事では、この CSS に指定した値がどのような処理を経て最終的な値に変換されていくのかを、コード例を用いながら説明していきます。

CSS の値の処理は W3C 仕様の「CSS Cascading and Inheritance Level 5」内にある「4. Value Processing」が該当します。

CSS の値は以下の段階を経て、ブラウザで描画できる値に変換されていきます。

No.日本語訳概要
1Declared Value宣言値宣言された値
2Cascaded Valueカスケード値カスケードの結果によって適用される値
3Specified Value指定値指定された値
4Computed Value計算値計算された後の値
5Used Value使用値レイアウト確定後に計算される値
6Actual Value実効値User-Agent によって最終調整される値

以下のスタイル指定を例に考えてみます。前提としてビューポート幅は 1000px とします。

比較する意図で、スタイルが適用されない指定がいくつか含まれています。

本記事で使用するコード
<!-- HTML -->
<div class="parent">
  <div class="child"></div>
</div>

<!-- CSS -->
<style>
.parent {
  --size: 33.33333%;
  inline-size: 400px;
}
.child {
  inline-size: var(--size);
}
div {
  inline-size: 100%;
}

/* 条件に一致しない指定 */
@media (min-width: 1200px) {
  .child {
    inline-size: 50%;
  }
}
/* セレクタが一致しない指定 */
#foo {
  inline-size: 200px;
}
/* 値の構文(syntax)が正しくない指定 */
.child {
  inline-size: 🍔;
}
</style>

このとき、<div class="child">inline-size プロパティの値は、最終的にはどのような値になるでしょうか。順番に見ていきましょう。

Declared Value は、対象の要素に指定された宣言が該当します。セレクタや @media などの条件に一致しない場合や、プロパティや値の構文が正しくない場合には除外されます。

また、-webkit- のようなベンダープレフィックス付きのレガシーな構文が、新しい構文に変換されるのもこのタイミングです。

今回の例では、セレクタにマッチして構文が正しい、以下の 2 つの指定に絞り込まれます。

  • Declared Value 宣言値
    .child {
      inline-size: var(--size);
    }
    div {
      inline-size: 100%;
    }
  • Cascade Value カスケード値
  • Specified Value 指定値
  • Computed Value 計算値
  • Used Value 使用値
  • Actual Value 実効値

ここで注意する点としては、カスタムプロパティの値が構文として正しいかどうかは、このタイミングでは評価されないということです。例として、以下のスタイルを比較してみます。

無効な値を直接指定する例と、カスタムプロパティに指定する例
/* 無効な値を直接指定 */
.child {
  inline-size: 🍔;
}

/* 無効な値をカスタムプロパティに指定 */
.parent {
  --size: 🍔;
}
.child {
  inline-size: var(--size);
}

無効な値を直接指定した場合には Declared Value の段階で無効だとみなされますが、カスタムプロパティに指定した場合には、そのまま Cascade Value に進みます。

このカスタムプロパティの値の評価については、後述する Computed Value で説明します。

Cascade Value は、カスケードの仕組みによって適用される値を指します。ここでは、詳細度の高い .childvar(--size) が適用されます。

  • Declared Value 宣言値
  • Cascade Value カスケード値
    /* 詳細度: 0-1-0 🏆 */
    .child {
      inline-size: var(--size);
    }
    
    /* 詳細度: 0-0-1 */
    div {
      inline-size: 100%;
    }
  • Specified Value 指定値
  • Computed Value 計算値
  • Used Value 使用値
  • Actual Value 実効値

より詳細なカスケードの仕組みについては、「CSS カスケード再入門 #1 基礎編」で詳しく説明しています。

Specified Value は、プロパティに指定された値そのものです。まだ値は計算されないので、今回の例では var(--size) のままです。

  • Declared Value 宣言値
  • Cascade Value カスケード値
  • Specified Value 指定値
    var(--size)
  • Computed Value 計算値
  • Used Value 使用値
  • Actual Value 実効値

なお、値に initialinheritunsetrevertrevert-layer といった CSS 全体のキーワード(CSS-wide keywords)を指定している場合には、先祖要素やオリジンの指定値に置き換わります。

Computed Value は、祖先要素のレイアウトが確定する前に計算できる値です。

例えば、font-size: 1rem の指定は、祖先要素のレイアウトに影響を受けないため、このタイミングで計算され、1rem(相対値)が 16px(絶対値)に変換されます。

また、var() 関数でカスタムプロパティを指定している場合には、参照先の値に置き換わります。@property の構文(syntax)が検証されるのもこのタイミングです。

今回の例では、var(--size) が、カスタムプロパティの値である 33.33333% に変換されます。

  • Declared Value 宣言値
  • Cascade Value カスケード値
  • Specified Value 指定値
  • Computed Value 計算値
    33.33333%
  • Used Value 使用値
  • Actual Value 実効値

そもそも計算を必要としない場合には、Specified Value と同じ値になります。

カスタムプロパティが評価されるのは Computed Value のタイミングのため、無効な値を指定している場合には、IACVT(Invalid At Computed-Value Time)と呼ばれる状態になります。

IACVT の値は、以下の条件によって決まります。

  • @property で定義していないカスタムプロパティや、syntax*(Universal Syntax)の場合には initial になる1
  • それ以外の場合には、unset が指定されているのと同様になる2

なお、すでに Cascade Value によって絞り込まれている状態なので、カスケードのステップをさかのぼって、次に優先度の高いスタイルが適用されることはありません。

加えて、この IACVT ですが、Declared Value の構文エラーと違って、現時点ではデベロッパーツール上では警告が表示されない点にも注意が必要です。

Google Chrome のデベロッパーツールのスクリーンショット
カスタムプロパティに無効な値を指定しても、警告は表示されない

Used Value は、祖先要素のレイアウトが確定してから計算される値です。

今回の例では、サイズをパーセント値(%)で指定しており、親要素のサイズが決まらないと絶対値(px)に変換することができません。

親要素 .parent の指定は inline-size: 400px なので、計算後の値は 133.33332px です。

  • Declared Value 宣言値
  • Cascade Value カスケード値
  • Specified Value 指定値
  • Computed Value 計算値
  • Used Value 使用値
    /* 400px * 33.33333% */
    133.33332px
  • Actual Value 実効値

祖先要素のレイアウトに影響を受けない場合は、Computed Value と同じ値になります。

最後に、Actual Value は、ブラウザ(User-Agent)によって最終調整される値です。

今回の例では小数点以下の値がブラウザにとって扱いやすい値に丸められ、Google Chrome では 133.328px、Firefox では 133.317px、そして Safari では 133.328125px に変換されます。

  • Declared Value 宣言値
  • Cascade Value カスケード値
  • Specified Value 指定値
  • Computed Value 計算値
  • Used Value 使用値
  • Actual Value 実効値
    /* Google Chrome */
    133.328px
    
    /* Firefox */
    133.317px
    
    /* Safari */
    133.328125px

ここまでの流れを経て、最終的にブラウザに描画されるスタイルの値が決まります。

さらに理解を深めたい場合には、W3C の仕様にもいくつか例が掲載されているので、こちらもあわせて参考にするとよいかもしれません。

getComputedStyle() で取得した値はどの値?

この見出しのリンク

JS で計算済みのスタイルの値を取得するには、以下の 2 つのメソッドを使用します。

  1. getComputedStyle() で、対象の要素に指定されたスタイルを取得
  2. getPropertyValue() で、指定したプロパティの値を取得
getComputedStyle() で計算済みの値を取得する例
<!-- HTML -->
<div class="parent">
  <div class="child"></div>
</div>

<!-- CSS -->
<style>
.parent {
  --size: 33.33333%;
  inline-size: 400px;
}
.child {
  inline-size: var(--size);
}
</style>

<!-- JS -->
<script>
const elem = document.querySelector('.child');

// 1. 対象の要素に指定されたスタイルを取得
const style = getComputedStyle(elem);

// 2. 指定したプロパティの値を取得
const value = style.getPropertyValue('inline-size');
console.log(value);
</script>

ここで得られる値はブラウザごとに異なり、Google Chrome では 133.328px、Firefox では 133.317px、そして Safari では 133.328125px なので、Actual Value と一致します。

しかし、W3C の仕様3や MDN では、Actual Value(実効値)とは明示されておらず、Resolved Value(解決値)という用語が使用されています。

ちなみに、JS やデベロッパーツールを介して、Computed Value(計算値)や Used Value(使用値)をピンポイントに取得する方法は現時点では見当たりません。

普段 CSS のコードを書くときに、Specified Value 以降の処理を意識することはほとんどないでしょう。しかし、カスタムプロパティや @property を使用する機会が増えていくなかで、その仕組みを理解しておくことは重要であると考えています。

最後に、今回のダイジェストを掲載します。

  • Declared Value 宣言値
    .child {
      inline-size: var(--size);
    }
    div {
      inline-size: 100%;
    }
  • Cascade Value カスケード値
    /* 詳細度: 0-1-0 🏆 */
    .child {
      inline-size: var(--size);
    }
    
    /* 詳細度: 0-0-1 */
    div {
      inline-size: 100%;
    }
  • Specified Value 指定値
    var(--size)
  • Computed Value 計算値
    33.33333%
  • Used Value 使用値
    /* 400px * 33.33333% */
    133.33332px
  • Actual Value 実効値
    /* Google Chrome */
    133.328px
    
    /* Firefox */
    133.317px
    
    /* Safari */
    133.328125px

次回は、これらの仕組みを踏まえて、@property の使い方や課題を取り上げます。

脚注

  1. このときの initial 値は「Guaranteed-Invalid Value」と呼ばれます。なお、カスタムプロパティの値に initial を指定することで明示的に無効な値を渡すこともできます。

  2. 継承されたプロパティの場合には inherit、そうでなければ initial になります。

  3. 「CSS Object Model」は、現時点では草案(Working Draft)段階である点には注意が必要です。