Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/sfc-scoped-css-slot-content.md
1---2title: Scoped CSS Cannot Style Slot Content Directly3impact: HIGH4impactDescription: Slot content receives the parent component's scope, not the child's, causing styles to fail unexpectedly5type: gotcha6tags: [vue3, sfc, scoped-css, slots, deep-selector]7---89# Scoped CSS Cannot Style Slot Content Directly1011**Impact: HIGH** - When a parent passes content through a slot, that content receives the parent component's scoped style attributes, not the child component's. This means the child component cannot style slot content with regular scoped CSS.1213## Task Checklist1415- [ ] Use `:deep()` selector in the wrapper component to style slot content16- [ ] Alternatively, use `:slotted()` pseudo-selector to target slotted elements17- [ ] For complex slot styling, consider using CSS modules or unscoped styles18- [ ] Document expected slot content structure when styling assumptions exist1920**Problematic Code:**21```vue22<!-- Card.vue (child component) -->23<template>24<div class="card">25<div class="card-body">26<slot />27</div>28</div>29</template>3031<style scoped>32.card-body {33padding: 1rem;34}3536/* BAD: Won't apply to slot content! */37.card-body h2 {38color: #333;39margin-bottom: 0.5rem;40}4142.card-body p {43color: #666;44}45</style>46```4748```vue49<!-- Parent.vue -->50<template>51<Card>52<!-- This h2 and p won't be styled by Card's scoped CSS -->53<h2>Card Title</h2>54<p>Card description text.</p>55</Card>56</template>57```5859**Correct Code:**60```vue61<!-- Card.vue - Using :slotted() -->62<template>63<div class="card">64<div class="card-body">65<slot />66</div>67</div>68</template>6970<style scoped>71.card-body {72padding: 1rem;73}7475/* GOOD: :slotted() targets slot content */76:slotted(h2) {77color: #333;78margin-bottom: 0.5rem;79}8081:slotted(p) {82color: #666;83}84</style>85```8687## Using :deep() Alternative8889```vue90<!-- Card.vue - Using :deep() -->91<style scoped>92.card-body {93padding: 1rem;94}9596/* :deep() also works for slot content */97.card-body :deep(h2) {98color: #333;99}100101.card-body :deep(p) {102color: #666;103}104</style>105```106107## Why This Happens108109Slot content is compiled in the parent component's scope:110111```vue112<!-- Parent template compiles to: -->113<Card>114<h2 data-v-parent123>Card Title</h2>115<p data-v-parent123>Card description</p>116</Card>117118<!-- Card template compiles to: -->119<div class="card" data-v-card456>120<div class="card-body" data-v-card456>121<slot /> <!-- Content inserted WITHOUT data-v-card456 -->122</div>123</div>124```125126The `<h2>` has `data-v-parent123`, but Card's scoped CSS expects `data-v-card456`.127128## :slotted() vs :deep() for Slots129130Both work, but have subtle differences:131132```vue133<style scoped>134/* :slotted() - Specifically for slot content */135/* Only targets direct slotted elements */136:slotted(h2) {137color: blue;138}139140/* :deep() - More general deep selector */141/* Can target nested elements within slot content */142.card-body :deep(h2) {143color: blue;144}145146/* For nested elements in slot content, must use :deep() */147:slotted(.wrapper h2) { } /* Won't work for nested h2 */148.card-body :deep(.wrapper h2) { } /* Works for nested */149</style>150```151152## Combining with Named Slots153154```vue155<template>156<div class="card">157<header class="card-header">158<slot name="header" />159</header>160<div class="card-body">161<slot />162</div>163<footer class="card-footer">164<slot name="footer" />165</footer>166</div>167</template>168169<style scoped>170/* Style specific slot content */171.card-header :slotted(h1),172.card-header :slotted(h2) {173margin: 0;174font-size: 1.25rem;175}176177.card-body :slotted(p) {178margin-bottom: 1rem;179}180181.card-footer :slotted(button) {182margin-right: 0.5rem;183}184</style>185```186187## Performance Tip: Use Classes188189Element selectors with `:slotted()` can be slower:190191```vue192<style scoped>193/* SLOWER: Element selector */194:slotted(p) {195color: gray;196}197198/* FASTER: Class selector */199:slotted(.card-text) {200color: gray;201}202</style>203```204205## When to Use Unscoped Styles206207For complex slot styling, unscoped styles may be cleaner:208209```vue210<template>211<article class="article-card">212<slot />213</article>214</template>215216<style>217/* Unscoped with unique prefix for complex content styling */218.article-card h1,219.article-card h2,220.article-card h3 {221font-family: Georgia, serif;222line-height: 1.2;223}224225.article-card p {226line-height: 1.6;227}228229.article-card img {230max-width: 100%;231}232233.article-card blockquote {234border-left: 3px solid #ccc;235padding-left: 1rem;236}237</style>238```239240## Reference241- [Vue.js Scoped CSS - Slotted Selectors](https://vuejs.org/api/sfc-css-features.html#slotted-selectors)242- [Vue.js Deep Selectors](https://vuejs.org/api/sfc-css-features.html#deep-selectors)243