カスケーディング変数のためのCSSカスタムプロパティ モジュール レベル1

W3C候補勧告スナップショット,

この文書の詳細情報
このバージョン:
https://www.w3.org/TR/2022/CR-css-variables-1-20220616/
最新公開バージョン:
https://www.w3.org/TR/css-variables-1/
編集者草案:
https://drafts.csswg.org/css-variables/
以前のバージョン:
履歴:
https://www.w3.org/standards/history/css-variables-1
実装報告:
https://wpt.fyi/results/css/css-variables
テストスイート:
http://test.csswg.org/suites/css-variables-1_dev/nightly-unstable/
https://wpt.fyi/results/css/css-variables/
フィードバック:
CSSWG課題リポジトリ
編集者:
Tab Atkins Jr. (Google)
この仕様への編集提案:
GitHub エディター

概要

このモジュールは、すべてのCSSプロパティで受け入れられる新しいプリミティブ値型としてカスケーディング変数を導入し、それらを定義するためのカスタムプロパティについて説明します。

CSSは、構造化文書(HTMLやXMLなど)の画面、紙などでのレンダリングを記述するための言語です。

この文書のステータス

このセクションは、公開時点でのこの文書のステータスについて説明します。 現在のW3C出版物の一覧や、 本技術レポートの最新改訂版は、W3C技術レポートインデックス https://www.w3.org/TR/ にてご覧いただけます。

この文書はCSS作業グループによって、候補勧告スナップショットとして 勧告トラックを用いて公開されました。 候補勧告として公開されたことは、W3Cおよびそのメンバーによる支持を意味するものではありません。 候補勧告スナップショットは広範なレビューを受けており、 実装経験を集めることを目的としており、作業グループメンバーによるロイヤリティフリーライセンスのコミットメントがあります。 この文書はW3C勧告となることを意図しています。 追加のフィードバックを収集するため、少なくともまでは候補勧告として維持されます。

ご意見・ご要望はGitHubで課題を提出(推奨)してください。 件名に仕様コード「css-variables」を含めてください(例:「[css-variables] …コメントの要約…」)。 すべての課題やコメントはアーカイブされています。 または、(アーカイブ有り) 公開メーリングリスト www-style@w3.org へ送信することもできます。

この文書は2021年11月2日W3Cプロセス文書に従って管理されています。

この文書はW3C特許ポリシーのもとで活動するグループによって作成されました。 W3Cは、グループの成果物に関連して行われた特許開示の公開リストを管理しています。 そのページには特許開示の手順も記載されています。 個人が特許についての実際の知識を有し、その特許に必須の請求項が含まれていると考える場合は、W3C特許ポリシー第6節に従い情報を開示しなければなりません。

1. はじめに

このセクションは規範的ではありません。

大規模な文書やアプリケーション (小規模なものでも) には、多くのCSSが含まれる場合があります。 CSSファイル内の値の多くは重複したデータになります。 例えば、 サイトがカラースキームを設定し、 サイト全体で3~4色を使い回すことがあります。 このデータを変更するのは困難で間違いが発生しやすく、 CSSファイル全体(場合によっては複数ファイル)に散在しているため、 置換操作(Find-and-Replace)も容易ではありません。

このモジュールでは、著者が任意の値を任意の名前でプロパティに割り当てられる カスタムプロパティ という著者定義のプロパティ群と、 それらの値を文書内の他のプロパティで利用できる var() 関数を導入します。 これにより、大きなファイルの可読性が向上し、 一見意味のない値にも分かりやすい名前が付きます。 編集作業も簡単で間違いが減り、 値を一箇所だけ変更すれば カスタムプロパティ を使ったすべての箇所に自動的に変更が伝播します。

1.1. 値の定義

この仕様は、[CSS2]CSSプロパティ定義規約 と、[CSS-VALUES-3]値定義構文に従います。 この仕様で定義されていない値型はCSS Values & Units [CSS-VALUES-3]で定義されています。 他のCSSモジュールとの組み合わせで、これらの値型の定義が拡張されることもあります。

各プロパティの定義に記載された固有の値に加えて、 この仕様で定義されるすべてのプロパティは CSS全体キーワード もプロパティ値として受け入れます。 可読性のため、明示的に繰り返してはいません。

2. カスタムプロパティの定義: --* プロパティ群

この仕様は、 カスタムプロパティ と呼ばれる制約のないプロパティ群を定義します。 これらはvar()の代入値 を定義するためなどにも使われます。

Name: --*
Value: <declaration-value>?
Initial: 保証された無効値
Applies to: すべての要素とすべての疑似要素(プロパティリストが制限されているものも含む)
Inherited: はい
Percentages: 該当なし
Computed value: 変数を置換した指定値、または保証された無効値
Canonical order: 文法による
Animation type: 離散的

ユーザーエージェントは、このプロパティを非視覚メディアを含むすべてのメディアでサポートすることが期待されています。

