惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hacker News - Newest:
Hacker News - Newest: "LLM"
Webroot Blog
Webroot Blog
S
Security @ Cisco Blogs
H
Heimdal Security Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
N
News and Events Feed by Topic
H
Hacker News: Front Page
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Application and Cybersecurity Blog
Application and Cybersecurity Blog
SecWiki News
SecWiki News
N
News | PayPal Newsroom
T
Tor Project blog
W
WeLiveSecurity
A
Arctic Wolf
Security Archives - TechRepublic
Security Archives - TechRepublic
S
Secure Thoughts
月光博客
月光博客
AWS News Blog
AWS News Blog
D
Docker
C
CERT Recently Published Vulnerability Notes
MyScale Blog
MyScale Blog
Google Online Security Blog
Google Online Security Blog
大猫的无限游戏
大猫的无限游戏
T
The Blog of Author Tim Ferriss
I
InfoQ
人人都是产品经理
人人都是产品经理
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
Hacker News: Ask HN
Hacker News: Ask HN
Blog — PlanetScale
Blog — PlanetScale
博客园 - 【当耐特】
Engineering at Meta
Engineering at Meta
Stack Overflow Blog
Stack Overflow Blog
Recorded Future
Recorded Future
罗磊的独立博客
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
The Exploit Database - CXSecurity.com
D
DataBreaches.Net
S
Security Affairs
WordPress大学
WordPress大学
T
Threatpost
Microsoft Security Blog
Microsoft Security Blog
V
Vulnerabilities – Threatpost
The Hacker News
The Hacker News
S
SegmentFault 最新的问题
B
Blog RSS Feed
Project Zero
Project Zero
P
Proofpoint News Feed

CSS Wizardry

Front-End’s Missing Metric: The TBT Window Meet Your Users Where They Are with Obs.js Better Browser Caching with No-Vary-Search font-family Doesn’t Fall Back the Way You Think What Is CSS Containment and How Can I Use It? When All You Can Do Is All or Nothing, Do Nothing Obs.js: Context-Aware Web Performance for Everyone Low- and Mid-Tier Mobile for the Real World (2025) The Fastest Site in the Tour de France Making Sense of the Performance Extensibility API Why Do We Have a Cache-Control Request Header? HTML Is Not a Programming Language… Build for the Web, Build on the Web, Build with the Web Licensing Code on CSS Wizardry A Layered Approach to Speculation Rules Designing (and Evolving) a New Web Performance Score Core Web Vitals Colours The Ultimate Contract Templates for Tech Consultants: Protect Your Business and Get Paid Optimising for High Latency Environments Cache Grab: How Much Are You Leaving on the Table? blocking=render: Why would you do that?! Correctly Configure (Pre) Connections The Three Cs: 🤝 Concatenate, 🗜️ Compress, 🗳️ Cache What Is the Maximum max-age? How to Clear Cache and Cookies on a Customer’s Device The Ultimate Low-Quality Image Placeholder Technique Core Web Vitals for Search Engine Optimisation: What Do We Need to Know? The HTTP/1-liness of HTTP/2 In Defence of DOM­Content­Loaded Site-Speed Topography Remapped Why Not document.write()? Speeding Up Async Snippets Critical CSS? Not So Fast! Measure What You Impact, Not What You Influence Optimising Largest Contentful Paint Measuring Web Performance in Mobile Safari Site-Speed Topography Speed Up Google Fonts Real-World Effectiveness of Brotli Performance Budgets, Pragmatically Lazy Pre-Browsing with Prefetch Making Cloud.typography Fast(er) Time to First Byte: What It Is and How to Improve It Self-Host Your Static Assets Tips for Technical Interviews Cache-Control for Civilians Bandwidth or Latency: When to Optimise for Which ITCSS × Skillshare What If? CSS and Network Performance The Three Types of Performance Testing Getting to Know a Legacy Codebase Image Inconsistencies: How and When Browsers Download Images Identifying, Auditing, and Discussing Third Parties My Digital Music Setup Measuring the Hard-to-Measure Finding Dead CSS The Fallacies of Distributed Computing (Applied to Front-End Performance) Ten Years Old Relative Requirements Airplanes and Ashtrays Performance and Resilience: Stress-Testing Third Parties Refactoring Tunnels Little Things I Like to Do with Git Writing Tidy Code Configuring Git and Vim Base64 Encoding & Performance, Part 2: Gathering Data Base64 Encoding & Performance, Part 1: What’s Up with Base64? Code Smells in CSS Revisited Typography for Developers Moving CSS Wizardry onto HTTPS and HTTP/2 Ack for CSS Developers A New Year, a New Focus Preparing Vim for Apple’s Touch Bar Choosing the Correct Average CSS Shorthand Syntax Considered an Anti-Pattern CSS Wizardry Newsletter Nesting Your BEM? Improving Perceived Performance with Multiple Background Images Continue Normalising Your CSS Pure CSS Content Filter Pragmatic, Practical, and Progressive Theming with Custom Properties Refactoring CSS: The Three I’s Speaker’s Checklist: Before and After Your Talk Improving Your CSS with Parker The Importance of !important: Forcing Immutability in CSS Mixins Better for Performance Managing Typography on Large Apps White October Events Workshop Partnership BEMIT: Taking the BEM Naming Convention a Step Further Travelling Like You Want to, When You Have To Contextual Styling: UI Components, Nesting, and Implementation Detail Subtleties with Self-Chained Classes Cyclomatic Complexity: Logic in CSS Immutable CSS Can CSS Be Too Modular? More Transparent UI Code with Namespaces When to use @extend; when to use a mixin The Specificity Graph CSS Wizardry Ltd.: Year 1 in review
Keep your CSS selectors short
Harry Roberts · 2012-05-16 · via CSS Wizardry

