# SFC スタイルの機能

# <style scoped>

<style> タグが scoped 属性を持つとき、その CSS は現在のコンポーネントの要素にだけ適用されます。これは Shadow DOM に見られるスタイルのカプセル化に似ています。いくつかの注意点がありますが、ポリフィルは必要ありません。これは PostCSS を使った次のような変換で実現しています:

<style scoped>
.example {
  color: red;
}
</style>

<template>
  <div class="example">hi</div>
</template>
1
2
3
4
5
6
7
8
9

次のようになります:

<style>
.example[data-v-f3f3eg9] {
  color: red;
}
</style>

<template>
  <div class="example" data-v-f3f3eg9>hi</div>
</template>
1
2
3
4
5
6
7
8
9

# 子コンポーネントのルート要素

scoped を使うと、親コンポーネントのスタイルは子コンポーネントに漏れません。しかし、子コンポーネントのルートノードは、親スコープの CSS と子スコープの CSS の両方が影響を受けます。これは親コンポーネントがレイアウトの目的で子コンポーネントのルート要素をスタイルできるようにするという設計のためです。

# ディープセレクタ

scoped スタイルのセレクタを「深く」したい場合、例えば子コンポーネントに影響を与えたい場合は、:deep() 擬似クラスを使用できます:

<style scoped>
.a :deep(.b) {
  /* ... */
}
</style>
1
2
3
4
5

上記は次のようにコンパイルされます:

.a[data-v-f3f3eg9] .b {
  /* ... */
}
1
2
3

TIP

v-html で作られた DOM コンテンツは、スコープ付きスタイルの影響を受けませんが、ディープセレクタを使ってスタイルすることができます。

# スロットセレクタ

デフォルトでスコープ付きスタイルは、親コンポーネントが所有しているコンテンツとみなして、<slot/> でレンダリングされたコンテンツに影響を与えません。スロットのコンテンツを明示的に対象にするには、:slotted 擬似クラスを使用できます:

<style scoped>
:slotted(div) {
  color: red;
}
</style>
1
2
3
4
5

# グローバルセレクタ

グローバルに 1 つのルールだけを適用したい場合、別の <style> を作るのではなく、:global 擬似クラスを使用できます(後述):

<style scoped>
:global(.red) {
  color: red;
}
</style>
1
2
3
4
5

# ローカルとグローバルのスタイル併用

スコープ付きとスコープなしのスタイルの両方を同じコンポーネントに含めることもできます:

<style>
/* グローバルスタイル */
</style>

<style scoped>
/* ローカルスタイル */
</style>
1
2
3
4
5
6
7

# スコープ付きスタイルのヒント

  • スコープ付きスタイルでもクラスが不要になるわけではないです。ブラウザがさまざまな CSS セレクタをレンダリングする方法のため、p { color: red } はスコープ付きの場合(例えば属性セレクタと合わせた場合)に何杯も遅くなります。.example { color: red } のようにクラスや ID を代わりに使えば、このパフォーマンスへの影響をほぼ解消することができます。

  • 再帰的コンポーネントの子孫セレクタには注意が必要です! .a .b セレクタを使った CSS ルールでは、.a に一致する要素に再帰的な子コンポーネントが含まれている場合、その子コンポーネントに含まれるすべての .b がルールに一致してしまいます。

# <style module>

<style module> タグは CSS Modules (opens new window) としてコンパイルされ、結果として得られる CSS クラスを $style というキーの下にオブジェクトとしてコンポーネントに公開します:

<template>
  <p :class="$style.red">
    This should be red
  </p>
</template>

<style module>
.red {
  color: red;
}
</style>
1
2
3
4
5
6
7
8
9
10
11

この結果として得られるクラスは、衝突を避けるためにハッシュ化され、CSS を現在のコンポーネントだけにスコープするのと同じ効果を実現します。

Global exceptions (opens new window)Composition (opens new window) などの詳細については CSS Modules の仕様 (opens new window) を参照してください。

# カスタム注入名

module 属性に値を指定することで、注入されたクラスオブジェクトのプロパティキーをカスタマイズできます:

<template>
  <p :class="classes.red">red</p>
</template>

<style module="classes">
.red {
  color: red;
}
</style>
1
2
3
4
5
6
7
8
9

# Composition API での使用

注入されたクラスは、useCssModule API を介して setup()<script setup> からアクセスできます。カスタム注入名のある <style module> ブロックは useCssModule が一致する module 属性の値を最初の引数として受け取ります:

// デフォルトでは <style module> を返します
useCssModule()

// 名前付きでは <style module="classes"> のクラスを返します
useCssModule('classes')
1
2
3
4
5

# 状態ドリブンな動的 CSS

SFC の <style> タグは v-bind CSS 関数を使って、CSS の値を動的コンポーネントの状態にリンクすることをサポートしています:

<template>
  <div class="text">hello</div>
</template>

<script>
export default {
  data() {
    return {
      color: 'red'
    }
  }
}
</script>

<style>
.text {
  color: v-bind(color);
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

この構文は <script setup> で動作して、JavaScript 式(引用符で囲む必要があります)をサポートしています:

<script setup>
const theme = {
  color: 'red'
}
</script>

<template>
  <p>hello</p>
</template>

<style scoped>
p {
  color: v-bind('theme.color');
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

実際の値はハッシュ化された CSS カスタムプロパティにコンパイルされるため、CSS は静的なままです。このカスタムプロパティは、インラインスタイルを介してコンポーネントのルート要素に適用され、ソースの値が変更されるとリアクティブに更新されます。