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

推荐订阅源

大猫的无限游戏
大猫的无限游戏
K
Kaspersky official blog
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog
aimingoo的专栏
aimingoo的专栏
M
MIT News - Artificial intelligence
小众软件
小众软件
云风的 BLOG
云风的 BLOG
腾讯CDC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Hugging Face - Blog
Hugging Face - Blog
S
SegmentFault 最新的问题
Stack Overflow Blog
Stack Overflow Blog
量子位
S
Secure Thoughts
G
GRAHAM CLULEY
C
CXSECURITY Database RSS Feed - CXSecurity.com
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
T
Threat Research - Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
G
Google Developers Blog
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
有赞技术团队
有赞技术团队
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Martin Fowler
Martin Fowler
The GitHub Blog
The GitHub Blog
Google DeepMind News
Google DeepMind News
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园 - 聂微东
宝玉的分享
宝玉的分享
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
Netflix TechBlog - Medium
Forbes - Security
Forbes - Security
Engineering at Meta
Engineering at Meta
S
Security Affairs
Help Net Security
Help Net Security
博客园 - 三生石上(FineUI控件)
AWS News Blog
AWS News Blog
博客园 - 叶小钗
Recent Commits to openclaw:main
Recent Commits to openclaw:main
V2EX - 技术
V2EX - 技术
Hacker News: Ask HN
Hacker News: Ask HN
Project Zero
Project Zero
H
Heimdal Security Blog
W
WeLiveSecurity
C
Check Point Blog

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 CSS Wizardry Ltd.: Year 1 in review CSS Guidelines 2.0.0
The Specificity Graph
Harry Roberts · 2014-10-29 · via CSS Wizardry

Written by on CSS Wizardry.

Table of Contents

Independent writing is brought to you via my wonderful Supporters.

  1. A better shape
  2. Addendum – 26 November, 2014

As we all (should) probably know by now, specificity is is one of CSS’ most troublesome features, and is an area that soon becomes hard to manage on projects of any reasonable size. Specificity is a trait best avoided, which is why we don’t use IDs in CSS, and we don’t nest selectors unless absolutely necessary.

However, we can’t avoid specificity completely: there are types of selector that we have to use on projects that are inherently more or less specific than others. For all we can avoid the worst bits, we need a way to manage the necessary parts. Enter The Specificity Graph.

The Specificity Graph is something I have only been speaking about recently, and is a cornerstone of my currently unpublished ITCSS architecture. It is a very simple model for diagrammatically assessing the overall health of your codebase in terms of specificity—a way of looking at an entire project’s CSS and highlighting any potentially troublesome areas of higher-than-ideal specificity. We can then use this snapshot to refactor and rearchitect old projects into a better shape, or to ensure we’re writing new projects in a sustainable manner.

It is worth highlighting explicitly and up front that the Specificity Graph is currently just a high-level and relatively crude model. Its aim is to give a very broad and general overview of the specificity of selectors across an entire project. It is not—yet, at least—an automated tool of any sort that will plot an accurate graph for you: it is a way of you assessing your own codebase and loosely plotting it yourself. One thing I would be keen to do is speak to Katie about putting the Specificity Graph into Parker one day (if she thinks it’s suitable, appropriate, and feasible).

The Specificity Graph works by taking a look at your entire project’s CSS, and plotting the specificity of all of its selectors on a simple line graph. On the x axis we plot Location in Stylesheet (whereabouts was the selector found? Line 10? Line 2663?) and on the y axis we plot the specificity of that selector (was it an ID? Was it a class? Was it a type selector?).

You can very roughly plot your own Specificity Graph by taking a look at your codebase at a very high level. It won’t be 100% accurate, but remember, this is just a model. You’ll likely find that, for most existing projects, you’ll get a graph that looks something like this:

A Specificity Graph showing poor distribution of specificity across a codebase.

As you can see, this is a very crude representation—we don’t even have any units on our axes!—but it instantly shows us the general distribution of specificity across this project. It’s erratic and poorly managed: these peaks and troughs are bad news. A peak represents a selector of a relatively high specificity, and occurs before a selector of a lower specificity. This is the specificity wars visualised: whenever you are in a valley, you have to climb back out to at least the height of the previous mountain if you want to overcome it.