カスタムプロパティとは、 名前が2つのハイフン(U+002D HYPHEN-MINUS)で始まるプロパティのことです。 例えば --foo のようなものです。 <custom-property-name> 生成規則はこれに対応し、 2つのハイフンで始まる任意の<dashed-ident>(2つのハイフンで始まる有効な識別子)と定義されます。 ただし -- 自体は除外されており、 これはCSSで将来利用されるために予約されています。 カスタムプロパティは著者および利用者専用であり、 CSSがここで示す以上の意味を与えることはありません。

テスト
カスタムプロパティは変数を定義し、 var() 記法で参照できます。 これは多くの用途に利用できます。 例えば、ページで少数の色を一貫して使う場合、 それらの色をカスタムプロパティに保存し、 変数として使用できます:
:root {
  --main-color: #06c;
  --accent-color: #006;
}
/* CSSファイルの残り */
#foo h1 {
  color: var(--main-color);
}

名前付けにより色の記憶法となり、 色コードの見落としやすいタイプミスを防ぎます。 また、テーマカラーを変更する場合も (カスタムプロパティ値のみ変更すればよいので) 多数のスタイルシートを編集する必要がなくなります。

他のCSSプロパティとは異なり、 カスタムプロパティ名はASCII大文字・小文字を区別しないものではありません。 代わりに、カスタムプロパティ名は 一致する場合のみ同一とみなします。

テスト
--foo--FOO は両方有効ですが、 それぞれ異なるプロパティです。var(--foo) は前者を参照し、 var(--FOO) は後者を参照します。

さらに、--foó--foó も異なるプロパティです。 前者はU+00F3(ラテン小文字oアキュート付き)で綴られ、 後者はASCIIの"o"+U+0301(合成アキュートアクセント)で綴られています。 "一致する"関係は、文字列をコードポイントごとに直接比較して判断します。 これによりUnicode正規化やロケール特有の照合の複雑さや落とし穴を避けます。

OSやキーボード、入力方法によって、 見た目が同じテキストでも異なるコードポイント列が使われる場合があります。 著者は混乱を避けるため、 変数名にわかりやすい名前を使うか、エスケープなどの手法で 見た目が似ているコード列が一致するようにしましょう。 例については [CHARMOD-NORM] の2.3節を参照してください。

以下のCSSを保守している開発者は、 テストパッチが赤色になる理由に困惑するかもしれません:
--fijord: red;
--fijord: green;
--fijord: blue;

.test {
  background-color: var(--fijord);
}

理由は、最初のカスタムプロパティは LATIN SMALL LETTER F + LATIN SMALL LETTER I + LATIN SMALL LETTER J の文字列を使い、 2番目は見た目は同じですが LATIN SMALL LETTER F + LATIN SMALL LIGATURE IJ、 3番目はLATIN SMALL LIGATURE FI + LATIN SMALL LETTER Jの文字列を使っているためです。

つまり、CSSには見た目が似ていても3つの異なるカスタムプロパティがあり、 そのうち2つは未使用です。

カスタムプロパティは、 all プロパティでリセットされません将来的にすべての変数をリセットするプロパティを定義するかもしれません。

CSS全体キーワードはカスタムプロパティでも利用でき、 他のプロパティと同じ意味を持ちます。

テスト

注: つまり、カスケード値の算定時に通常通り解釈され、 カスタムプロパティの値としては保持されず、 対応する変数による置換には使われません。

注: このモジュールは カスタムプロパティvar() 関数による 「変数」利用に焦点を当てていますが、 実際のカスタムプロパティとしてスクリプトで解析・利用することもできます。 CSS Extensions仕様 [CSS-EXTENSIONS] で、これらの用途がさらに拡張される見込みです。

カスタムプロパティは通常のプロパティなので、 どの要素にも宣言でき、 通常の継承やカスケード規則で解決されます。 @media や他の条件付き規則で条件付けもでき、 HTMLの style 属性でも使えますし、 CSSOMで読み書きも可能です。

テスト

特に、トランジションやアニメーションも可能ですが、 UAが内容を解釈できないため、 「50%で切り替え」挙動(賢く補間できない値ペアの場合の挙動)になります。 ただし、カスタムプロパティ@keyframes ルールで使うと アニメーション汚染となり、 アニメーションプロパティで var() 関数を通じて参照された場合の扱いに影響します。

テスト

アニメーション汚染は 「伝染性」です。 アニメーション汚染プロパティを参照するカスタムプロパティも アニメーション汚染となります。

このスタイル規則:
:root {
  --header-color: #06c;
}

ルート要素にカスタムプロパティ --header-color を宣言し、その値を"#06c"に設定しています。 このプロパティは文書の残りの要素にも継承されます。 var() 関数で値を参照できます:

h1 { background-color: var(--header-color); }

この規則は background-color: #06c; と同じですが、変数名により色の由来が明確になり、 文書内でvar(--header-color)を他の要素にも使えば、 ルート要素の--header-colorプロパティを変更するだけで すべての利用箇所を一度に更新できます。

