Files
agent-skills/skills/vue-best-practices/reference/sfc-scoped-css-performance.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

209 lines
4.4 KiB
Markdown

---
title: Use Class Selectors in Scoped CSS for Performance
impact: MEDIUM
impactDescription: Element selectors in scoped CSS are slower because browsers must check both the element type and data attribute
type: efficiency
tags: [vue3, sfc, scoped-css, performance, css-selectors]
---
# Use Class Selectors in Scoped CSS for Performance
**Impact: MEDIUM** - When Vue compiles scoped CSS, it adds attribute selectors to every rule. Element selectors combined with attribute selectors (e.g., `p[data-v-xxx]`) are significantly slower for browsers to match than class selectors with attributes (e.g., `.text[data-v-xxx]`).
## Task Checklist
- [ ] Prefer class selectors over element selectors in scoped styles
- [ ] Avoid deeply nested element selectors in scoped CSS
- [ ] Use BEM or similar naming conventions to ensure unique class names
- [ ] For heavy styling, consider CSS modules or utility-first approaches
**Problematic Code:**
```vue
<template>
<article>
<header>
<h1>{{ title }}</h1>
<p>{{ subtitle }}</p>
</header>
<section>
<p>{{ content }}</p>
<ul>
<li v-for="item in items" :key="item">{{ item }}</li>
</ul>
</section>
</article>
</template>
<style scoped>
/* BAD: Element selectors are slower when scoped */
article {
max-width: 800px;
}
header {
margin-bottom: 2rem;
}
h1 {
font-size: 2rem;
}
p {
line-height: 1.6;
}
ul {
padding-left: 1.5rem;
}
li {
margin-bottom: 0.5rem;
}
</style>
```
**Correct Code:**
```vue
<template>
<article class="article">
<header class="article-header">
<h1 class="article-title">{{ title }}</h1>
<p class="article-subtitle">{{ subtitle }}</p>
</header>
<section class="article-content">
<p class="article-text">{{ content }}</p>
<ul class="article-list">
<li v-for="item in items" :key="item" class="article-list-item">
{{ item }}
</li>
</ul>
</section>
</article>
</template>
<style scoped>
/* GOOD: Class selectors are faster */
.article {
max-width: 800px;
}
.article-header {
margin-bottom: 2rem;
}
.article-title {
font-size: 2rem;
}
.article-subtitle,
.article-text {
line-height: 1.6;
}
.article-list {
padding-left: 1.5rem;
}
.article-list-item {
margin-bottom: 0.5rem;
}
</style>
```
## How Scoped CSS Compiles
Vue transforms scoped CSS by adding data attributes:
```css
/* What you write */
p { color: red; }
.text { color: blue; }
/* What Vue generates */
p[data-v-7ba5bd90] { color: red; }
.text[data-v-7ba5bd90] { color: blue; }
```
Browser CSS matching for `p[data-v-xxx]`:
1. Find all `<p>` elements (element lookup)
2. Check each for the `data-v-xxx` attribute (attribute check)
Browser CSS matching for `.text[data-v-xxx]`:
1. Find all elements with class `text` (class lookup - optimized)
2. Check each for the `data-v-xxx` attribute
Class lookups are highly optimized in modern browsers, making option 2 faster.
## When Performance Matters
This optimization matters most when:
- Rendering large lists (100+ items)
- Complex component trees
- Animation-heavy interfaces
- Mobile devices with limited CPU
```vue
<template>
<!-- 1000 items - performance difference is noticeable -->
<ul class="list">
<li v-for="item in items" :key="item.id" class="list-item">
<span class="item-name">{{ item.name }}</span>
<span class="item-price">{{ item.price }}</span>
</li>
</ul>
</template>
<style scoped>
/* Fast selectors for large lists */
.list { ... }
.list-item { ... }
.item-name { ... }
.item-price { ... }
</style>
```
## Acceptable Element Selectors
For small components with few elements, element selectors are fine:
```vue
<template>
<button class="btn">
<span>{{ label }}</span>
</button>
</template>
<style scoped>
/* OK for small, simple components */
.btn {
padding: 0.5rem 1rem;
}
span {
font-weight: bold;
}
</style>
```
## CSS Modules Alternative
For performance-critical components, CSS modules avoid the attribute selector entirely:
```vue
<template>
<p :class="$style.text">Content</p>
</template>
<style module>
/* Generates unique class names without attribute selectors */
.text {
color: red;
}
/* Compiles to something like: .text_abc123 { color: red; } */
</style>
```
## Reference
- [Vue Loader Scoped CSS](https://vue-loader.vuejs.org/guide/scoped-css.html#child-component-root-elements)
- [CSS Selector Performance](https://csswizardry.com/2011/09/writing-efficient-css-selectors/)