Chapter 26: Scoped Styling
Scoped Styling (or <style scoped>)
This is the reason why so many developers fall in love with Vue’s .vue file format — because it gives you true style encapsulation without any extra tools or naming conventions.
What is Scoped Styling? (Simple Human Explanation)
When you write this in a .vue file:
|
0 1 2 3 4 5 6 7 8 9 |
<style scoped> h1 { color: navy; } .card { background: #f8fafc; } </style> |
Vue does something magical during build time:
- It automatically adds a unique data attribute to every HTML element inside this component’s template Example: data-v-123abc45
- It rewrites every CSS selector inside <style scoped> so that it only matches elements that have that exact data attribute
Result → your styles cannot leak out to other components, and styles from other components cannot accidentally affect this one.
It’s like each component gets its own private CSS namespace — without you having to do BEM, CSS Modules, styled-components, Tailwind prefixes, or anything manual.
Real, Live Example – Two Components, No Style Conflicts
1. UserCard.vue (fancy blue card)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<!-- src/components/UserCard.vue --> <template> <div class="card"> <h2 class="title">{{ name }}</h2> <p class="email">{{ email }}</p> </div> </template> <script setup lang="ts"> defineProps<{ name: string email: string }>() </script> <style scoped> .card { padding: 1.5rem; background: #eff6ff; border: 2px solid #3b82f6; border-radius: 12px; box-shadow: 0 4px 15px rgba(59,130,246,0.15); max-width: 400px; margin: 1rem auto; } .title { color: #1e40af; margin: 0 0 0.5rem; } .email { color: #1e3a8a; margin: 0; } </style> |
2. ProductCard.vue (completely different orange card — same class names!)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<!-- src/components/ProductCard.vue --> <template> <div class="card"> <h3 class="title">{{ productName }}</h3> <p class="price">₹{{ price.toLocaleString() }}</p> </div> </template> <script setup lang="ts"> defineProps<{ productName: string price: number }>() </script> <style scoped> .card { padding: 1.8rem; background: #fffbeb; border: 2px solid #d97706; border-radius: 16px; box-shadow: 0 6px 20px rgba(217,119,6,0.12); max-width: 420px; } .title { color: #92400e; margin: 0 0 0.8rem; font-size: 1.4rem; } .price { color: #b45309; font-size: 1.5rem; font-weight: bold; margin: 0; } </style> |
Parent usage – HomeView.vue
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<template> <div class="container"> <h1>Different Cards – No Style Leakage</h1> <UserCard name="Rahul Sharma" email="rahul@webliance.in" /> <ProductCard productName="Vue.js Premium Course" price={4999} /> </div> </template> <script setup lang="ts"> import UserCard from '@/components/UserCard.vue' import ProductCard from '@/components/ProductCard.vue' </script> <style scoped> .container { max-width: 900px; margin: 2rem auto; padding: 0 1rem; } </style> |
What you see in browser DevTools (Elements tab):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!-- UserCard root --> <div class="card" data-v-7b8c2d3e> <h2 class="title" data-v-7b8c2d3e>Rahul Sharma</h2> ... </div> <!-- ProductCard root --> <div class="card" data-v-a9f1e4c2> <h3 class="title" data-v-a9f1e4c2>Vue.js Premium Course</h3> ... </div> |
→ Both have class=”card” and .title — but no conflict → Vue added unique data-v-xxx attributes → selectors become .card[data-v-7b8c2d3e] and .card[data-v-a9f1e4c2]
Important Rules & Behaviors (2026 Details)
| Feature / Question | Answer / Behavior |
|---|---|
| What happens to global styles? | Global <style> (no scoped) still affect everything — use sparingly |
| Can I use :deep() / ::v-deep / /deep/ ? | Yes — to style child components from parent (but prefer slots or redesign) |
| What about <style module> ? | CSS Modules — different system (class names become variables) — rarely used now |
| Does scoped affect <slot> content? | No — slotted content keeps parent’s scope (very important!) |
| Can I disable scoping? | Yes — just remove scoped attribute → becomes global |
| Performance impact? | Almost zero — Vue compiles it at build time, no runtime cost |
| Tailwind / UnoCSS / CSS-in-JS users? | Scoped still works perfectly — most people keep scoped even with utility CSS |
Very Common & Useful Pattern: Scoped + Deep Selector
When you want to style something inside a child component from the parent:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!-- Parent component --> <style scoped> .my-list :deep(.child-item) { border-bottom: 1px dashed #cbd5e1; } /* Vue 3.2+ recommended syntax */ .my-list ::v-deep(.child-item) { ... } /* Old syntax (still works) */ .my-list /deep/ .child-item { ... } </style> |
But honest 2026 advice: → Try to avoid :deep as much as possible — it breaks encapsulation → Prefer slots, design tokens, or CSS variables passed via props
Quick Summary Table – Scoped Styling in 2026
| Question | Answer |
|---|---|
| Purpose | Prevent style leakage between components |
| Syntax | <style scoped> (add scoped attribute) |
| How Vue does it | Adds unique data-v-xxxx to elements + rewrites selectors |
| Global styles | <style> (no scoped) — affects whole app |
| Slotted content | Uses parent’s scope — not child’s |
| Styling child components from parent | :deep(selector) / ::v-deep (use sparingly) |
| Recommended for UI libraries? | Yes — almost always keep scoped |
| Recommended with Tailwind? | Yes — scoped + @apply or utility classes works great |
Your Mini Practice Task
- Create two components: BlueBox.vue and RedBox.vue
- Give both a class .box with completely different styles
- Use both in a parent → confirm no style conflict in DevTools
- Add a global <style> rule for .box → see that it does affect both (shows why scoped is valuable)
Any part still fuzzy? Want me to show:
- Scoped + Tailwind real example?
- How :deep / ::v-deep looks in DevTools?
- Scoped vs CSS Modules vs styled-components?
- Common gotcha with slots + scoped?
Just tell me — we’ll debug it together step by step 🚀
Happy scoping from Hyderabad! 💙