カスタムプロパティが複数回宣言された場合は、 標準のカスケード規則で解決されます。 変数は常に、同じ要素上にある関連カスタムプロパティの算出値から値を取得します:
:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }

<p>私はルート要素から青を継承しました!</p>
<div>私は直接緑を設定されました!</div>
<div id='alert'>
  私は直接赤を設定されました!
  <p>私も赤です, 継承によるものです!</p>
</div>
カスタムプロパティの実用例として、 文字列を利用箇所から分離して国際化保守を容易にすることがあります:
:root,
:root:lang(en) {--external-link: "external link";}
:root:lang(el) {--external-link: "εξωτερικός σύνδεσμος";}

a[href^="http"]::after {content: " (" var(--external-link) ")"}

変数宣言は別ファイルに分離することもでき、 翻訳の保守がより簡単になります。

2.1. カスタムプロパティ値の構文

カスタムプロパティの許容される構文は非常に寛容です。 <declaration-value> 生成規則は 1つ以上のトークンからなる任意の並びにマッチしますが、並び内に <bad-string-token><bad-url-token>、 対応しない <)-token><]-token><}-token>、 またはトップレベルの <semicolon-token> トークンや 値が "!" である <delim-token> トークンが含まれていない限り有効です。

テスト

さらに、カスタムプロパティの値にvar()参照が含まれる場合、 var()参照は指定された var()文法に従い有効でなければなりません。 そうでない場合、カスタムプロパティは無効となり無視されます。

注: この定義と一般的なCSS構文規則により、 カスタムプロパティ値には対応しない引用符や括弧が含まれることはなく、 再シリアライズ時に囲むスタイル規則など大きな構文構造に影響を与えることはありません。

注: カスタムプロパティには末尾に !important を含めることができますが、これはCSSパーサーによって自動的に値から除去され、 カスケード内でそのカスタムプロパティが「重要」となります。 つまり、トップレベルの "!" 文字の禁止は !important の使用を妨げません。 !important は構文チェックの前に除去されます。

テスト
例えば、次のようなカスタムプロパティは有効です:
--foo: if(x > 5) this.width = 10;

この値は変数としては明らかに役立ちませんが、 通常のプロパティでは無効ですが、 JavaScriptで読み取って処理することは可能です。

カスタムプロパティの値、 およびカスタムプロパティに代入されるvar()関数の値は、 大文字・小文字を区別し、 著者が指定した元の大文字・小文字を保持しなければなりません。 (多くのCSS値はASCII大文字・小文字を区別しないため、 ユーザーエージェントはそれらを「正規化」して単一の大文字・小文字にできますが、 カスタムプロパティではそれは許可されません。)

テスト
カスタムプロパティには何でも含められるため、 それらの中身をどう解釈するかを一般的に知る方法はありません (var()で既知のプロパティへ代入されるまで)。 一部だけ解決するよりも、完全に未解決のままとし、 CSSトークンのストリーム と var() 関数だけが混在している状態です。

これには副次的な影響があります。 例えば、CSS内の相対URLは、 値が現れるスタイルシートのベースURLから解決されます。 ただし、--my-image: url(foo.jpg); のようなカスタムプロパティが "/a/style.css" スタイルシートに現れた場合、 すぐに絶対URLに解決されることはなく、 その変数が別の "/b/style.css" スタイルシートで background: var(--my-image); のように使われた場合、 その時点で "/b/foo.jpg" に解決されます。

2.2. 保証された無効値

カスタムプロパティの初期値は 保証された無効値です。 § 3 カスケーディング変数の利用: var()記法で定義されているように、 var()で この値を持つカスタムプロパティを代入すると、 それを参照するプロパティは算出値時に無効となります。

この値は空文字列としてシリアライズされますが、 実際に --foo: ; のように カスタムプロパティに空値を書き込むことは 有効な(空の)値であり、 保証された無効値ではありません。 何らかの理由で変数を手動で 保証された無効値にリセットしたい場合は、 キーワード initial を使うことで可能です。

2.3. 依存サイクルの解決

カスタムプロパティは ほぼ完全に評価されずに残されますが、 値の中に var() 関数のみ許可・評価されます。 これにより、カスタムプロパティが自身を参照する var() を使ったり、 2つ以上のカスタムプロパティが互いに参照し合うことで 循環依存が発生することがあります。

各要素ごとに、 各カスタムプロパティをノードとする 有向依存グラフを作成します。 カスタムプロパティ prop の値に var() 関数が var プロパティを参照している場合(var() の フォールバック引数内も含む)、prop から var へのエッジを追加します。 カスタムプロパティから自身へのエッジも可能です。

依存グラフにサイクルがある場合、 サイクル内のすべてのカスタムプロパティ算出値時に無効となります。

テスト

