Polymer Elementの作成者は、アプリケーション内でエレメントの見た目をスタイリングするのにカスタムCSSプロパティを提供することができます。

CSSの作成者は、カスタムプロパティによって、あらゆるCSSプロパティで利用できるカスケード式のCSS変数を定義できます。

CSS変数はコンテキスト外で使用することができ、スタイルシート全体にスタイル情報が散らばるのを防げます。CSSの作成者がカスタムプロパティに値を割り当てれば、var()関数を用いてスタイルシートの他のどんな場所からも利用できます。

このようにカスタムCSSプロパティを利用することでCSSの編集は遥かに容易になり、エラーの発生も抑制できます。

例えば、<paper-checkbox>エレメントは、チェックボックスの色や間隔、サイズ、ラベルをスタイリングするためのカスタムプロパティを提供しています。

開発者は、これらのプロパティを利用して、アプリケーションの中で<paper-checkbox>エレメントのスタイルを設定できます。

独自のCustom Elementsを作成する際は、カスタムプロパティを用いてエレメントの利用者に向けてインターフェイスを構築し、彼/彼女らがエレメントのスタイルを設定できるようにします。

エレメントの作成者が用意したカスタムプロパティのインターフェイスを利用する場合は、エレメントのAPIドキュメントを参照してください。

模範的なサンプルとして、<paper-checkbox> API documentationを参照してください。

以下のコードサンプルでは、<paper-checkbox>をデフォルトのスタイルを適用しエレメントに挿入しています。:

Plunkerで動作を確認

<base href="//polygit2.appspot.com/components/">
<link rel="import" href="polymer/polymer.html">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="paper-checkbox/paper-checkbox.html">

<paper-checkbox>Check me</paper-checkbox>

次の点に注意してください。

  • チェックボックスのラベルのデフォルトのフォントはTimes New Romanです。これはスタイル情報の無い全てのWebページに適用されます。
  • チェックボックスは、paper-elementのテーマからデフォルトのカラーを受け取ります。(訳注:paper-elementsに共通のスタイルは、paper-stylesに定義されています。ここで定義されているスタイルは、Googleの提唱するマテリアルデザインのガイドラインに沿ったものです。).

<paper-checkbox>エレメントのスタイルプロパティは、エレメントの作成者が用意したカスタムCSSプロパティを通じて設定できます。

Custom Elementのカスタムプロパティを利用するには、次の構文でスタイルルールを定義します。:

paper-checkbox {
  --paper-checkbox-checked-color: red;
}

Plunkerで動作を確認

Paper Elementsは、これらを複数利用した場合に、変数を用いてこれらのエレメント群に対して一貫したスタイリングを行う手段を提供しています。

<paper-checkbox>の中で変数を使用してカスタムCSSプロパティを設定できます。:

<style is="custom-style">
  p {
    color: var(--paper-red-500);
  }
  paper-checkbox {
    --paper-checkbox-checked-color: var(--paper-red-500);
  }
</style>

エレメントの作成者は、テーマに関する内部実装の詳細を公開する代わりに、エレメントのAPIの一部としてカスタムCSSプロパティを一つ以上定義します。

これらのカスタムプロパティは、他の標準的なCSSプロパティと同様に定義することができ、定義された時点で合成されたDOMツリー(composed DOM tree)を通じて下位にスタイルを継承します。これはcolorfont-familyが及ぼす影響に似ています。

以下の簡単な例において、<my-toolbar>の作成者は、エレメントの利用者によってツールバーのタイトルの色を変更できるようにすべきと考えました。そこで作成者は、セレクタ.titlecolorプロパティに--my-toolbar-title-colorというカスタムプロパティを割り当て公開しました。ツールバーの利用者は、ツリー上のどこかのCSSルールにおいて、この変数を定義するかもしれません。変数が定義された場合、他の標準なCSSプロパティの継承と同様に、プロパティの値は定義された箇所からツールバーへ下位に継承されます。

例:

<dom-module id="my-toolbar">
  <template>
    <style>
      :host {
        padding: 4px;
        background-color: gray;
      }
      .title {
        color: var(--my-toolbar-title-color);
      }
    </style>
    <span class="title">{{title}}</span>
  </template>
  <script>
    class MyToolbar extends Polymer.Element {
      static get is() {
        return "my-toolbar";
      }
    }
    customElements.define(MyToolbar.is, MyToolbar);
</script>
</dom-module>

<my-toolbar>の利用例:

<dom-module id="my-element">
  <template>
    <style>
      /* Make all toolbar titles in this host green by default */
      :host {
        --my-toolbar-title-color: green;
      }
      /* Make only toolbars with the .warning class red */
      .warning {
        --my-toolbar-title-color: red;
      }
    </style>
    <my-toolbar title="This one is green."></my-toolbar>
    <my-toolbar title="This one is green too."></my-toolbar>
    <my-toolbar class="warning" title="This one is red."></my-toolbar>
  </template>
  <script>
    class MyElement extends Polymer.Element {
      static get is() {
        return "my-element";
      }
    }
    customElements.define(MyElement.is, MyElement);
  </script>
