CSS - @property

目次

概要

ルール名@property
構文@property --name {
    syntax: "type";
    inherits: bool;
    initial-value: value;
}
サポートhttps://caniuse.com/mdn-css_at-rules_property

CSS のカスタムプロパティ(--変数) の利用に制約を加えることにより、コードの安全性を高めるものです。2020年の Chrome 85 や 2023年の Firefox 128 でサポートされました。

説明

CSS では下記の様に変数(カスタムプロパティ)を定義・使用することができます。

:root {
  --text-color: #333;
}
body {
  color: var(--text-color);
}

@property を用いて上記を書き換えると次のようになります。

@property --text-color {
  syntax: "<color>";
  inherits: false;
  initial-value: #333;
}
body {
  color: var(--text-color);
}

@property を用いることにより次のようなメリットがあります。

ただし、色々な注意点も多いので、利用する際には後述の「注意点」を参照してください。

詳細

プロパティ定義(@property)

プロパティを下記の様に定義します。

@property --text-color {
  syntax: "<color>";
  inherits: false;
  initial-value: #333;
}

syntaxinherits は省略できません。initial-valuesyntax"*" の時は 省略できますが、それ以外の時は省略できません。省略すると @property 定義自体が無効となります。

名前(--variable-name)

変数名を定義します。変数名は今後の CSS の拡張とバッティングしないように -- で始める必要があります。

@property --text-color {
  syntax: "<color>";
  inherits: false;
  initial-value: #333;
}

シンタックス(syntax)

syntax は変数に格納できる値の型を制限します。

@property --text-color {
  syntax: "<color>";
  inherits: false;
  initial-value: #333;
}

型名

型には下記の型名や auto などのキーワードを指定します。

スペース区切りの複数指定(+)

型に + をつけるとスペース区切りの複数個の値が指定可能となります。

@property --my-padding {
  syntax: "<length>+";
  inherits: false;
  initial-value: 8px 16px;
}