注: 依存サイクルに関与する定義済みプロパティは、 値内に無効な変数が含まれるようになる(算出値時に無効になる)か、 独自のサイクル処理を定義する(例えば font-sizeem 値を使う場合など)。 カスタムプロパティのように 保証された無効値にはなりません。

この例は、変数を安全に使うカスタムプロパティを示しています:
:root {
  --main-color: #c06;
  --accent-background: linear-gradient(to top, var(--main-color), white);
}

--accent-backgroundプロパティ (var(--main-color) を使う他のプロパティも含む)は --main-colorプロパティが変更されると自動的に更新されます。

一方、この例は変数同士が互いに依存している無効な例です:
:root {
  --one: calc(var(--two) + 20px);
  --two: calc(var(--one) - 20px);
}

--one--two の両方が 算出値時に無効となり、 長さではなく保証された無効値になります。

重要なのは、カスタムプロパティは値内のvar()関数を算出値時に解決するということです。 これは値が継承されるに発生します。 一般的に、循環依存は同じ要素上の複数のカスタムプロパティが互いを参照する場合のみ発生し、 要素ツリー上位で定義されたカスタムプロパティが下位の要素で定義されたプロパティと 循環参照を起こすことはありません。

テスト
例えば、次の構造の場合、 これらのカスタムプロパティは循環しておらず、すべて有効な変数を定義します:
<one><two><three /></two></one>
<style>
one   { --foo: 10px; }
two   { --bar: calc(var(--foo) + 10px); }
three { --foo: calc(var(--bar) + 10px); }
</style>

<one> 要素は --foo の値を定義します。 <two> 要素はこの値を継承し、さらに --barfoo 変数を使って値を割り当てます。 最後に <three> 要素は、変数代入--bar の値を継承し (つまり calc(10px + 10px) という値になります)、 その値を使って --foo を再定義します。 継承された --bar の値には <one> で定義された --foo プロパティへの参照は もはや含まれていないため、 var(--bar) 変数で --foo を定義しても循環せず、 実際には通常のプロパティで変数として参照されると 30px に解決されます。

3. カスケーディング変数の利用: var() 記法

カスタムプロパティの値は、 var()関数を使って 他のプロパティ値に代入することができます。 var()の構文は以下の通りです:

var() = var( <custom-property-name> , <declaration-value>? )
テスト

@supports


通常のカンマ省略規則(値を区切らない場合はカンマを省略する必要がある)への例外として、 何も後に続かない素のカンマは、var()内では有効として扱われなければならず、 空のフォールバック値を示します。

テスト

注: つまり、var(--a,) は有効な関数であり、 --a カスタムプロパティが無効または存在しない場合、 var() は何も代入しないことを指定します。

var() 関数は、 要素の任意のプロパティの値の任意の部分に使用できます。 var() 関数はプロパティ名やセレクター、 その他プロパティ値以外の用途には使用できません。 (そうすると通常は無効な構文になったり、変数と関係のない意味の値となります。)

テスト
例えば、次のコードは変数をプロパティ名として誤って使おうとしています:
.foo {
  --side: margin-top;
  var(--side): 20px;
}

これは margin-top: 20px; を設定することと同じではありません。 実際には、2つ目の宣言はプロパティ名が無効なため、構文エラーとして単に破棄されます。

関数の第1引数は、代入対象となるカスタムプロパティの名前です。 第2引数が指定されていれば、それはフォールバック値であり、 参照されたカスタムプロパティの値が保証された無効値の場合に代入値として使われます。

テスト

注: フォールバックの構文はカスタムプロパティ同様にカンマが許容されます。 例えば、var(--foo, red, blue)はフォールバック値としてred, blueを定義します。 つまり、最初のカンマから関数の終わりまでの部分がフォールバック値として扱われます。

フォールバック値は防御的コーディングのために役立ちます。 例えば、 著者が大規模なアプリケーションに組み込むことを想定したコンポーネントを作成し、 スタイルに変数を用いることで、 アプリ全体のテーマに合わせて アプリの著者がコンポーネントを簡単にカスタマイズできるようにします。

フォールバックがない場合、 アプリの著者はコンポーネントが使用するすべての変数に値を指定する必要があります。 フォールバックがあれば、コンポーネントの著者がデフォルト値を指定できるので、 アプリの著者は上書きしたい変数だけ値を指定すれば済みます。

/* コンポーネントのスタイル: */
.component .header {
  color: var(--header-color, blue);
}
.component .text {
  color: var(--text-color, black);
}

/* 大規模アプリケーションのスタイル: */
.component {
  --text-color: #080;
  /* header-colorは設定されていないため
     blue(フォールバック値)のまま */
}

プロパティに1つ以上のvar()関数が含まれていて、 それらの関数が構文的に有効である場合、 プロパティ全体の文法はパース時点で有効とみなされます。 実際の構文チェックは算出値時に、 var()関数が代入された後にのみ行われます。

テスト