Written by on CSS Wizardry.

Table of Contents

Independent writing is brought to you via my wonderful Supporters.

  1. Increases Selector Efficiency
  2. Reduces Location Dependency
  3. Increases Portability
  4. Reduces Chances of Selector Breakage
  5. Decreases Specificity
  6. Can Make Code More Forgiving
  7. Final Word

One thing I believe, as a very, very general rule of thumb, is that as sites get bigger, selectors should get shorter.

By this I mean that if you want to create extensible and maintainable, flexible and predictable websites, you should really take care to make your CSS selectors as dev-friendly as possible; i.e. short.

Keeping CSS selectors short helps with a lot of things:

  • Increases selector efficiency
  • Reduces location dependency
  • Increases portability
  • Reduces chances of selector breakage
  • Decreases specificity
  • Can make code more forgiving

This is a very vague list, so I’m going to address each in order. You will find that there is a lot of crossover between each point (e.g. reducing location dependency inherently means your selectors are more portable) but I feel they are all points in their own right.

Increases Selector Efficiency

I have written before about CSS selector efficiency. I’m going to gloss over a lot of the intricacies in this post so for a full background understanding I recommend you read Writing efficient CSS selectors first.

If we ignore actual types of selector (* {} is typically the slowest, depending on how it’s being used, IDs are the fastest followed by classes, descendants are comparably quite slow followed by pseudo-selectors) then in general it is safe to say that shorter selectors are faster.

This stands to reason, if we compare these two selectors:

html body .header .nav {}
.nav {}

There we can see pretty clearly that in the first example, the browser has to look out for four things, the .nav class, then the .header class, then the body element and then, finally, the html element (browsers read selectors right-to-left).

With the second example the browser only needs to look for one thing; the .nav class. The browser has four times less work to do to match that selector. Every time you write a selector try and trim as much losable stuff from it as possible. Instead of ul.nav {} (two checks) write .nav {} (one check). Instead of .nav li a {} (three) write .nav a {} (two).

Now, CSS selector performance is—by-and-large—not something we really need to worry about any more, but that doesn’t mean we should be wasteful. I’m sure none of us would miss a lost £5 but that doesn’t mean we go slipping banknotes into paper shredders… Selector efficiency does exist and you might as well improve it where you very easily can.

Reduces Location Dependency

By keeping selectors short you are likely to be reducing the amount of descendant (e.g. .sidebar .promo {}) and child (e.g. .sidebar > .promo {}) selectors. By removing these descending types of selectors you are reducing the necessity for an element to live inside another one. Let’s reuse the .sidebar .promo {} example…

By having a selector like .sidebar .promo {} we are saying we want to target any promotional item that lives in an element with the class of .sidebar. This means that we are tied to always using that styling inside a certain element; we have a dependency on location.

By replacing .sidebar .promo {} with something like .secondary-promo {} we can now place the element in question anywhere we wish. In the sidebar—as before—but now also in the footer, or in the header, or after an article.

By reducing descendants we can really reduce dependency and make things a lot more portable…

Increases Portability

So now that we’re not tied to locationally dependant selectors, we find that our components are a lot more portable. We can move things a lot more easily because our CSS doesn’t care where a thing lives, it just cares that it exists. Awesome!

Another way to increase portability is to not qualify selectors. A qualified selector is one like ul.nav {} or a.button {} or div.content {}.