</dom-module>

この--my-toolbar-title-colorプロパティは、<my-toolbar>内部実装でカプセル化されたtitleエレメントのcolorだけに作用します。<my-toolbar>の作成者は、ユーザーに対して公開されたカスタムプロパティに変更を加えなければ、クラス名titleを変更したり、<my-toolbar>の内部実装を再構築したりすることができます。

利用者がカスタムプロパティを設定しない場合に備えて、var()関数にデフォルト値を含めることもできます。:

color: var(--my-toolbar-title-color, blue);

デフォルト値としてカスタムプロパティを利用する場合には、次のような構文を使用します。:

color: var(--my-color, var(--my-default-color))

このように、カスタムCSSプロパティは、通常のCSSによるスタイリングと自然に適合するように、Custom Elementの作成者がテーマ設定に関するAPIを利用者に向けて公開するための有用な手段といえます。

(訳注:CSSミックスインに関する詳細は、公開中の提案書を参照してください。) エレメントの作成者が、テーマ設定に関して大事と考えられる全てのCSSプロパティを予測し、それらを個別に公開するのはやっかい(あるいは不可能)なことです。

CSSミックスインは、この機能面の課題を解決するための提案です。CSSミックスインを使用するには、CSSミックスインのポリフィルをインポートします。:

<!-- import CSS mixins polyfill -->
<link rel="import" href="/bower_components/shadycss/apply-shim.html">

下位互換性を保つため、polymer.htmlのインポートにはCSSミックスインのポリフィルも含まれています。ハイブリッドエレメントを定義する場合には、追加のインポートは不要です。

CSSミックスインを使用する場合、エレメントの作成者はCSSのプロパティのセットを一つのカスタムプロパティとして定義することができ、エレメントのShadow DOM内に記述されたCSSルールとして、セット内の全てのプロパティが適用されることになります。このような拡張はミックスイン機能によって実現されますが、これは一見varに類似していますが、プロパティのセットをまとめてミックスインすることが可能です。

ミックスインを適用するには@applyを使用します:

@apply --mixin-name;

ミックスインの定義は、カスタムプロパティの定義と似ていますが、値として一つ以上のルールを定義したオブジェクトをとる点が異なります。:

selector {
  --mixin-name: {
    /* rules */
  };
}

例:

<dom-module id="my-toolbar">
  <template>
    <style>
      :host {
        padding: 4px;
        background-color: gray;
        /* apply a mixin */
        @apply --my-toolbar-theme;
      }
      .title {
        @apply --my-toolbar-title-theme;
      }
    </style>
    <span class="title">{{title}}</span>
  </template>
  ...
</dom-module>

my-toolbarの利用例::

<dom-module id="my-element">
  <template>
    <style>
      /* Apply custom theme to toolbars */
      :host {
        --my-toolbar-theme: {
          background-color: green;
          border-radius: 4px;
          border: 1px solid gray;
        };
        --my-toolbar-title-theme: {
          color: green;
        };
      }
      /* Make only toolbars with the .warning class red and bold */
      .warning {
        --my-toolbar-title-theme: {
          color: red;
          font-weight: bold;
        };
      }
    </style>
    <my-toolbar title="This one is green."></my-toolbar>
    <my-toolbar title="This one is green too."></my-toolbar>
    <my-toolbar class="warning" title="This one is red."></my-toolbar>
  </template>
  <script>
    class MyElement extends Polymer.Element {
      static get is() {
        return "my-element";
      }
    }
    customElements.define(MyElement.is, MyElement);
  </script>
</dom-module>

エレメントがスタイル情報を上書きしなければ、エレメントはその親からスタイルを継承します。:

<link rel="import" href="components/polymer/lib/elements/custom-style.html">
<custom-style>
  <style is="custom-style">
    p {
      color: var(--paper-red-900);
      font-family: Sans-serif;
    }
    paper-checkbox {
      --paper-checkbox-checked-color: var(--paper-red-900);
    }
  </style>
</custom-style>
<body>
	<p><paper-checkbox>Check me</paper-checkbox></p>
</body>

ドキュメントのhtmlエレメントをスタイリングすることで、グローバルなスタイルを記述できます。:

<link rel="import" href="components/polymer/lib/elements/custom-style.html">
<custom-style>
  <style is="custom-style">
    html {
      font-family: Sans-serif;
      --my-color: var(--paper-red-900);
      color: var(--my-color);
    }
    paper-checkbox {
      --paper-checkbox-checked-color: var(--my-color);
    }
  </style>
</custom-style>

フォントファミリーは継承されますが、テキストカラーは継承されない点に注意してください。これは<paper-checkbox>がテキストカラーを上書きしているためです。.

Polymerのカスタムプロパティのshimは、エレメントの生成時に一度だけカスタムプロパティの値を評価し適用します。CSSクラスの適用など動的な変更があった場合にエレメント(及びそのサブツリー)を再評価するためには、エレメント上でupdateStylesメソッドを呼び出して下さい。ページ上のすべてのエレメントをアップデートするには、Polymer.updateStylesを呼び出します。