Even this very basic model can tell us a lot about the current state of, and pitfalls with, the specificity in our project. We can visualise the places where we might expect problems, see why we have problems right now, and then we can use that information to refactor or rework our CSS into a better shape.

A graph with a lot of peaks and troughs is a bad Specificity Graph: it is telling us that our CSS is full of—or prone to—specificity issues because of weighty selectors being defined before lighter ones. We have a codebase that is likely to prove problematic because we have erratic and poorly managed specificity-and-source-order: we are more likely to spend time undoing or negating the effects of high specificity CSS that was defined too early in the project.

A spiky graph is a bad graph.

A better shape

It’s all well and good knowing what our Specificity Graph currently looks like, but what should it look like? Well, if we know we want to avoid peaks and troughs, we could presume that we want a flat graph: a flat graph means no specificity wars because there are no valleys to leave or mountains to overcome.

The issue there is that it’s impossible to have a perfectly flat line because there will always be some selectors that are of a lower or higher specificity than others. You have type selectors (blockquote {}, a {}, table {}, etc.), class selectors (.btn {}, .carousel {}, .media {}, etc.), and potentially even some !importants (on your helper and utility classes). It should go without saying that we don’t have any IDs.

The key here isn’t so much eliminating specificity completely, but managing it better. In general we do want to keep specificity low at all costs, but any unavoidable specificity we introduce needs taking care of appropriately.

We don’t want a flat graph, we want an upward-trending one:

A Specificity Graph showing much better, upward-trending distribution of specificity across a codebase.

An upward trending Specificity Graph represents CSS that is, essentially, written in specificity order. We start with low specificity selectors at the beginning of the project (the * {} selector for global box sizing, for example), followed up by type selectors (unclassed HTML elements, like ul {}), followed up by class-based component selectors (such as .site-nav {}, or .calendar {}) and finally ending with our overrides and helper classes (like .clearfix {}, or .hidden {}).

As a specific example, a lot of people tend to include their helper classes quite early on in a project, because they feel global and ‘frameworky’. The issue here is that these often carry !important, making them very high specificity. Including these types of rule early on in a project produces a huge spike in the Specificity Graph right near the beginning, setting the bar very high for subsequent work. Move all utility and helper classes to the very end of your CSS in order to introduce a spike right at the end, meaning you’re far less likely to have to undo or override high specificity selectors in your subsequent work.

Any further CSS you add to the project should be authored in the relevant places in order to maintain the upward trending graph. No more bolting things on to the end of a stylesheet! If you’re adding new rules for type selectors, do not put them after your modules’ styles; if you’re adding new utilities, put them after everything else.

Ordering CSS based on its specificity leads to much simpler and hassle-free maintenance, as well as offering improved scalability due to more evenly distributed complexity and a more sane working environment.

Always try to maintain an upward trending Specificity Graph across your projects. It is a very helpful way of keeping a high-level view of your code, it is a very simple litmus test and benchmark, and also provides a really effective way of writing better quality CSS in more general terms.

Addendum – 26 November, 2014

It is important to re-stress the word trending. The Specificity Graph should trend upward. There will be little ups and downs along the way, for example:

.module {
}

    .module__header {
    }


.module--large {
}

    .module--large > .module__header {
    }

Here we would get a kink in our graph where the .module--large > .module__header {} selector is double that of its neighbours. These kinks do, unfortunately, get magnified when we run our code through an actual Specificity Graph tool, which leads me to believe that the Specificity Graph is probably best kept as a conceptual model.

The aim of the Specificity Graph is to illustrate the importance of not having overly specific selectors appearing too early on. Close-ups of the graph will show many jagged, saw-tooth sections, but my aim has always been to concentrate on the overall trend of the whole graph. Saw-toothing is to be expected; what we do not want is all of our helper classes, carrying !importants, to appear at the beginning of our project.

Again, we need to look at the overall trend. The graph needs to trend upward, even taking into account the many ups-and-downs along the way.