プロパティ値内でvar()を代入するには:

  1. カスタムプロパティvar()関数の第1引数で指定され、 かつそのプロパティがアニメーション汚染であり、 var()関数がアニメーション不可なプロパティで使われている場合、 このアルゴリズムの残りの処理ではカスタムプロパティは初期値を持つものとして扱う。
  2. カスタムプロパティvar()関数の第1引数で指定され、 その値が初期値以外の場合、 var()関数を対応するカスタムプロパティの値で置き換える。
  3. それ以外の場合、var()関数が第2引数としてフォールバック値を持つ場合、 var()関数をフォールバック値で置き換える。 フォールバック内にvar()参照があれば、 それらも代入する。
  4. それ以外の場合、 var()関数を含むプロパティは算出値時に無効となる。

    注: 他にもプロパティが算出値時に無効になる要因があります。

テスト

CSSOM


注意、var() 代入はテキストレベルではなく CSS トークン [css-syntax-3] のレベルで行われます。 変数で一部だけ提供された単一トークンを作ることはできません:
.foo {
  --gap: 20;
  margin-top: var(--gap)px;
}

これは margin-top: 20px;(長さ)を設定することと同じではありません。 実際には、margin-top: 20 px; (数値+識別子)と同じであり、 margin-top プロパティに対して単に無効な値です。 ただし、calc() を使えば、次のように同じことを正しく実現できます:

.foo {
  --gap: 20;
  margin-top: calc(var(--gap) * 1px);
}
テスト

var() 関数は算出値時に 代入されます。 すべての var() 関数が代入された後に、 宣言が定義された文法と一致しない場合、 その宣言は算出値時に無効となります。

テスト

宣言に含まれるすべてのvar()関数が代入された後、 その宣言がCSS全体キーワード(および空白のみ)で構成されている場合、 その値は最初からそのキーワードが指定値だったかのように決定されます。

テスト
例えば、次の利用例は構文的には問題ありませんが、 変数が代入されると意味のない結果になります:
:root { --looks-valid: 20px; }
p { background-color: var(--looks-valid); }

20pxbackground-colorに対して無効な値なので、 このプロパティはtransparentbackground-colorの初期値)になります。

プロパティがデフォルトで継承されるもの(例:color)だった場合は、 初期値ではなく継承値になります。

var()関数はCSS全体キーワードカスタムプロパティ自体から取得することはできません。 そのように指定しようとしても(例:--foo: initial;)、 カスタムプロパティの明示的デフォルト化が発生するだけです。 しかし、フォールバックにはCSS全体キーワードを指定できます:
p { color: var(--does-not-exist, initial); }

上記コードでは、--does-not-existプロパティが存在しない、 または算出値時に無効の場合、 var()initialキーワードを代入し、 プロパティは元々color: initialだったかのように振る舞います。 これにより、継承ではなくドキュメントの初期color値が使われます(フォールバックがなければ継承)。

3.1. 無効な変数

カスタムプロパティの値が 保証された無効値の場合、var()関数で代入することはできません。 これを試みると宣言は算出値時に無効となります(有効なフォールバックが指定されていない場合)。

宣言は、算出値時に無効になる場合があります。 それは、var()カスタムプロパティ(上記のように 保証された無効値)を参照する場合や、 有効なカスタムプロパティを使っていても 変数代入後の値が無効な場合です。 この場合、プロパティの型によって算出値は以下のいずれかになります:

プロパティが未登録のカスタムプロパティの場合
登録済みカスタムプロパティ汎用構文の場合

算出値は保証された無効値となります。

それ以外の場合

プロパティが継承されるものなら継承値、そうでないなら初期値となり、 そのプロパティ値がunsetキーワードとして指定された場合と同様です。

テスト
例えば、次のコードでは:
:root { --not-a-color: 20px; }
p { background-color: red; }
p { background-color: var(--not-a-color); }

<p> 要素は透明な背景(background-colorの初期値)になります。 赤い背景にはなりません。 カスタムプロパティ自体が解除されている場合や、 無効なvar()関数を含む場合も同じです。

この挙動は、著者がスタイルシートに直接background-color: 20pxと書いた場合とは異なります。 その場合は通常の構文エラーとなり、規則が破棄され、 background-color: red規則が使われます。

注: 算出値時に無効という概念は、 変数は他の構文エラーのように「早期失敗」できないために存在します。 UAがプロパティ値が無効だと気づいた時点では、 すでに他のカスケード値は破棄されています。

3.2. ショートハンドプロパティ内の変数

var()関数は ショートハンドプロパティを構成するロングハンドに分解するときや、 その構成ロングハンドからショートハンドプロパティに再シリアライズするときに 一部の複雑さをもたらします。

ショートハンドプロパティの値にvar()関数が含まれる場合、 関連するロングハンドプロパティは、 そのショートハンドに変数が含まれていることを示す特別な(著者からは観測できない) 代入保留値で埋められる必要があります。 これは、変数が代入されるまで ロングハンドプロパティの値が決定できないことを示します。