カンマ区切りの複数指定(#)

型に # をつけるとカンマ区切りの複数個の値が指定可能となります。

@property --my-color {
  syntax: "<number>#";
  inherits: false;
  initial-value: 255, 128, 128;
}

もしくは(|)

A | BA または B を意味します。

@property --my-width {
  syntax: "<length> | <percentage>";
  inherits: false;
  initial-value: 50vw;
}
@property --my-color {
  syntax: "red | green | blue";
  inherits: false;
  initial-value: red;
}

全称構文定義(*)

* はすべての型を受け付けます。

@property --my-border {
  syntax: "*";
  inherits: false;
  initial-value: 1px solid gray;
}

型定義に合致しない場合

initial-value に指定した値が syntax に違反する場合、@property 定義自体が無効となります。つまり、syntax で宣言した型宣言も無効となります。

@property --my-name {
  syntax: "<color>";    /* 型宣言も無効となる */
  inherits: false;
  initial-value: 24px;  /* 型宣言と合致していない */
}

上記以外の場所で型宣言に違反する値を代入しようとすると代入が無効となり、initial-value で宣言した値や、継承可能な場合は先祖要素で正常に代入された値が参照されます。

@property --my-name {
  syntax: "<color>";
  inherits: false;
  initial-value: red;
}
.test {
  --my-name: 24px;           /* 型宣言に合致しない無効な値 */
  font-size: var(--my-name); /* red と解釈される */
}

継承(inherits)

inherits は祖先要素で上書きした値が子孫要素に継承されるか否かを指定します。継承不可(false) にしておくことで、祖先コンポーネントで値が上書きされたとしても、@property で定義した値を確実に参照できるようになります。

@property --text-color {
  syntax: "<color>";
  inherits: true;
  initial-value: red;          /* 初期値 */
}
.parent {
  --text-color: blue;          /* 祖先要素で上書き */
  .child {
    color: var(--text-color);  /* 継承可(true)であればblue、継承不可(false)であればred */
  }
}

初期値(initial-value)

初期値を指定します。

@property --text-color {
  syntax: "<color>";
  inherits: false;
  initial-value: red;
}

inheritssyntax"*" の時は省略可能ですが、それ以外の場合は省略できません。省略すると @property 定義自体が無効となり、型宣言も無効となります。

@property --my-name {
  syntax: "<color>";
  inherits: false;
  /* initial-value を省略 */
}
.parent {
  --my-name: 24pt;           /* 型宣言も無効なので24ptも代入できてしまう */
  font-size: var(--my-name);
}

変数を用いたアニメーションの改善

下記は変数を用いたアニメーションの例です。赤から白にグラデーションした背景画像を、3秒かけて白から赤のグラデーションにアニメーションするものですが、色が徐々に変化するのではなく、瞬時に切り替わってしまいます。

<style>
@keyframes my-animation {
  0%   { --start: red;   --end: white; }
  100% { --start: white; --end: red; }
}
.gradient {
  width: 300px;
  height: 50px;
  animation: my-animation 3s infinite alternate;
  background-image: linear-gradient(to right, var(--start), var(--end));
}
</style>
<div class="gradient"></div>

これは、CSS が redwhite といったキーワードを色名として認識することができず、文字列と解釈してしまうことが原因です。下記の宣言を追加して --start--end が色名であることを宣言することで、変数を用いたグラデーションアニメーションが期待通りに動くようになります。

@property --start {
  syntax: "<color>";
  inherits: false;
  initial-value: red;
}
@property --end {
  syntax: "<color>";
  inherits: false;
  initial-value: white;
}

注意点

syntax, inherits, initial-value は省略できない

syntax"*" の時に initial-value を省略できる以外には、syntax, inherits, initial-value は省略することができません。省略すると @property 定義自体が無効となってしまいます。

@property --my-color {
  syntax: "<color>
  inherits: false;
  /* initial-value を省略してしまったので @propety 自体が無効 */
}

auto などのキーワードには <custom-indent> 型を指定する

autobold などのキーワードは <custom-ident> 型で指定します。

@property --my-font-weight {
  syntax: "<custom-ident>";
  inherits: false;
  initial-value: bold;
}

型を複数指定できない

syntax には +# による複数繰り返し以外には、型を複数指定することができないようです。例えば下記のような記述はできません。

@property --my-padding {
  syntax: "<length> <length>";
  inherits: false;
  initial-value: 8px 16px;
}

独立計算可能な初期値しか設定できない

initial-value には独立計算可能な値しか設定することができません。例えば、24px などの値は独立計算可能ですが、1em などの値は親要素のフォントサイズに依存しており独立計算できないため設定が無効となり、@property 定義自体が無効となります。

@property --font-size {
  syntax: "<length>";
  inherits: false;
  initial-value: 3em;     /* 独立計算できないので無効 */
}

誤っていてもエラーが出る訳ではない

@property の書き方を誤っていたり、syntax で定義した型以外の値を設定したとしてもどこかにエラーメッセージが表示される訳ではありません。開発者ツールの [Elements] で表示される @property 一覧で確認するなど、正常に設定されているかを確認する必要があります。

@property --font-size {
  syntax: "<length>";
  inherits: false;
  initial-value: 3em;     /* ← 誤って設定していてもエラーメッセージは無い */
}

<length-percentage> と <length> | <percentage> は違う

<length-percentage><length> | <percentage> は多少異なります。前者は長さとパーセンテージの混在が許されますが、後者は混在が許されません。

@property --my-size-1 {
  syntax: "<length-percentage>+";
  inherits: false;
  initial-value: 100% 100px;   /* 混在が許される */
}
@property --my-size-2 {
  syntax: "<length>+ | <percentage>+";
  inherits: false;
  initial-value: 100% 100px;   /* 混在は許されない */
}

下記も同様に混在とみなされます。

@property --my-size-1 {
  syntax: "<length-percentage>";
  inherits: false;
  initial-value: calc(100% - 100px);   /* 混在が許される */
}
@property --my-size-2 {
  syntax: "<length> | <percentage>";
  inherits: false;
  initial-value: calc(100% - 100px);   /* 混在は許されない */
}

初期値に initial や inherit などは使用できない

initial-valueinitial, inherit, unset, revert, rever-layer の値を設定することはできません。設定すると @property 定義自体が無効となります。

@property --my-name {
  syntax: "*";
  inherits: false;
  initial-value: initial;  /* 設定できない */
}

仕様への要望

使用してみての感想ですが、次のような強化があればな...と思います。

inheritsinitial-value は省略できるようにして欲しい。

@property --my-color {
  syntax: "<color>";
  /* inherits省略 */
  /* initial-value省略 */
}

下記のようなシンタックスもサポートしてほしい。

syntax: "<length> <length>";
syntax: "<length>{1,4}";
syntax: "[<length> | <percentage>]#";

autosolid などのキーワードも --my-name などのカスタムプロパティも型には <custom-ident> を指定しますが、<keyword><custom-ident> に分けてほしい。

@property --default-border {
  syntax: "<length> <keyword> <color>";
  inherits: false;
  initial-value: 1px solid gray;
}

あとは、開発者ツールのコンソールでエラー表示したり、Nu Html Checker などのバリデーションツールでエラー検出して欲しいですね。