やあ、お疲れ。Webコンポーネントの実装を進めているところかい?
Shadow DOMの壁を前にして、「あれ、外から入れたはずのコンテンツにスタイルが当たらないぞ……?」と深夜のオフィスで頭を抱えている姿が目に浮かぶよ。
安心してくれ、君がハマっているその壁こそ、多くのフロントエンドエンジニアが通る「`::slotted()`の特殊な仕様と特異性(Specificity)」の罠だ。
今日は、この`::slotted`擬似要素について、仕様の裏側から現場で絶対に役立つ実践的なテクニックまで、シニアの私から徹底的に叩き込んであげよう。これを読めば、もうCSSの適用範囲で迷うことはなくなるはずだ。
—
1. そもそも `::slotted()` とは何か? なぜ通常のセレクタが効かないのか
まず大前提として、Shadow DOMの最大の武器は「カプセル化(スタイルのカプセル化)」だよね。ホスト側のCSSがコンポーネント内部を汚染しないし、逆に内部のCSSが外側に漏れ出すこともない。
しかし、ここで一つジレンマが生じる。
「コンポーネントの構造(Shadow DOM内)はカプセル化したいけれど、コンポーネントの利用者(Light DOM側)が挿入したコンテンツ(スロットコンテンツ)に対しては、デフォルトのデザインを適用したい」というケースだ。
ここで登場するのが `
ブラウザの裏側の動き:どちらのDOMツリーに属しているのか?
ここが一番のキモなんだけど、`::slotted(
しかし、それをスタイリングしているのは「シャドウDOM(コンポーネントの内部)」のスタイルシートなんだよ。
つまり、ブラウザは「外からやってきた要素が、シャドウ内のスロットにパズルのピースのようにハマった瞬間」を捉えてスタイルを適用している。
この「所有権の境界線」をまたぐ特殊な性質ゆえに、通常のCSSセレクタとはルールが大きく異なるんだ。
—
2. 現場で絶対に知っておくべき仕様の罠と「特異性(Specificity)」
さて、ここからが本題の泥臭い話だ。`::slotted()` を使う上で、多くのエンジニアが躓くポイントが2つある。
罠その1:子孫セレクタが書けない(直撃するのは「そのもの」だけ)
例えば、以下のようなCSSを書いたことはないかい?
/ NGな書き方:これだと動かない! /
::slotted(.my-text) span {
color: red;
}
「スロットされた要素の中にある `` を赤くしたい」という意図なのだろうけれど、これは動作しない。
`::slotted()` がターゲットにできるのは、スロットの穴に直接差し込まれたトップレベルの要素そのものだけなんだ。その中の子孫要素まで一網打尽にスタイリングすることは、カプセル化的にも仕様上も許可されていない。
罠その2:カプセル化の壁(詳細度の逆転)
もう一つの罠は、詳細度(Specificity)の力学だ。
実は、`::slotted()` 自体の詳細度は、セレクタ内の最も強い引数の詳細度と同じになる。だが、コンポーネントの「外側(Light DOM)」から書かれたインラインスタイルや強力なセレクタには、原則として勝てないようになっている。
要するに、「コンポーネントの利用者(外部)が指定したスタイルが、内部の `::slotted()` のスタイルよりも優先される」という原則があるんだ。これはコンポーネントの再利用性を保つ上では正しい設計なんだけど、設計時に頭に入れておかないと「なんで俺の書いたCSSが効かないんだ!」と発狂することになる。
—
3. 実践!コピペで動くWeb Components & `::slotted` サンプル
百聞は一見に如かず。実際に動くコードを見ていこう。
今回は、カード型のカスタム要素を例にして、`::slotted()` の正しい使い方を実証してみるよ。
以下のコードをそのままHTMLファイルに貼り付けてブラウザで開いてみてほしい。
- 1. そもそも `::slotted()` とは何か? なぜ通常のセレクタが効かないのか
- ブラウザの裏側の動き:どちらのDOMツリーに属しているのか?
- 2. 現場で絶対に知っておくべき仕様の罠と「特異性(Specificity)」
- 罠その1:子孫セレクタが書けない(直撃するのは「そのもの」だけ)
- 罠その2:カプセル化の壁(詳細度の逆転)
- 3. 実践!コピペで動くWeb Components & `::slotted` サンプル
- 山田 太郎
- `)を正確にターゲットにしている。 2. `::slotted(.highlight)`: クラス名ベースでスロットコンテンツの見た目を装飾している。これにより、利用者が任意のタグ(` `でも` `でも)にこのクラスを付与するだけで、コンポーネント側で統一されたデザインを強制できる。 3. カプセル化の維持: シャドウDOM内部のCSSでありながら、外側から挿入された要素の見た目を安全にコントロールできていることが確認できるはずだ。 — 4. シニアから現場のエンジニアへ:知っておくべき設計のベストプラクティス
山田 太郎
フロントエンド・アーキテクト / CSS愛好家。日夜レイアウトバグと戦う日々。
このサンプルコードの注目ポイント
1. `::slotted([slot=”card-title”])`: 属性セレクタを使って、特定の名前付きスロットに挿入された要素(`
`)を正確にターゲットにしている。 2. `::slotted(.highlight)`: クラス名ベースでスロットコンテンツの見た目を装飾している。これにより、利用者が任意のタグ(` `でも` `でも)にこのクラスを付与するだけで、コンポーネント側で統一されたデザインを強制できる。 3. カプセル化の維持: シャドウDOM内部のCSSでありながら、外側から挿入された要素の見た目を安全にコントロールできていることが確認できるはずだ。 — 4. シニアから現場のエンジニアへ:知っておくべき設計のベストプラクティス
`::slotted()` は強力なツールだが、何でもかんでもこれに頼るのは設計のアンチパターンになり得る。現場で大規模なデザインシステムを構築する際、私たちが意識している指針をいくつかシェアしよう。
① スロットコンテンツには「見た目(デザイン)」ではなく「構造と意味」を求めよ
Web Componentsの設計において、スロットは「コンテンツの流し込み口」だ。フォントサイズや色を `::slotted()` でガチガチに固めすぎると、コンポーネントの柔軟性が失われる。
基本的には、レイアウト(マージンや配置など)や最低限のタイポグラフィの初期値に留め、色や細かい装飾は利用側に委ねる(あるいはCSSカスタムプロパティ(CSS変数)を経由する)方が、モダンな設計としては美しくなることが多い。
② 子孫要素のスタイリングが必要な場合の代替案
前述した通り、`::slotted()` では内部の要素(例:`::slotted(div) span`)を指定できない。もしスロット内の深くまでスタイルを統制したい場合は、以下のいずれかの手法を検討してほしい。
- CSSカスタムプロパティ(CSS変数)の活用:
コンポーネント側で `–user-card-text-color` などの変数を定義し、それをスロットの中身の要素側で使ってもらう。これが最も現代的でクリーンなアプローチだ。
- パーツ指向(`::part` 擬似要素)への切り替え:
スロットではなく、シャドウDOM内部の要素に `part=”title”` のように名前をつけ、外部から `user-card::part(title)` のようにスタイリングさせるアプローチ(これは `::slotted` とは逆向きの強力な技術だ)。
—
おわりに
`::slotted()` は、Shadow DOMという堅牢な要塞の中で、外の世界(Light DOM)と優雅に手を取り合うための数少ない架け橋だ。
最初は「制限が多くて使いづらいな」と感じるかもしれない。だが、その制限こそが、コンポーネントの独立性と保守性を担保するためのブラウザベンダーからのメッセージなんだ。
仕様の裏側を正しく理解し、適切な場面で的確に使いこなせれば、君の書くフロントエンドのコードベースは一段とプロフェッショナルなものになるはずだ。
さあ、エディタに戻って、実際のプロジェクトで試してみよう。質問があればいつでも声をかけてくれ!

コメント