この値は通常通りカスケードされ、 算出値時に(var()関数が最終的に代入された後)、 ショートハンドがパースされ、ロングハンドに適切な値が与えられます。

テスト

注: ショートハンドが var() なしで書かれている場合、 パース時に分解されて構成する ロングハンドプロパティ に分けられます。 その後ロングハンドが カスケードに参加し、 ショートハンドプロパティはほぼ破棄されます。 一方、ショートハンドに var() が含まれている場合は、 var() が何に代入されるかわからないため、 この処理はできません。

代入保留値は、APIで観測可能な場合は空文字列としてシリアライズされなければなりません。

テスト

ショートハンドプロパティは、 構成するロングハンドプロパティの値を集めて、 それらと同じ値集合にパースされる値を合成することでシリアライズされます。

あるショートハンドに属するすべてのロングハンドプロパティが、 同じ元のショートハンド値由来の代入保留値の場合、 ショートハンドプロパティはその元の(var()を含む)値としてシリアライズされなければなりません。

それ以外の場合、 あるショートハンドに属するロングハンドプロパティのいずれかが代入保留値である場合、 またはまだ代入されていないvar()関数を含む場合、 ショートハンドプロパティは空文字列としてシリアライズされなければなりません。

3.3. 長すぎる変数の安全な扱い

単純な実装では、var()関数が「ビリオンラフ攻撃」のバリエーションに使われる可能性があります:

.foo {
  --prop1: lol;
  --prop2: var(--prop1) var(--prop1);
  --prop3: var(--prop2) var(--prop2);
  --prop4: var(--prop3) var(--prop3);
  /* etc */
}

この短い例では、--prop4の算出値はlol lol lol lol lol lol lol lolとなり、 元のlolが8回繰り返されます。 レベルを1つ増やすごとに識別子の数は倍増します。 これを30レベルまで拡張すると(手作業でも数分で可能)、 --prop30にはほぼ10億回識別子が現れることになります。

この種の攻撃を防ぐため、 UAはvar()関数が展開するトークン列の長さに UA定義の制限を課さなければなりません。 var()がこの制限より長いトークン列に展開される場合、 そのプロパティは算出値時に無効となります。

テスト

この仕様ではどの程度の制限を課すべきかは定義されていません。 ただし、1KB以上のテキストを含むカスタムプロパティにも妥当なユースケースがあるため、 制限値は比較的高く設定することが推奨されます。

注: UAがリソース制約のために標準を逸脱することが許されるという一般原則は、 ここでも基本的に当てはまります。 UAは別途、サポート可能なカスタムプロパティの長さや識別子のサイズに上限を設けている場合もあります。 この節で特にこの攻撃を強調する理由は、 長い歴史があり、 一見してどの部分も大きすぎるようには見えなくても実行できてしまうためです。

4. API

すべてのカスタムプロパティ宣言は、大文字小文字区別フラグがセットされています。

テスト

注: カスタムプロパティはキャメルケース形式ではCSSStyleDeclarationオブジェクトに現れません。 なぜなら、その名前は大文字・小文字の両方を含み、 それぞれが異なるカスタムプロパティを示す場合があるからです。 自動キャメルケース化によるテキスト変換はこの仕様と両立できません。 それでも、getPropertyValue()などで正しい名前でアクセス可能です。

4.1. カスタムプロパティのシリアライズ

カスタムプロパティ名は、著者が指定したコードポイント列をそのまま(大文字小文字も変更せず)シリアライズしなければなりません。

注: 非カスタムプロパティの場合、 プロパティ名はASCII範囲に制限され、ASCII大文字小文字区別なしです。 そのため、実装では通常、名前を小文字化してシリアライズします。

カスタムプロパティの指定値は著者が指定した通り厳密にシリアライズしなければなりません。 他のプロパティで発生しうる単純化(コメント除去、空白正規化、数値トークンの再シリアライズなど)は行ってはなりません。

カスタムプロパティの算出値もまた著者が指定した通り厳密にシリアライズしなければなりません。 ただし、var()関数の置換を除きます。

テスト
例えば、次のプロパティの場合:
--y: /* baz */;
--x: /* foo */ var(--y) /* bar */;

--xの指定値のシリアライズは"/* foo */ var(--y) /* bar */"としなければならず、 算出値のシリアライズは"/* foo */ /* baz */ /* bar */"としなければなりません。

(値の先頭の空白はCSSパーサーによって自動でトリムされるため、ここには残りません。)

この要件は、著者がカスタムプロパティに非CSS情報を格納する場合があり、 その情報の「正規化」が著者コードを壊す形で変更される可能性があるため存在します。

例えばUUIDをカスタムプロパティに格納する場合、 --uuid: 12345678-12e3-8d9b-a456-426614174000 のように、スクリプトでアクセスした際に指定どおりそのまま返される必要があります。

この値はCSSで隣接する数値や次元としてパースされます。 特に「-12e3」は数値としてパースされ、-12000となります。 他の文脈でCSSOMが要求するような形で再シリアライズすると、 著者の用途が致命的に壊れてしまいます。

