【実務・中級編】 ::part 擬似要素によるShadow DOMの外部スタイリング – CSS実践ガイド

こんにちは。フロントエンドの現場で日々CSSの魔改造と格闘している君なら、一度はこんな絶望を味わったことがあるはずだ。

「Web Components使ってカプセル化されたのはいいけど、中のボタンのホバー色ひとつ変えられないじゃん……!」

そう、Shadow DOMという名の「完全なる城壁」の前に、僕たちの強力なCSSセレクタは無力化される。外から `.my-component button` なんて叩いても、カプセル化の壁に阻まれてビクともしない。CSS Variablesで変数を流し込むという逃げ道もあるが、コンポーネント側の設計者が変数を定義してくれていなければ、それも手詰まりだ。

だが、安心してほしい。今日のテーマである `::part` 擬似要素 と `part` 属性を使いこなせば、コンポーネントのカプセル化を完全に破壊することなく、「ここだけは外からスタイリングさせてやるよ」という公式の抜け穴(API)を優雅に彫り込むことができる。

今日は、実務で明日から使えるこの強力な武器について、ブラウザの裏側の動きも含めて徹底的に解説しよう。

—

なぜShadow DOMは僕たちのCSSを拒絶するのか?

まず敵を知ることから始めよう。Shadow DOMの本質は「カプセル化」だ。
ReactのCSS ModulesやVueのScoped CSSは、ビルド時にクラス名にハッシュを付与して衝突を防いでいる「疑似的なカプセル化」に過ぎない。しかし、Web ComponentsのShadow DOMは、ブラウザのレンダリングエンジンレベルでDOMツリーを隔離する。

外部のCSSが内部に漏れ出さないし、内部のCSSも外に漏れ出さない。これは巨大なアプリケーションを開発する上では最高のエクスプロキシだが、デザインシステムを構築する側からすると「融通の利かない頑固ジジイ」に見える瞬間がある。

「いやいや、子孫セレクタで中の要素を直撃させてよ!」と思うかもしれないが、それを許すとコンポーネントの内部構造(カプセル化された実装詳細)に外部のスタイルが依存してしまい、コンポーネントのバージョンアップ時にアプリ全体が崩壊するという、昔ながらの悪夢が再来する。

そこでW3Cの仕様策定者たちが生み出したのが、「カプセル化の壁に、あらかじめ設計者が許可した『小窓』を開ける」というアプローチ、それが `part` 属性と `::part` 擬似要素だ。

—

`part` と `::part` の基本メカニズム

仕組みは驚くほどシンプルだ。

1. コンポーネントの内部(Shadow Tree内)で、外部からスタイルを当てさせたい要素に `part=”任意の名前”` を付与する。
2. 外部のCSS(ホスト側の文書)から、カスタム要素名に対して `::part(任意の名前)` を使ってスタイルを記述する。

ブラウザはこれをどう処理しているか?
ブラウザのレンダリングエンジンは、`::part` を見つけると、「Shadow DOMの境界をまたぐ特例」として、指定されたパーツにのみ外部からのスタイルルールを適用する。コンポーネントの他の部分は相変わらず鉄壁のガードに守られたままだ。

言葉だけではピンとこないだろう。実務でそのまま使える、カード型コンポーネントのサンプルコードを見てみよう。

—

実践!コピペで動くコード例

以下のコードをそのままブラウザ(ChromeやSafariなど、現代のモダンブラウザならどこでも動く)のHTMLファイルに貼り付けてみてほしい。





::part 擬似要素の実践デモ




このコードをブラウザで開くと、`user-card` というカプセル化されたコンポーネントの中にある `

` や `