updateStylesは引数にプロパティ/値のペアを持つオブジェクトをとり、カスタムプロパティの現在の値をアップデートすることができます。

例:

<dom-module id="x-custom">
  <template>
    <style>
      :host {
        --my-toolbar-color: red;
      }
    </style>
    <my-toolbar>My awesome app</my-toolbar>
    <button on-tap="changeTheme">Change theme</button>
  </template>
  <script>
    class XCustom extends Polymer.Element {
      static get is() {
        return "x-custom";
      }
      static get changeTheme() {
        return function() {
        this.updateStyles({
          '--my-toolbar-color': 'blue',
        });
      }
    }
    customElements.define(XCustom.is, XCustom);
  </script>
</dom-module>
this.updateStyles({
  '--some-custom-style': 'green',
  '--another-custom-style': 'blue'
});

場合によっては、実行時にエレメントがカスタムプロパティの値を取得する必要があるかもしれません。このようなケースでは、ポリフィルShady CSSがロードされているかどうかによって処理が多少異なります。:

if (ShadyCSS) {
  style = ShadyCSS.getComputedStyleValue(this, '--something');
} else {
  style = getComputedStyle(this).getPropertyValue('--something');
}

レガシーAPIを利用するエレメントでは、Shady CSSのロードをテストする代わりに、インスタンスメソッドgetComputedStyleValueを呼び出してみることができます。

クロスプラットフォーム上でカスタムプロパティをサポートするため、Polymerは内部でJavaScriptのライブラリを利用しています。ライブラリはCSS Variablesの仕様(テーマ設定に利用される仕様)にほぼ準拠しながらも、これまで説明してきた通り、その機能を拡張してミックスインプロパティ(CSSのスタイルルールをセットで定義できる機能)もサポートしています。パフォーマンス上の理由から、Polymerはネイティブのカスタムプロパティのすべての機能を再現するわけではありません。shimは、CSSのダイナミズムの可能性を最大限活かすため、実用性とパフォーマンスの利益のトレードオフを調整します。

現時点におけるshimの制限は以下のとおりです。パフォーマンスとダイナミズムの改善については引き続き検討が行われています。

エレメントの生成時においては、エレメントとマッチしたプロパティの定義だけが適用されます。プロパティ値を更新する動的な変更が自動的に適用されることはありません。Polymer Element上でupdateStylesメソッドを呼び出すか、Polymer.updateStylesを使って全てのエレメントのスタイルをアップデートすることで、スタイルの再評価を強制的に適用することができます。

例えば、エレメント内に次のようなマークアップがあるとします。:

HTML:

<div class="container">
  <x-foo class="a"></x-foo>
</div>

CSS:

/* applies */
x-foo.a {
  --foo: brown;
}
/* does not apply */
x-foo.b {
  --foo: orange;
}
/* does not apply to x-foo */
.container {
  --nog: blue;
}

上記のx-fooにクラスbを追加した後、新しいスタイルを適用するためにホストエレメントはthis.updateStylesを呼び出す必要があります。この時点でスタイルの再計算が行われ、ツリーを下ってスタイルが適用されます。

動的な影響は、プロパティの適用時点で反映されます。

以下の例において、#titleエレメントにhilightedクラスを追加/削除することで狙い通りの効果をもたらすことができるのは、動的変更による影響がカスタムプロパティの適用に関連づけられているためです。

#title {
  background-color: var(--title-background-normal);
}

#title.highlighted {
  background-color: var(--title-background-highlighted);
}

親から子へ向かう通常のCSSの継承と異なり、Polymerのshimによって提供されるカスタムプロパティでは、上位のスコープ内や、:hostルールのスコープ内でプロパティが設定され、Custom Elementに継承されている場合に限り変更することができます。エレメントのローカルDOMのスコープ内では、カスタムプロパティに設定できるのは単一の値だけです。スコープ内でのプロパティ変更の計算は、shimにとって非常に大きなコストととなるばかりか、Custom Elementのスコープを超えたスタイリングというshimの主目的からすれば必要なものではありません。

<dom-module id="my-element">
  <template>
    <style>
     :host {
       --custom-color: red;
     }
     .container {
       /* Setting the custom property here will not change */
       /* the value of the property for other elements in  */
       /* this scope.                                      */
       --custom-color: blue;
     }
     .child {
       /* This will be always be red. */
       color: var(--custom-color);
     }
    </style>
    <div class="container">
      <div class="child">I will be red</div>
    </div>
  </template>
  <script>
    class MyElement extends Polymer.Element {
      static get is() {
        return "my-element";
      }
    }
    customElements.define(MyElement.is, MyElement);
  </script>
</dom-module>

カスタムプロパティのshimは、割り当てられたエレメント(distributed elements)へのスタイリングはサポートしていません。

/* Not supported */
:host ::slotted(div) {
  --custom-color: red;
}