【Vue/Nuxt】子コンポーネントに親のスタイルを反映させる方法

この記事で解決できるお悩み
  • 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 経由で渡したコンテンツにスタイルを適用できる
  • バージョンによって書き方が異なるので、バージョンを確認してから書き方を選ぶ

「スタイルが効かない…」とハマったときは、まずバージョンと書き方が合っているかを確認してみてください。それだけで解決することがほとんどです。ぜひ参考にしてみてください!

この記事が気に入ったら
フォローしてね!

シェアしていただけると大変励みになります!
  • URLをコピーしました!
  • URLをコピーしました!
目次