- scoped をつけているせいで、子コンポーネントのスタイルが親から反映されない
- slot で表示しているコンテンツにスタイルが当たらない
- :deep() を書いたのにスタイルが効かない…
Vue / Nuxt 開発では、スタイルのスコープ汚染を防ぐために style scoped を使うのが一般的です。ところが、この便利な機能が原因で「子コンポーネントや slot に親のスタイルがまったく効かない!」という壁にぶつかることがあります。
この記事では、その問題を解決する :deep() セレクタ・:slotted() セレクタの使い方を、コード例を交えてわかりやすく解説します。さらに、バージョンによって書き方が異なるつまづきポイントもカバーしているので、ハマりがちな落とし穴も回避できます。
なぜ子コンポーネントにスタイルが反映されないのか
Vue / Nuxt では、<style scoped> を付けると、そのコンポーネントのテンプレートにだけ一意の属性(data-v-xxxxxxxx)が付加されます。
例えば、以下のように書いた場合
<style scoped>
.title { color: red; }
</style>ビルド後はこのように変換されます:
.title[data-v-xxxxxxxx] { color: red; }このため、子コンポーネントの要素には data-v-xxxxxxxx 属性が付かず、親のスコープドスタイルがマッチしません。これが「子コンポーネントにスタイルが効かない」原因です。
解決方法
① :deep() セレクタを使う
子コンポーネント内の要素を親のスコープドスタイルでスタイリングできます。
<template>
<div class="wrapper">
<ChildComponent />
</div>
</template>
<style scoped>
.wrapper :deep(.child-title) {
color: blue;
font-size: 1.2rem;
}
</style>上記のように書くと、ChildComponent 内の .child-title クラスに対してスタイルを適用できます。
② :slotted() セレクタを使う
slot に渡したコンテンツ(slot コンテンツ)にスタイルを当てたい場合は、:slotted() を使います。
デフォルトでは、<slot /> で表示されるコンテンツは「親コンポーネントのもの」とみなされるため、子コンポーネント側の scoped スタイルはそのまま当たりません。:slotted() はこの問題への直接的な解決策です。
<template>
<div class="card">
<slot />
</div>
</template>
<style scoped>
:slotted(p) {
color: green;
line-height: 1.8;
}
</style>親コンポーネントから <p> タグを slot に渡すと、子コンポーネントの :slotted(p) スタイルが反映されます。
【つまづきポイント】バージョンによって書き方が違う
ここが一番ハマりやすいポイントです。Vue / Nuxt のバージョンによって、Deep セレクタの書き方が異なります。
Vue 3 / Nuxt 3 の場合(推奨)
<style scoped>
.parent :deep(.child) {
color: red;
}
</style>Vue 3 では :deep() が公式の書き方です。出来るだけ、これを使いましょう。
Vue 2 / Nuxt 2 の場合
Vue 2 系では :deep() がサポートされていないため、以下の書き方を使います:
/* ① >>> を使う方法 */
<style scoped>
.parent >>> .child {
color: red;
}
</style>
/* ② /deep/ を使う方法(Sass などを使う場合はこちら) */
<style scoped>
.parent /deep/ .child {
color: red;
}
</style>
/* ③ ::v-deep を使う方法(Vue 2.6 以降〜Vue 3 移行期) */
<style scoped>
.parent ::v-deep .child {
color: red;
}
</style>なぜ書き方がバラバラなのか
この混乱の背景には、>>> が Sass(SCSS)でパースできないという問題があります。SCSS を使っている場合、>>> はコンパイルエラーになることがあります。そのため、Vue Loader の頃から /deep/ や ::v-deep がエイリアスとして提供されてきました。
Vue 3 でこれらがすべて :deep() に統一されましたが、古いプロジェクトや中間バージョンを使っている場合、どの書き方を使うか迷うことがあります。
「:deep を書いたのに効かない!」という場合は、まず自分の Vue / Nuxt のバージョンを確認して、該当の書き方を使うようにしましょう
まとめ
最後まで読んでいただき、ありがとうございました!
今回は「Vue / Nuxt で scoped スタイルを使っているときに、子コンポーネントや slot にも親のスタイルを反映させる方法」についてご紹介しました。
おさらいです:
scoped CSSは一意の属性セレクタで管理されるため、子コンポーネントには親のスタイルが届かない:deep()を使えば、子コンポーネント内の要素にスタイルを適用できる(Vue 3 / Nuxt 3 推奨):slotted()を使えば、slot 経由で渡したコンテンツにスタイルを適用できる- バージョンによって書き方が異なるので、バージョンを確認してから書き方を選ぶ
「スタイルが効かない…」とハマったときは、まずバージョンと書き方が合っているかを確認してみてください。それだけで解決することがほとんどです。ぜひ参考にしてみてください!