5. 変更点

5.1. 2021年11月11日 CRドラフト以降の変更点

5.2. 2015年12月3日 CR以降の変更点

5.3. 2014年5月6日 Last Call Working Draft以降の変更点

6. 謝辞

CSSワーキンググループの多くの方々に、長年にわたり変数の夢を守り続けてくださったことに深く感謝します。 特に Daniel Glazman 氏と David Hyatt 氏に感謝します。 メーリングリストでも多くの方々がこの変数仕様の発展に寄与してくださいました。 特に Brian Kardell 氏、 David Baron 氏、 François Remy 氏、 Roland Steiner 氏、 そして Shane Stephens 氏に感謝します。

7. プライバシーに関する考慮事項

この仕様は、著者が自身で管理するページ内でスタイリング情報を渡すための純粋に著者レベルの仕組みを定義します。 そのため、新しいプライバシーに関する懸念事項はありません。

8. セキュリティに関する考慮事項

§ 3.3 長すぎる変数の安全な扱いは、 var()関数など 「マクロ展開」的な仕組みに対して成立しうる、長年知られるDoS攻撃について指摘し、 それへの防御策を義務付けています。

適合性

文書の慣例

適合性要件は記述的な断定とRFC 2119の用語を組み合わせて表現されます。規範的部分で使われる「MUST」, 「MUST NOT」,「REQUIRED」,「SHALL」,「SHALL NOT」,「SHOULD」,「SHOULD NOT」, 「RECOMMENDED」,「MAY」,「OPTIONAL」というキーワードはRFC 2119の定義通りに解釈されます。 ただし可読性のため、この仕様書ではこれらのキーワードはすべて大文字で表記されているわけではありません。

本仕様の文書本文は、明示的に非規範と記載されている節・例・注記を除き、すべて規範的です。[RFC2119]

本仕様の例は、「for example」という語で始まるか、あるいは class="example" によって規範的本文から区別されます。例:

これは参考例の一例です。

参考情報の注記は「Note」で始まり、class="note"で本文と区別されます。例:

注: これは参考情報の注記です。

勧告は規範的なセクションとして特別な注意を引くようにスタイルされ、<strong class="advisement">で区別されます。例: UAはアクセシブルな代替手段を必ず提供しなければならない。

テスト

本仕様の内容に関連するテストは、このような「テスト」ブロックで記録される場合があります。 これらのブロックは全て非規範的です。


適合性クラス

本仕様への適合性は、以下の3種類の適合性クラスに対して定義されます:

スタイルシート
CSS スタイルシート
レンダラー
UA。スタイルシートの意味論を解釈し、それを使った文書を描画するもの。
オーサリングツール
UA。スタイルシートを作成するもの。

スタイルシートは、本モジュールで定義された構文を使う全ての文が 汎用CSS文法および本モジュールで定義される各機能の個別文法に従って有効である場合、本仕様に適合するものとみなされます。

レンダラーは、スタイルシートを適切な仕様通りに解釈するだけでなく、 本仕様で定義される全ての機能を正しくパース・描画することで本仕様に適合します。ただし、UAがデバイスの制約により文書を正しく描画できない場合は、UAが非適合となるわけではありません。(例:UAはモノクロモニタ上で色の描画を要求されません。)

オーサリングツールは、汎用CSS文法および本モジュールの各機能の個別文法に従って構文的に正しいスタイルシートを書き、 本モジュールで記述されるスタイルシートの他の適合性要件も満たす場合、本仕様に適合するものとみなされます。

部分実装

著者が将来互換性のあるパース規則を活用してフォールバック値を指定できるように、 CSSレンダラーは、十分なサポートがない@規則・プロパティ・プロパティ値・キーワード・その他構文要素を 必ず無効として扱い、適切に無視しなければなりません。 特に、ユーザーエージェントは、サポートされない構成値のみを選択的に無視し、サポートされる値のみを単一の複数値宣言で有効にすることは 絶対にしてはなりません。いずれかの値が無効とされる場合(サポートされない値は必ずそうなる)、 CSSでは宣言全体が無視されなければなりません。

不安定/独自機能の実装について

将来の安定CSS機能との衝突を避けるために、 CSSWGはベストプラクティスに従って、 不安定な機能独自拡張を実装することを推奨します。

非実験的な実装

仕様がCandidate Recommendation段階に達したら、 実験的でない実装が可能となり、実装者は 仕様通りに正しく実装できたCRレベルの機能についてはプレフィックスなしの実装をリリースすべきです。

CSSの実装間での相互運用性を確立・維持するため、 CSSワーキンググループは非実験的なCSSレンダラーに対し、 プレフィックスなし実装をリリースする前に、実装レポートと(必要なら)その際に利用したテストケースを W3Cへ提出するよう依頼しています。W3Cに提出されたテストケースはCSSワーキンググループによるレビュー・修正の対象となります。

