CSS Shadow Parts は、開発者がシャドウツリー内の要素に CSS プロパティをスタイル設定することを可能にします。これは、Ionic Framework Shadow DOMコンポーネントをカスタマイズする際に非常に便利です。
Ionic Frameworkは、Web Componentsの分散型セットです。Web Componentsは、スタイルとマークアップをカプセル化するためにShadow DOMの仕様に従っています。
Shadow DOM は、スタイルがコンポーネントから漏れて、意図せずに他の要素に適用されるのを防ぐのに便利です。例えば、ion-button
コンポーネントに .button
クラスを割り当てています。Shadow DOM によるカプセル化がなければ、ユーザーが自分の要素に .button
クラスを設定した場合、Ionic Framework のボタンスタイルを継承してしまうでしょう。ion-button` は Shadow コンポーネントであるため、これは問題ではありません。
しかし、このカプセル化のために、スタイルはShadowコンポーネントの内部要素に侵入することができません。つまり、Shadow コンポーネントがそのシャドウツリーの内部にある要素をレンダリングする場合、その内部要素を CSS で直接ターゲットにすることはできません。例として ion-select
コンポーネントを使用すると、次のようなマークアップがレンダリングされます。
<ion-select>
#shadow-root
<div class="select-text select-placeholder"></div>
<div class="select-icon"></div>
</ion-select>
プレースホルダーのテキストとアイコン要素は #shadow-root
の内部にあるため、以下のCSSはプレースホルダーのスタイル付けに 無効 です。
ion-select .select-placeholder {
color: blue;
}
So how do we solve this? CSS Shadow Parts!
Shadow Partsは、開発者がシャドウツリーの外側から、シャドウツリー内のスタイルを設定することを可能にします。これを行うには、part を公開し 、::part を使用してスタイルを設定する必要があります。
シャドウ DOM コンポーネントを作成する際、シャドウツリー内の要素に part
属性を割り当てることで、パートを追加することができます。これはIonic Frameworkでコンポーネントに追加され、エンドユーザーからのアクションは必要ありません。
引き続き、例として ion-select
コンポーネントを使用し、マークアップは以下のように更新されます。
<ion-select>
#shadow-root
<div part="placeholder" class="select-text select-placeholder"></div>
<div part="icon" class="select-icon"></div>
</ion-select>
上記では、placeholder
とicon
という2つのPartsを表示しています。すべてのPartsについては、select documentation を参照してください。
これらのPartsが公開されたことで、要素は ::part を使って直接スタイルを設定することができるようになりました。
`::part()` 擬似要素により、開発者はPart属性で公開されているシャドウツリー内の要素を選択することができます。
ion-select
は、値が選択されていないときにテキストをスタイル付けするための placeholder
Partを公開していることが分かっているので、次のようにカスタマイズすることができます。
ion-select::part(placeholder) {
color: blue;
opacity: 1;
}
part
を使ったスタイリングでは、その要素で受け付けられる任意のCSSプロパティを変更することができます。
partをターゲットにできることに加え、擬似要素を明示的に露出させずにスタイル付けすることができます。
ion-select::part(placeholder)::first-letter {
font-size: 22px;
font-weight: 500;
}
Partsは、ほとんどの擬似クラスでも動作します。
ion-item::part(native):hover {
color: green;
}
Ionic Frameworkコンポーネントのすべての公開Partsは、そのAPIページの「CSS Shadow Parts」の見出しで確認できます。すべてのコンポーネントとそのAPIページを表示するには、Component documentation を参照してください。
コンポーネントがPartsを持つためには、以下の条件を満たしている必要があります。
- Shadow DOM コンポーネントであること。 Scoped または Light DOM コンポーネントの場合、子要素を直接対象とすることができる。コンポーネントが Scoped または Shadow の場合、コンポーネントのドキュメントページ にその名前で表示されます。
- これは子要素を含んでいます。例えば、
ion-card-header
はShadowコンポーネントですが、すべてのスタイルはホストエレメントに適用されます。子要素を持たないので、Partsは必要ありません。 - 子要素は構造的なものではありません。
ion-title
を含む特定のコンポーネントでは、子要素は内部要素を配置するために使用される構造的な要素です。構造的な要素をカスタマイズすることは、予期しない結果をもたらす可能性があるため、お勧めしません。
CSS Shadow Partsは最近のすべてのメジャーブラウザでサポートされています。ただし、一部の古いバージョンではshadow partsがサポートされていません。アプリにpartsを実装する前に、ブラウザのサポートが要件を満たしていることを確認してください。旧バージョンのブラウザのサポートが必要な場合は、引き続き CSS Variables を使用してスタイリングすることをお勧めします。
Vendor prefixed 擬似要素は、現時点ではサポートされていません。例えば、`::-webkit-scrollbar`擬似要素のようなものです。
my-component::part(scroll)::-webkit-scrollbar {
background: green;
}
詳しくは GitHubのIssue をご覧ください。
ほとんどの擬似クラスはPartsでサポートされていますが、構造的な擬似クラスはサポートされていません。動作しない構造的擬似クラスの例を以下に示します。
my-component::part(container):first-child {
background: green;
}
my-component::part(container):last-child {
background: green;
}
擬似要素 ::part()
は追加の ::part()
にマッチすることができません。
例えば、my-component::part(button)::part(label)
は何もマッチしません。これは、そうすることで意図した以上の構造的な情報を露出してしまうからです。
もし <my-component>
の内部ボタンが part="label => button-label"
のようなものを使って、ボタンの内部Partsをパネル自身のpart要素マップに転送していた場合、 my-component::part(button-label)
といったセレクタはボタンのラベルだけを選び、他のラベルを無視することになるでしょう。