Qualified selectors are bad because they reduce efficiency (more checks than we really need) but—more importantly—because they tie us to specific elements. We can’t now use that .button class on an <input> or a <button>, for example. We can’t apply .nav to an <ol> to make a breadcrumb.

Selectors should be element-agnostic. Your CSS shouldn’t care what element you’re wanting to apply styling to.

Another way to make selectors more portable is to drop elements altogether. Take this, for example:

/* Base widget styling */
.widget {}

/* Style up widget titles */
.widget > h2 {}

Here we have a troublesome selector; what if that <h2> needs to become a <h3>? What if we need to add another, non-titling <h2> as a child of .widget? We’ve made ourselves a very rigid and unportable selector here. Instead we should have:

/* Base widget styling */
.widget {}

/* Style up widget titles */
.widget-title {}

Now we can apply .widget-title to any element—let’s say a <h4>—and can now also have any number of unclassed <h4>s in the widget without them adopting any title styling. Ossom!

Reduces Chances of Selector Breakage

The longer a selector is, the more things the browser has to satisfy before it can match it. The more checks there are then—naturally—the more chance there is for something to go wrong.

A (very exaggerated) selector like body > div:nth-of-type(2) > article:first-child > p:first-child {}—borrowed from my talk Breaking Good Habits—has ten checks; ten things that must be satisfied in order for the browser to make that match.

All that needs to happen is the location of the div:nth-of-type(2) to change or the p:first-child to become a blockquote or the article:first-child to no longer be a child of the div:nth-of-type(2) or any manner of things before that selector will break. Simply replacing that with a class of .intro {} means that there is only one thing that could possibly break, and the chances of that happening are pretty much zero (you’d have to explicitly delete the class from your HTML to prevent a match).

Shorter selectors mean there is statistically less chance for things to go wrong.

Decreases Specificity

This is the big one! This is where it really matters!

Longer selectors have a higher specificity. Specificity is a nightmare and you should keep specificity as low as possible all of the time. We already know that we do not use IDs in CSS but a chain of selectors are often just as bad (though not quite).

A selector like .widget > h2 {} has a higher specificity (as well as the other problems we discussed) than a selector like .widget-title {}.

.nav li a {} has a higher specificity than .nav a (and is also less efficient). Reducing selector length reduces selector specificity and that is very important. High specificity leads to self-induced specificity battles that can only be won by making subsequent selectors more specific (or using !important, shame on you). This is a terrible thing. The easiest way to reduce specificity (after dropping IDs from your CSS completely) is to keep your selectors short.

Can Make Code More Forgiving

This is a very specific but very decent example of how short selectors can make code more forgiving. However, I will warn you, you can argue two sides of what I’m about to tell you; you can argue that it makes your code a lot more flexible and can gracefully handle breakages or you could argue that it allows breakages in the first place by being too lenient. Anyway, here’s a true story…

In working on a pretty huge project at Sky I stuck to my own rules and coded a (vertical) nav bar CSS like so:

.nav { /* Nav styles */ }

/* Note NO .nav li styles as this was a vertically stacking nav. */

.nav a { display:block; /* More styles */ }

Now, there was a CMS error which went undetected where the markup getting spat out was:

<ul class=nav>
    <a href=#></a>
    <a href=#></a>
    <a href=#></a>
    <a href=#></a>
</ul>

Spot the problem? No <li>s! This is really not cool but, as I had used .nav a {} instead of .nav li a {} nothing broke. My code was a lot more forgiving than if I’d had that third check in there.

Now, this doesn’t make the markup right, and it does actually allow poorer markup than a more verbose selector, but you can see how the CSS was very forgiving of a markup error.

Now I said you could argue both sides here, a more verbose selector means that we’d have spotted the CMS error immediately as no styles would have hit the <a>s. But! In the same breath, our CSS was flexible enough to be okay with that. Make of it what you will, because I too am sat on the fence and a little disappointed that the error wasn’t spotted, but here is a very specific example of how shorter selectors can lead to more forgiving CSS.

Final Word

I did mention that this is a rule I’ve applied to larger sites but, honestly, you should apply this everywhere. The things we’ve discussed tend to really come into their own (and their absence painfully aware) on larger builds, but they will definitely, definitely help you on builds of all sizes; small or large.

So, by using more classes and less descendants, keeping selectors short and portable, keeping selectors element-agnostic and generally considering maintenance and chance-of-change when writing our CSS, we can really easily improve the quality of our code infinitely. We can make things more efficient, more forgiving, more flexible and more reusable just by revisiting one of the most simple and fundamental aspects of CSS; our selectors.