CSS の `@property` ルールでカスタムプロパティを定義する
CSS の @property ルールを使うことで、CSS のカスタムプロパティを定義できます。カスタムプロパティを定義することでプロパティの構文チェック、デフォルト値の設定、プロパティが値を継承するかどうかの設定などが可能になります。カスタムプロパティに誤った値が代入されることを防いだり、グラデーションのアニメーションなど、従来は実装が難しかった機能をサポートすることができます。
CSS の @property ルールを使うことで、CSS のカスタムプロパティ を定義できます。カスタムプロパティを定義することでプロパティの構文チェック、初期値の設定、プロパティが値を継承するかどうかの設定が可能になります。
@property --my-color {
/** red や #fff のように色のキーワードまたはカラーコードを代入可能 */
syntax: "<color>";
inherits: false;
initial-value: red;
}
/* カスタムプロパティを使用 */
.my-element {
--my-color: blue;
color: var(--my-color);
}@property ルールのメリット
@property ルールを使うことで、以下のようなメリットがあります。
- カスタムプロパティによりコードをドキュメント化する
- 誤った値の代入を防ぎ、適切なエラーメッセージを表示する
- グラデーションのアニメーションなど、従来は実装が難しかった機能をサポートする
コードのドキュメント化・誤った値の代入を防ぐ
通常のカスタムプロパティの定義は以下のような記法で定義されます。
:root {
--primary: #7bd389;
--secondary: #607466;
}:root にカスタムプロパティを定義することで、コードベース全体で使用できるカスタムプロパティを定義できます。このような方法はプロダクトで統一されたテーマを適用したい場合によく使われます。ボタンの background-color に #7bd389 と記述されているよりも、カスタムプロパティを使用して var(--primary) とあるほうが意味がより伝わりやすいというメリットがあります。また、テーマを変更したくなった場合に、--primary の値を変更するだけで全体のテーマを変更できるため、保守性が高くなります。
一方でカスタムプロパティは意図しない値が代入される危険があります。ここでは --primary という変数には色の値が入るべきであるという前提がありますが、誤って --primary に文字列や数値が代入されてしまうと、意図しない挙動を引き起こす可能性があります。
例えば --primary というカスタムプロパティがすでに存在することを知らずに、同じ名前のカスタムプロパティでうっかり上書きしてしまうかもしれません。
.card {
--primary: 100px;
width: var(--primary);
}カスタムプロパティが複数の場所で定義されていた場合には、最も近いスコープのカスタムプロパティが優先されます。もし .ca rd クラスの配下で --primary に色の値が代入されていることを前提としていたコードが記述されていた場合、思わぬデザイン崩れが発生してしまうかもしれません。
:root {
--primary: #7bd389;
--secondary: #607466;
}
.card {
--primary: 100px;
width: var(--primary);
}
/** button.css */
.button {
/* 意図せず無効な値が代入されてしまっている */
background-color: var(--primary);
}@property ルールでは syntax フィールドにデータ型を指定することで、カスタムプロパティに代入される値の型を制限できます。:root で行っていたカスタムプロパティの定義は以下のように記述できます。
@property --primary {
syntax: "<color>";
inherits: true;
initial-value: #7bd389;
}
@property --secondary {
syntax: "<color>";
inherits: true;
initial-value: #607466;
}第一に syntax フィールドに <color> と指定されているおかげで、開発者は --primary に代入される値がカラーコードであることをコードから読み取ることができます。静的型付け言語のようにコードをドキュメント化できるという点がメリットと言えるでしょう。
もし --primary に無効な値、つまりカラーコード以外の値が代入された場合には、プロパティの値は初期値(initial-value で設定した値)にフォールバックします。下記のコードでは .card クラスの --primary に 100px が代入されていますが、これは無効な値であるため、--primary の値は初期値である #7bd389 にフォールバックします。
@property --primary {
syntax: "<color>";
inherits: true;
initial-value: #7bd389;
}
@property --secondary {
syntax: "<color>";
inherits: true;
initial-value: #607466;
}
.card {
/** この値は `<color>` というデータ型に一致しないため */
--primary: 100px;
/** 初期値にフォールバックされるため、`var(--primary)` は `#7bd389` になる */
width: var(--primary);
}このように開発者はすぐにコードの誤りに気づくことができ、意図せぬデザイン崩れを未然に防ぐことができます。Developer Tools の Styles タブを見ると、誤った値が代入された場合には invalid と表示されるていることがわかります。適切なエラーメッセージを表示することで、開発者が問題を解決する手助けとなります。

