【Polymer 1.0】スタイリング
目次
導入
Polymerは、Shadow DOMスタイリングのルールに従い、カスタムエレメントのLocal DOMに対するScopedスタイル(範囲を限定して適用されるスタイル)を提供します。Scopedスタイルは<style>
タグをLocal DOMテンプレート(<template>
タグ)の中に配置します。
以下はシンプルなスタイリングのサンプルです。:host
はカスタムエレメント自身のスタイルを定義する場所です。::content
擬似エレメントは<content>
タグに挿入されるLight DOMのエレメントに対するスタイルを定義します。
<dom-module id="my-element"> <template> <style> :host { display: block; border: 1px solid red; } #child-element { background: yellow; } .content-wrapper ::content > .special { background: orange; } </style> <div id="child-element">In local DOM!</div> <div class="content-wrapper"> <content></content> </div> </template> <script> Polymer({ is: 'my-element' }); </script> </dom-module>
<my-element> <div class="special">In light DOM!</div> </my-element>
カスタムエレメントの外にスタイルを配置したい、またはカスタムエレメント間でスタイルを共有するには、共有スタイルと外部スタイルシートを参照ください。
<content>タグに挿入されるエレメントのスタイリング
このセクションでは<content>
タグに挿入されるエレメントにスタイルを適用する方法を示します。CSSでは<content>
タグを::content
擬似エレメントで表します。
::content
擬似エレメントの左側には親を示すセレクタが必要です:
:host ::content div
これは次のような意味になります(実際にこうなるわけではありません):
my-element div
Shady DOM環境では、Local DOMの表示用DOMツリーに<content>
タグは現れません。このため、実行時には::content
擬似エレメントはCSSから削除され、また::content
のすぐ左にCSSコンビネータ(>, +, ~)がある場合も削除されます。
- 次は
<content>
タグ直下に挿入されるエレメントにスタイル適用を限定します。::content
の右にはもちろんCSSコンビネータ(>, +, ~, 子孫セレクタ など)も指定できます:
:host ::content > .special
これは次のような意味になります(実際にこうなるわけではありません):
my-element > .special
次は上記をふまえたサンプルプログラムです:
<dom-module id="my-element"> <template> <style> /* スタイル適用を<content>タグ直下に挿入されるエレメントに限定する */ :host ::content > .special { background: greenyellow; } </style> <content></content> </template> <script> Polymer({ is: 'my-element' }); </script> </dom-module>
<my-element> <!-- このエレメントには.specialのスタイルが当たる --> <div class="special">私はグリーンイエローです。</div> <div> <!-- このエレメントには.specialを設定してもスタイルは当たらない --> <div class="special">私は透明です。</div> </div> </my-element>
スコープを横断するスタイリング
カスタムCSSプロパティ
Polymerは、W3Cの仕様であるCSS VariablesにおけるカスタムCSSプロパティとPolymerの差異を埋めるためのshim(緩衝材)を提供します。
カスタムCSSプロパティは、スタイルをカスタムエレメントのAPIの一部として外部に公開する際に使用します。例えばアプリケーションに"テーマ"を適用するような場合、カスタムエレメントの内部詳細を公開してテーマを適用するのではなく、カスタムエレメントでカスタムCSSプロパティを公開し、このプロパティに対してテーマを適用するといった場合に使用します。
カスタムCSSプロパティの値設定は次のように行います。プロパティの名前は先頭を--
で始めます:
--my-toolbar-title-color: #cc0000;
設定された値はvar()
関数を使用して取得できます:
color: var(--my-toolbar-title-color);
またvar()
関数はデフォルト値を設定することもできます。利用者がカスタムCSSプロパティの値設定を行わなかった場合はこの値が使用されます:
color: var(--my-toolbar-title-color, blue);
次のサンプルでは、my-toolbar
の作成者は利用者のニーズとしてタイトルを変更したい要望があることを認識しています。作成者は--my-toolbar-title-color
というカスタムCSSプロパティを定義し、このプロパティの値はツールバーのタイトルエレメントのcolor
プロパティに設定されます。
作成者側の例:
<dom-module id="my-toolbar"> <template> <style> :host { padding: 4px; background-color: gray; } .title { /* タイトル色のカスタムCSSプロパティを定義 */ color: var(--my-toolbar-title-color); } </style> <span class="title">{{titleName}}</span> </template> <script> Polymer({ is: 'my-toolbar', properties: { titleName: String } }); </script> </dom-module>
利用者側の例:
<dom-module id="my-element"> <template> <style> /* このカスタムエレメント内のmy-toolbarのタイトルはデフォルトで白 */ :host { --my-toolbar-title-color: white; } /* .warningクラスが設定されたmy-toolbarのタイトルは赤 */ .warning { --my-toolbar-title-color: red; } </style> <my-toolbar title-name="This one is white."></my-toolbar> <my-toolbar title-name="This one is white too."></my-toolbar> <my-toolbar class="warning" title-name="This one is red."></my-toolbar> </template> <script> Polymer({ is: 'my-element' }); </script> </dom-module>
カスタムCSSプロパティの値設定に関する制限
自身のカスタムエレメントが持つカスタムCSSプロパティへの値設定は:host
でしか行うことはできません。これ以外の場所での値設定は無効です。この内容を示す次のサンプルプログラムを確認してください:
<dom-module id="my-element"> <style> :host { /** * 自身のカスタムエレメントのカスタムCSSプロパティの設定は:hostでのみ可能です。 */ --custom-color: red; } .container { /** * 自身のカスタムエレメントのカスタムCSSプロパティの設定は無効です。 * (他のカスタムエレメントのカスタムCSSプロパティは設定可能です) */ --custom-color: blue; } .child { /** * このカスタムCSSプロパティは常に赤になります。 * (:hostでredが設定されているので。.containerで設定された値は無効です。) */ color: var(--custom-color); } </style> <template> <div class="container"> <div class="child">I will be red</div> </div> </template> <script> Polymer({is: 'my-element'}); </script> </dom-module>
カスタムCSS Mixin
カスタムエレメントの作成者が、テーマの作成で必要となりそうな全てのCSSプロパティを洗い出し、定義することは難しいでしょう。
この問題の解決策として、Polymerが提供するカスタムプロパティのshimは、設定したいCSSプロパティ一式をパッケージ化し、パッケージされた全てのCSSプロパティをカスタムエレメントに適用する
カスタムエレメントに対してmixinを適用するには@apply
を使用します:
@apply(--mixin-name);
mixinの定義はカスタムCSSプロパティと似ていますが、設定する値はオブジェクトで、この中に1つ以上のスタイルを定義します:
.warning { --mixin-name: { color: red; font-weight: bold; }; }
次にmixinのサンプルを示します:
作成者側の例:
<dom-module id="my-toolbar"> <template> <style> :host { padding: 4px; background-color: gray; /* mixinを適用 */ @apply(--my-toolbar-theme); } .title { /* mixinを適用 */ @apply(--my-toolbar-title-theme); } </style> <span class="title">{{titleName}}</span> </template> <script> Polymer({ is: 'my-toolbar', properties: { titleName: {type: String} } }); </script> </dom-module>
利用者側の例:
<dom-module id="my-element"> <template> <style> /* 通常テーマのmixinを定義し、my-toolbarに適用する */ :host { --my-toolbar-theme: { background-color: green; border-radius: 4px; border: 1px solid gray; }; --my-toolbar-title-theme: { color: white; }; } /* 警告用テーマのmixinを定義し、.warningクラスが設定されたmy-toolbarに適用する */ .warning { --my-toolbar-title-theme: { color: red; font-weight: bold; }; } </style> <my-toolbar title-name="This one is white."></my-toolbar> <my-toolbar title-name="This one is white too."></my-toolbar> <my-toolbar class="warning" title-name="This one is red."></my-toolbar> </template> <script> Polymer({ is: 'my-element' }); </script> </dom-module>
カスタムCSSプロパティAPI
PolymerのカスタムCSSプロパティのshimは、カスタムエレメントの作成時に一度カスタムCSSプロパティの評価と適用を行います。ただし、テーマの変更などによってCSSクラスが動的に変更されるような場合、このカスタムCSSプロパティの再評価をエレメント(とそのサブツリー)にさせるために、this.updateStyles()
を実行してください。ページ上の全てのエレメントを更新させるには、Polymer.updateStyles()
を実行してください。
また、利用者はカスタムエレメントによって提供されるcustomStyle
にkey-valueペアを設定することで、カスタムCSSプロパティを直接編集することができます。その後updateStyles()
を実行してください。
次のサンプルでは、変更ボタンの押下によって、カスタムCSSプロパティを含んだクラスの付加/削除と、customStyle
でカスタムCSSプロパティを直接編集しています。また、この編集内容を反映するためにthis.updateStyles()
を実行しています:
作成者側の例:
<dom-module id="my-toolbar"> <style> :host { padding: 4px; background-color: var(--my-toolbar-bg); } .title { color: var(--my-toolbar-color); } </style> <template> <span class="title"><content></content></span> </template> <script> Polymer({ is: 'my-toolbar' }); </script> </dom-module>
利用者側の例:
<dom-module id="my-element"> <style> :host { --my-toolbar-bg: blue; --my-toolbar-color: white; } .warning { --my-toolbar-color: red; } </style> <template> <my-toolbar id="toolBar">My awesome app</my-toolbar> <button on-tap="changeOnTap">変更</button> </template> <script> Polymer({ is: 'my-element', _warn: false, changeOnTap: function () { this._warn = !this._warn; if (this._warn) { // my-toolbarにwarningクラス付加 this.$.toolBar.classList.add('warning'); // my-toolbarの背景色を緑に変更 this.customStyle['--my-toolbar-bg'] = 'green'; } else { // my-toolbarのwarningクラス削除 this.$.toolBar.classList.remove('warning'); // my-toolbarの背景色を青に変更 this.customStyle['--my-toolbar-bg'] = 'blue'; } // 変更したスタイルの反映(これを実行しないと変更が反映されない) this.updateStyles(); } }); </script> </dom-module>
メインドキュメントでのスタイリング
- ブラウザがShadow DOMをサポートしていなくても、
custom-style で定義さたスタイルはカスタムエレメントのLocal DOMへ影響しないことを保証します。 - ブラウザがShadow DOMをサポートしていなくても、Shadow DOMの仕様である
/deep/
と::shadow
コンビネータを使用できます。 custom-style でもカスタムCSSプロパティを定義できます。全てのカスタムエレメントに適用するカスタムCSSプロパティは:root
セレクタで定義してください。
<!DOCTYPE html> <html> <head> <script src="../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> <link rel="import" href="../bower_components/polymer/polymer.html"> <!-- custom-styleを使用してスタイルを定義 --> <style is="custom-style"> /* カスタムエレメントのLocal DOMに影響をあたえずに、アプリケーション全体の設定ができる */ div { color: white; font-style: italic; background-color: green; } /* /deep/ と ::shadow コンビネータを使用することができる */ body /deep/ my-element::shadow .title { color: red; } /* アプリケーション全体で使用可能なカスタムCSSプロパティを定義できる */ :root { --my-element-title-background: blue; } </style> </head> <body> <!-- メインドキュメントでアプリケーション全体で使用するdivに対してスタイルが定義されており、 これが適用されてHeaderとFooterの文字は白でイタリック、背景色は緑になる。 ただしmy-elementにはこのスタイルは適用されない。 --> <div>Header</div> <my-element></my-element> <div>Footer</div> <dom-module id="my-element"> <template> <style> /** * メインドキュメントで /deep/ と ::shadow を使用して.titleにスタイルが定義されており、 * それが適用されてタイトルの文字色が赤になる。 */ .title { /* メインドキュメントで設定された青が背景色に設定される */ background-color: var(--my-element-title-background); } </style> <div class="title">My Element!</div> </template> <script> document.addEventListener('WebComponentsReady', function () { Polymer({ is: 'my-element' }); }); </script> </dom-module> </body> </html>
カスタムエレメントでも上記のような:root
セレクタは例外で、このセレクタはドキュメントレベルでしか利用できません。
共有スタイルと外部スタイルシート
<dom-module>
の中にスタイル一式をパッケージすることより複数のカスタムエレメントでスタイル定義を共有することができます。このように<dom-module>
が保持するスタイルは
スタイルモジュールはパッケージされたスタイル一式を名前付けし、カスタムエレメントまたは
スタイルモジュールは<dom-module>
エレメントの中に定義してください:
shared-styles.html
<dom-module id="shared-styles"> <template> <style> .red { color: red; } </style> </template> </dom-module>
id
属性はスタイルモジュールを参照するための名前を指定します。スタイルモジュールの名前はカスタムエレメントと同じ名前空間を使用するので、カスタムエレメントとスタイルモジュールでid
が競合しないようにしてください。
定義されたスタイルモジュールを使用するには2つの作業が必要です。まず<link>
タグでスタイルモジュールをインポートし、次に<style>
タグでスタイルをインクルードします。
次の例ではカスタムエレメントでスタイルモジュールを使用しています:
<!-- スタイルモジュールをインポート --> <link rel="import" href="../shared-styles/shared-styles.html"> <dom-module id="my-element"> <template> <!-- スタイルモジュールの名前を指定してスタイルをインクルード --> <style include="shared-styles"></style> <style> :host { display: block; } </style> <span>My Element!</span> </template> <script>Polymer({is: 'my-element'});</script> </dom-module>
上記例では、スタイルモジュールをインクルードする<style>
と、カスタムエレメント自身のスタイルを定義する<style>
のように分けていましたが、分けずに1つの<style>
で記述することもできます:
<style include="shared-styles"> :host { display: block; } </style>
このように1つの<style>
で記述した場合、カスタムエレメント自身のスタイルが定義された後にスタイルモジュールが適用されるので、スタイルモジュールの方が優先(上書き)されることになります。ただしこの動作は将来的に変更されるかもしれません。
また、
<!-- スタイルモジュールをインポート --> <link rel="import" href="../shared-styles/shared-styles.html"> <!-- スタイルモジュールの名前を指定してスタイルをインクルード --> <style is="custom-style" include="shared-styles"></style>
次にスタイルモジュールを使用したサンプルを示します:
shared-styles.html
<dom-module id="shared-styles"> <template> <style> .title { color: white; } </style> </template> </dom-module>
index.html
<!DOCTYPE html> <html> <head> <script src="../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> <link rel="import" href="../bower_components/polymer/polymer.html"> <!-- スタイルモジュールをインポート --> <link rel="import" href="shared-styles/shared-styles.html"> </head> <body> <my-element></my-element> <dom-module id="my-element"> <template> <!-- スタイルモジュールのスタイルをインクルード --> <style include="shared-styles"> .title { background-color: blue; } </style> <div class="title">My Element!</div> </template> <script> document.addEventListener('WebComponentsReady', function () { Polymer({ is: 'my-element' }); }); </script> </dom-module> </body> </html>