テストケースや実装レポートの提出方法については、CSSワーキンググループのウェブサイト http://www.w3.org/Style/CSS/Test/ で案内しています。質問はpublic-css-testsuite@w3.orgのメーリングリストで受け付けています。

CR終了基準

本仕様がProposed Recommendationへ進むためには、 各機能ごとに少なくとも2つの独立かつ相互運用可能な実装が必要となります。各機能は異なる製品セットで実装されてもよく、 すべての機能が単一製品で実装される必要はありません。この基準のために、以下の用語を定義します:

独立
各実装は異なる開発者が独立して開発したものであり、他の適格な実装のコードを共有・再利用・派生してはなりません。 本仕様の実装と関係しないコード部分はこの要件から除外されます。
相互運用可能
公式CSSテストスイートの該当テストケースに合格する、またはWebブラウザ以外の場合は同等のテストに合格すること。 該当UAが相互運用性を主張する場合、同じテストに合格できる追加のUAが必要であり、 その同等テストは公開され、ピアレビュー可能でなければなりません。
実装
ユーザーエージェント(UA)で以下を満たすもの:
  1. 本仕様を実装している。
  2. 一般公開されている。製品版や公開ベータ・プレビュー版・「ナイトリービルド」なども含む。 非製品版リリースの場合は、少なくとも1ヶ月間当該機能を実装して安定性を示している必要がある。
  3. 実験的でない(テスト合格だけを目的としたバージョンではなく、今後も通常利用されるもの)。

本仕様は少なくとも6ヶ月間Candidate Recommendationとして公開されます。

索引

この仕様で定義される用語

参照によって定義される用語

参考文献

規範的参考文献

[CSS-ANIMATIONS-1]
Dean Jackson ほか. CSS Animations Level 1. 2018年10月11日. WD. URL: https://www.w3.org/TR/css-animations-1/
[CSS-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 5. 2022年1月13日. CR. URL: https://www.w3.org/TR/css-cascade-5/
[CSS-CONDITIONAL-3]
David Baron; Elika Etemad; Chris Lilley. CSS Conditional Rules Module Level 3. 2022年1月13日. CR. URL: https://www.w3.org/TR/css-conditional-3/
[CSS-PROPERTIES-VALUES-API-1]
Tab Atkins Jr. ほか. CSS Properties and Values API Level 1. 2020年10月13日. WD. URL: https://www.w3.org/TR/css-properties-values-api-1/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 2021年12月24日. CR. URL: https://www.w3.org/TR/css-syntax-3/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 3. 2019年6月6日. CR. URL: https://www.w3.org/TR/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. 2021年12月16日. WD. URL: https://www.w3.org/TR/css-values-4/
[CSS2]
Bert Bos ほか. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 2011年6月7日. REC. URL: https://www.w3.org/TR/CSS21/
[CSSOM-1]
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). 2021年8月26日. WD. URL: https://www.w3.org/TR/cssom-1/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. Selectors Level 4. 2018年11月21日. WD. URL: https://www.w3.org/TR/selectors-4/
[WEB-ANIMATIONS-1]
Brian Birtles ほか. Web Animations. 2021年5月18日. WD. URL: https://www.w3.org/TR/web-animations-1/

情報提供的参考文献

[CHARMOD-NORM]
Addison Phillips ほか. Character Model for the World Wide Web: String Matching. 2021年8月11日. NOTE. URL: https://www.w3.org/TR/charmod-norm/
[CSS-BACKGROUNDS-3]
Bert Bos; Elika Etemad; Brad Kemper. CSS Backgrounds and Borders Module Level 3. 2021年7月26日. CR. URL: https://www.w3.org/TR/css-backgrounds-3/
[CSS-BOX-4]
Elika Etemad. CSS Box Model Module Level 4. 2020年4月21日. WD. URL: https://www.w3.org/TR/css-box-4/
[CSS-CASCADE-6]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS Cascading and Inheritance Level 6. 2021年12月21日. WD. URL: https://www.w3.org/TR/css-cascade-6/
[CSS-COLOR-4]
Tab Atkins Jr.; Chris Lilley; Lea Verou. CSS Color Module Level 4. 2021年12月15日. WD. URL: https://www.w3.org/TR/css-color-4/
[CSS-EXTENSIONS]
Tab Atkins Jr.. CSS Extensions. ED. URL: https://drafts.csswg.org/css-extensions/
[CSS-FONTS-4]
John Daggett; Myles Maxfield; Chris Lilley. CSS Fonts Module Level 4. 2021年12月21日. WD. URL: https://www.w3.org/TR/css-fonts-4/

プロパティ索引

名前 初期値 適用対象 継承 %ages アニメーション型 標準順序 算出値
--* <declaration-value>? 保証された無効値 全要素および全ての疑似要素(制限付きプロパティリストを持つものも含む) はい n/a 離散的 文法に従う 変数を代入した指定値、または保証された無効値