グラデーションのアニメーションなど、従来は実装が難しかった機能をサポートする
CSS でグラデーションのアニメーションを実装する例を考えてみましょう。通常のカスタムプロパティを使って実装では以下のようなコードになるでしょう。
:root {
--color1: #7bd389;
--color2: #607466;
--angle: 0deg;
}
@keyframes gradient {
0% {
--angle: 0deg;
}
100% {
--angle: 360deg;
}
}
.gradient {
width: 100px;
height: 100px;
background: linear-gradient(var(--angle), var(--color1), var(--color2));
animation: gradient 3s linear infinite;
}@keyframes ルールでグラデーションのアニメーションを定義し、--angle カスタムプロパティがアニメーションの進行度に応じて増加するようにしています。.gradient クラスでは animation プロパティで gradient アニメーションを適用し、3 秒かけて 360 度回転するグラデーションを実装しています。
しかし、このコードは期待と反して正しく動作しません。
これは CSS のアニメーションがすべてのカスタムプロパティを文字列として解析しているためです。0deg → 360deg を数値の増加として解釈できないため、アニメーションが正しく動作しないのです。
@property ルールを使い、<angle> というデータ型を指定することで、この問題を解決できます。
@property --angle {
syntax: "<angle>";
inherits: true;
initial-value: 0deg;
}このように記述することで、--angle に代入される値が角度であることを明示できるため、正しくブラウザによって解釈されるようになります。下記の例でグラデーションのアニメーションが行われていることが確認できます。
カスタムプロパティによる構文チェック
@property ルールの syntax フィールドには許容される構文を指定します。記述できる構文は、あらかじめ定義されたデータ型、もしくは記号を使ってデータ型拡張して指定できます。
あらかじめ定義されたデータ型は以下のようなものがあります。
<length><number><percentage><length-percentage><color><image><url><integer><angle><time><resolution><transform-function><custom-ident>
例えば <length> は長さの値を表すデータ型で、width や height、margin などのプロパティに使用されます。<length> は 42px、1.5rem、100% などのように <number> 型と 1 つ以上の単位を組み合わせたものです。
記号を使ったデータ型の拡張
あらかじめ定義されたデータ型に加えて、以下の記号を使用してデータ型を指定できます。
+:スペースで区切られたリスト#:カンマで区切られたリスト|:or で複数のデータ型を指定
例として <length>+ と定義されたデータ型は 1rem 2rem 3rem のように padding や margin などのプロパティに使用される複数の長さの値を表します。
@property --my-padding {
syntax: "<length>+";
inherits: true;
initial-value: 0;
}
.my-element {
--my-padding: 1rem 2rem 3rem;
padding: var(--my-padding);
}<color># と定義されたデータ型は #7bd389, #607466 のようにカンマで区切られた複数の色の値を表します。これは rgp() や hsl() などの色の値を複数指定する際に使用されます。
@property --my-color {
syntax: "<color>#";
inherits: true;
initial-value: #7bd389;
}
.my-element {
--my-color: #7bd389, #607466;
background: linear-gradient(to right, var(--my-color));
}<length>|<percentage> と定義されたデータ型は 1rem | 100% のように長さとパーセンテージのどちらかの値を表します。これは width や height などのプロパティに使用されます。
@property --my-size {
syntax: "<length>|<percentage>";
inherits: true;
initial-value: 100%;
}
.my-element {
--my-size: 1rem | 100%;
width: var(--my-size);
}red | blue | green のように具体的な値を指定することもできます。
@property --my-color {
syntax: "red | blue | green";
inherits: true;
initial-value: red;
}
.my-element {
--my-color: blue;
color: var(--my-color);
}あらゆるデータ型を指定する *
syntax に * を指定することで、あらゆるデータ型を指定できます。この場合、カスタムプロパティにはどのような値が代入されてもエラーが発生しません。
@property --my-value {
syntax: "*";
inherits: true;
initial-value: 0;
}
.my-element1 {
--my-value: 24px;
font-size: var(--my-value);
}
.my-element2 {
--my-value: blue;
background-color: var(--my-value);
}カスタムプロパティの値の継承を防ぐ
@property ルールの inherits フィールドに false を指定することでカスタムプロパティに代入した値の継承を防ぐことができます。inherits フィールドに false を指定した場合、カスタムプロパティに代入された値はその要素のみに適用され、その要素の子孫要素には適用されません。
例を見てみましょう。inherits フィールドに true を指定した場合、.parent クラスに --my-color に blue が代入されているため、.child クラスにも blue が適用されます。これは通常の方法でカスタムプロパティを定義した場合と同じ挙動です。
inherits フィールドに false を指定した場合、子要素の .child クラスには --my-color の初期値である red が適用されます。
inherits: false を指定することで意図せぬ子要素に影響を与えることを防ぐことができます。
まとめ
@propertyルールを使うことで、カスタムプロパティに対してデータ型の指定、初期値の設定、値の継承の有無を設定できる- カスタムプロパティに対してデータ型を指定することで、誤った値の代入を防ぎ、適切なエラーメッセージを表示することができる
@propertyルールでデータ型を指定することでグラデーションのアニメーションなど従来は実装が難しかった機能をサポートすることができる
