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-dynamic-content.md
1---2title: Scoped CSS Does Not Apply to Dynamically Added Content3impact: HIGH4impactDescription: Programmatically inserted DOM elements won't receive scoped style data attributes, causing styles to fail silently5type: gotcha6tags: [vue3, sfc, scoped-css, dynamic-content, v-html]7---89# Scoped CSS Does Not Apply to Dynamically Added Content1011**Impact: HIGH** - Vue's scoped CSS works by adding data attributes to elements at compile time. Content added dynamically at runtime (via `v-html`, JavaScript DOM manipulation, or third-party libraries) won't have these attributes, so scoped styles won't apply.1213## Task Checklist1415- [ ] For `v-html` content, use `:deep()` selectors or unscoped styles16- [ ] Avoid programmatic DOM manipulation; prefer Vue's reactive template system17- [ ] When DOM manipulation is unavoidable, use global styles with unique class prefixes18- [ ] Consider CSS modules for content that mixes static and dynamic elements1920**Problematic Code:**21```vue22<script setup>23import { ref } from 'vue'2425const htmlContent = ref('<p class="dynamic">This is dynamic content</p>')26</script>2728<template>29<div class="container">30<div v-html="htmlContent"></div>31</div>32</template>3334<style scoped>35/* BAD: Won't apply to the dynamic <p> element! */36.dynamic {37color: red;38font-weight: bold;39}40</style>41```4243**Correct Code:**44```vue45<script setup>46import { ref } from 'vue'4748const htmlContent = ref('<p class="dynamic">This is dynamic content</p>')49</script>5051<template>52<div class="container">53<div v-html="htmlContent"></div>54</div>55</template>5657<style scoped>58/* GOOD: Use :deep() for v-html content */59.container :deep(.dynamic) {60color: red;61font-weight: bold;62}63</style>64```6566## Why This Happens6768Vue scoped CSS adds a unique data attribute (e.g., `data-v-7ba5bd90`) to:691. All elements in the component's template (at compile time)702. All CSS selectors7172```html73<!-- What Vue generates at compile time -->74<div class="container" data-v-7ba5bd90>75<div data-v-7ba5bd90>76<!-- v-html content is inserted at runtime WITHOUT the attribute -->77<p class="dynamic">This is dynamic content</p>78</div>79</div>80```8182```css83/* Generated scoped CSS */84.dynamic[data-v-7ba5bd90] { color: red; }85/* ^ Won't match because the dynamic <p> doesn't have data-v-7ba5bd90 */86```8788## Alternative: Global Styles with Unique Prefix8990```vue91<script setup>92import { ref } from 'vue'93const htmlContent = ref('<p class="my-component-dynamic">Dynamic text</p>')94</script>9596<template>97<div class="my-component">98<div v-html="htmlContent"></div>99</div>100</template>101102<!-- Use unscoped styles with unique prefixes -->103<style>104.my-component .my-component-dynamic {105color: red;106}107</style>108```109110## Programmatic DOM Manipulation111112When using third-party libraries that manipulate the DOM:113114```vue115<script setup>116import { ref, onMounted } from 'vue'117118const editorRef = ref(null)119120onMounted(() => {121// Third-party editor that injects its own DOM elements122initRichEditor(editorRef.value)123})124</script>125126<template>127<div class="editor-wrapper">128<div ref="editorRef"></div>129</div>130</template>131132<style scoped>133/* BAD: Won't reach injected editor elements */134.editor-toolbar { ... }135.editor-content { ... }136</style>137138<style>139/* GOOD: Global styles scoped by parent class */140.editor-wrapper .editor-toolbar {141background: #f5f5f5;142}143.editor-wrapper .editor-content {144padding: 1rem;145}146</style>147```148149## Best Practice: Prefer Reactive Templates150151Instead of dynamic HTML, use Vue's reactive system when possible:152153```vue154<script setup>155import { ref } from 'vue'156157// BAD: Dynamic HTML that needs special style handling158const badHtml = ref('<span class="highlight">text</span>')159160// GOOD: Reactive data that templates handle161const items = ref([162{ text: 'Item 1', isHighlighted: true },163{ text: 'Item 2', isHighlighted: false }164])165</script>166167<template>168<!-- BAD -->169<div v-html="badHtml"></div>170171<!-- GOOD: Scoped styles work normally -->172<ul>173<li174v-for="item in items"175:key="item.text"176:class="{ highlight: item.isHighlighted }"177>178{{ item.text }}179</li>180</ul>181</template>182183<style scoped>184/* Works perfectly with reactive template */185.highlight {186background: yellow;187}188</style>189```190191## Reference192- [Vue.js Scoped CSS](https://vuejs.org/api/sfc-css-features.html#scoped-css)193- [GitHub Issue: Scoped CSS not applied for programmatically added elements](https://github.com/vuejs/vue/issues/7649)194