Written by Harry Roberts on CSS Wizardry.
Table of Contents
- Total Rules, Selectors, Identifiers, Declarations
- Selectors Per Rule
- Identifiers Per Selector
- Specificity Per Selector
- Total ID Selectors
- Total Unique Colors
- Total Important Keywords
- Practical Usage
- The Worst Offender
- Anomalous Data
Parker is an absolutely fantastic,
beautifully simple static analysis tool that gives you some very insightful
metrics about your CSS files. Parker is built and maintained by Katie
Fenn, a developer from England who has since
become a friend.
I use Parker almost daily, and regularly go through it with my clients and
workshop attendees. Parker surfaces some really interesting numbers, and if you
know what they represent, you can draw some really valuable insights about your
CSS from them.
(As an aside, I have a pretty cool story about Parker:
I was delivering a workshop for a company in the UK a year or two ago, when a
woman in attendance asked me Do you ever use Parker?
My answer was an
enthusiastic Yes! I use Parker all the time! Do you?!
, to which she
replied Yeah… I built it.
That was how/when I met Katie.)
Okay. Let’s go.
If you haven’t got it installed already, you’ll want to do that first. Parker
(parker
) is a command line tool, which can be installed (install
) globally
(-g
) using npm (npm
):
You can then run Parker against a compiled stylesheet like so:
$ parker path/to/stylesheet.css
I’d recommend running your current project’s CSS file through Parker right now.
If you don’t have a stylesheet to hand, this
one
will work pretty well. That one will give you some output like this:
Total Stylesheets: 1
Total Stylesheet Size: 37178
Total Rules: 260
Total Selectors: 365
Total Identifiers: 487
Total Declarations: 522
Selectors Per Rule: 1.4038461538461537
Identifiers Per Selector: 1.6821917808219178
Specificity Per Selector: 9.293150684931506
Top Selector Specificity: 30
Top Selector Specificity Selector: .c-score[data-score^="0."]
Total Id Selectors: 0
Total Unique Colors: 25
Unique Colors: #FFFF00,#000000,#C0C0C0,#F3F3F3,#333333,#378BB5,#FFFFFF,#666666,#E4E4E4,#FF2100,#FF4200,#FF6300,#FF8400,#CC9E00,#999600,#668F00,#338700,#317CA1,#CC0000,#ABC123,#98AB1F,#999999,#CCCCCC,#FFFF88,#4099C5
Total Important Keywords: 60
Total Media Queries: 5
Media Queries: screen and (min-width: 1024px),screen and (min-width: 1280px),screen and (min-width: 720px),screen and (min-width: 480px),screen and (min-width: 1200px)
This is the CSS file that I will be using as an example throughout this article.
We’re not going to look at every single one of these metrics in this blog post.
Instead, we’ll look at the slightly more abstract data and work out what it
represents, and what insights it gives us. We have to dig a little deeper for
the implicit meaning in a lot of these values, but once we’ve worked out what
we’re looking for, we open up a whole new world of knowledge.
Before we get too deep into things, there are a few of things we need to be
aware of when using Parker:
- This article was written in context of Parker version
0.0.10
. If any
major changes are released, I will endeavour to update this article
accordingly. - Parker reports mean values. It would be quite nice to know that ‘most of
your selectors have class-level specificity’, rather than ‘the average
specificity across all of your selectors is roughly that of a class’. A
subtle but significant distinction. - Unfortunately Parker currently reports specificity incorrectly. Katie and
I are in active discussions about how best to tackle this, but for now it’s
technically incorrect. In real terms—unless you have hellishly nested
selectors using more than 10 classes—it shouldn’t impact you too much, but
it’s certainly something to be aware of.
With these points in mind, please remember: Parker is not failsafe; it is not
infallible and nor does it claim to be. Parker is best used as a rough guide; a
finger in the air. Parker’s job is just to present you with the numbers; it’s
down to you to know what those numbers represent, and what they mean for your
CSS.
That’s what this article is for.
Total Rules, Selectors, Identifiers, Declarations
All of these metrics are very self explanatory, and don’t need any special
mention. However, using two of these metrics we can actually polyfill a very
useful one that isn’t present (Katie and I are discussing its addition).
If we divide Total Declarations by Total Rules, we are left with the mean number
of declarations per ruleset:
Total Declarations ÷ Total Rules = Declarations Per Ruleset
Given our data set, we’re left with:
522 ÷ 260 = 2.007692308
We have an average of 2 declarations per ruleset.
What this tells us is how large each of our rulesets are: rulesets with lots of
declarations are probably quite monolithic, and could/should probably be broken
down into smaller composable responsibilities.
Take this example of an overly loaded ruleset:
.btn-login {
display: inline-block;
padding: 2em;
background-color: green;
color: white;
}
There are a number of problems present here. Firstly, the name btn-login
describes a very specific use case, making it very difficult to reuse. Secondly,
this is a pretty monolithic ruleset—it handles everything about this button,
mixing up structural and cosmetic responsibilities. Instead, we could break this
out into:
.btn {
display: inline-block;
}
.btn--large {
padding: 2em;
}
.btn--positive {
background-color: green;
color: white;
}
Three smaller and more composable rulesets, each with a smaller number of
declarations. CSS like this likely adheres to the Single Responsibility
Principle,
meaning we have very well defined and encapsulated rulesets that can be combined
and composed in a very modular fashion.
This is how the Declarations Per Ruleset metric comes in handy.
Selectors Per Rule
Selectors Per Rule: 1.4038461538461537
Selectors Per Rule is pretty simple. The following CSS has one selector per
rule:
.c-btn {
display: inline-block;
padding: 12px;
}
.c-btn--small {
padding: 6px;
}
.c-btn--large {
padding: 24px;
}
This CSS has two selectors per rule:
h1, .u-h1 {
font-size: 3rem;
}
h2, .u-h2 {
font-size: 2rem;
}
h3, .u-h3 {
font-size: 1.5rem;
}
Straightforward enough, but what does this actually tell us?
Well, we want a number as close to one as possible. Lots of selectors per rule
suggests that we’re applying the exact same declarations (styles) to a number of
different selectors. Perhaps we could create a single catch-all selector to
handle all eventualities, making our CSS smaller, and abstracting patterns out
into more reusable selectors. Let’s look at an example of some poorly written
CSS that has lots of selectors per rule:
input[type="text"]
input[type="email"],
input[type="password"],
textarea {
border: 1px solid #ccc;
background-color: #fff;
color: #333;
padding: 4px;
}
As we add more input types to this site, that list of selectors continues to
grow. A much simpler and shorter solution would be to tie all of this
information up into a single reusable class, for example:
.c-input-text {
border: 1px solid #ccc;
background-color: #fff;
color: #333;
padding: 4px;
}
So no matter if the input handles email addresses, regular text, passwords, or
an input type yet to be invented, we don’t need to maintain a very
implementation-specific list of selectors.
We often end up with lots of selectors per rule for two key reasons:
- Fear of using classes in our markup: The surprisingly still-persistent
fear and avoidance of judicious use of classes in our HTML often leads to
developers creating (and subsequently maintaining) unwieldy lists of
selectors that are all chained to the exact same declarations. By adopting a
more class-based architecture, we can begin to recycle these rules in a much
more terse and practical way. - Using Sass’
@extend
: Sass’@extend
functionality has long been
considered an anti-pattern for a number of reasons (please see
Extending silent classes in
Sass,
When to use@extend
; when to use a
mixin,
Mixins Better for
Performance).
@extend
transplants selectors from one part of your project to all
converge on another. This has plenty of its own problems—detailed in the
linked articles—but it ultimately gives us a long, unwieldy list of selectors
chained to the same declarations; it increases our Selectors Per Rule.
In the extreme, it’s not uncommon to see @extend
being abused by developers
striving to never repeat the same declaration twice, leading to CSS that looks
like this:
Damn!
I wouldn’t like to be the one who maintains this 125 lines CSS selector! pic.twitter.com/0jAEb2h8VF— Gaël
Métais (@gaelmetais) 7 February
2015
A confusing mess of unrelated selectors all grouped together via coincidence as
opposed to reason. This fragments style information across your project, creates
unusual relationships and dependencies, removes encapsulation, and makes using
DevTools far more difficult:

Identifiers Per Selector
Identifiers Per Selector: 1.6821917808219178
Identifiers per selector is not to be confused with IDs. Identifiers per
selector is a measure of parts per selector. For example, take the following
CSS:
.c-btn {
/* 1 identifier */
}
.c-btn:hover {
/* 2 identifiers */
}
.c-widget--large .widget__title {
/* 2 identifiers */
}
header nav ul li a {
/* 5 identifiers */
}
Nesting, qualifying, and pseudo selectors all increase the Identifiers Per
Selector metric, and we should strive to keep this number as small as possible.
Identifiers Per Selector is effectively our Cyclomatic
Complexity.
Having a high number of identifiers per selector brings a few problems:
- Increased specificity: The more selectors we have in our compound
selector, the higher the specificity will be. Specificity is best kept as low
as possible. - Decreased portability: It’s harder to move styles around the view when
they’re tightly bound to a specific DOM structure. - Increased fragility: The more parts in a compound selector, the more
chances there are of something going wrong. - Increased filesize: A minor issue, but any bytes added to your selectors
cannot be reclaimed by a minifier—they’re dead weight.
For further reading about the problems with long selectors, please refer to my
article Keep your CSS selectors
short.
We want to see an Identifiers Per Selector value between 1 and 2 (remember, this
being a mean value means we can end up with decimals). We can’t have smaller
than 1 by definition. Anything over 2 means that, on average, every selector in
the codebase is the equivalent to everything being nested or qualified at least
once.
Our selectors should be as short as possible, but as long as necessary, and
Parker helps us measure this.
Specificity Per Selector
Specificity Per Selector: 9.293150684931506
As mentioned, specificity is reported slightly incorrectly by Parker, but things
should still work okay for the most part.
In CSS, every type of selector has an inherent specificity value:
- Universal selector: 0/ignored
- Element selectors: 1
- Class selectors, attribute selectors, pseudo selectors: 10
- IDs: 100
When we look at a complex/compound selector, we add together all like selectors
(i.e. all IDs, all classes, all elements) and present the overall specificity as
three separate integers. For example:
…has a specificity of 200, 0, 2
because we have two IDs at 100 each, zero
classes at 10 each, and two elements at 1 each. Unfortunately, Parker reports
this as 202
as a result of adding the three integers together.
…has a specificity of 0, 20, 0
because we have zero IDs at 100 each, two
class-like selectors at 10 each, and zero element selectors at 1 each.
…has a specificity of 0, 10, 1
because we have zero IDs at 100 each, one
pseudo class at 10 each, and one element at 1 each. Unfortunately, Parker
reports this as 11
as a result of adding the three integers together.
Because we are (or should be) working to a class-based architecture, we want to
see a number as close to 10 (the specificity of a single class) as possible.
Remember, however, that Parker reports mean values, so if you have lots of
element selectors or (heaven forbid) ID selectors, your reported value may be
skewed as a result.
Because it’s inevitable that we’ll have some nested selectors, and will
certainly have pseudo selectors attached to some of our classes, I would
typically deem a Specificity Per Selector value of anything up to 20 as being
‘safe’. Anything over 20 means that, on average, every selector in the codebase
is the equivalent to two classes worth of specificity.
If your value is over 20, you’ll want to remove any/all IDs you have in the
project, and look to reduce all unnecessary nesting and qualifying to get your
selectors as flat as possible.
Total ID Selectors
This number should be exactly zero, as there is absolutely no good reason
whatsoever to use IDs in CSS. They are infinitely more specific than classes,
and instantly impossible to reuse: both of these traits are diametrically
opposed to the pursuit of modular and reusable CSS.
Further reading: When using IDs can be a pain in the
class…,
Hacks for dealing with
specificity.
Total Unique Colors
This metric is pretty subjective, and will depend a lot on your project. If
you’re working on a large site with lots of themes and sub-themes, expect to see
a relatively large number. If you’re working on a smaller site with a limited
obvious colour scheme, be suspicious of larger numbers.
Given the small size of the demo project we’re using for this article, a value
of 25 surprises me a little. I should create a design task to look through the
project and begin to audit and rationalise my use of colours.
Live update: I actually did just go and explore where all the numerous
unique colours were coming from, and I tracked it down to my colour-coded
scoring
component:
the existence of these colours is, thankfully, both intentional and justifiable.
Total Important Keywords
Total Important Keywords: 60
!important
isn’t as evil as we like to make out, so if you reported number is
not zero, you don’t necessarily need to panic. Just ensure that any instances of
!important
are only found against your utility classes.
To understand how and when we should use !important
, please see my recent
article The Importance of !important
: Forcing Immutability in
CSS.
To get a quick look at where !important
is being used in your project, try
running this inside of your CSS directory:
$ git grep --break -C 2 "\!important"
This will show you all instances of !important
with two lines of context (-C
) either side of the result. Hopefully the two lines of context allow you to
2
see the selectors that the !important
lives inside; if not, just try
increasing the number.
Practical Usage
Let me close with some little tips and tricks for getting some real and
immediate practical value out of Parker…
The Worst Offender
Initially we’ll want to run Parker over all of our entire compiled project, and
get a report for every single line of CSS we have. Not only does this give us a
handy project-wide overview of things, it tells us what our worst
offender is: it’s our Top Selector Specificity Selector.
This is the current worst selector in our project, so this is the first/next
thing we need to fix. Once we’ve refactored that down and removed it, we run
Parker again. Now we’ll get a new worst offender, and we refactor that, and so
on. This gives us tiny, bitesize chunks of refactoring work where we only ever
have to focus on our worst bit of CSS, rather than being over faced with a
project’s worth of fixes.
Anomalous Data
Calculating the mean values means that any anomalous data points will skew our
results. The problem with means is that if I have one hand in the fire and one
hand in the freezer, on average I’m a comfortable temperature. Means are only
useful when you’re comparing similar data points, so any outliers will affect
the results. This means that running Parker across an entire codebase might not
always give us an entirely representative report (if we have a legacy project
that has lots of nesting, or IDs, those selectors will pull our mean values a
little higher).
To combat this, I often pull out discrete features of the CSS project and run
Parker over those in isolation. For example, copy/paste your button styles out
of your compiled Stylesheet and into its own buttons.css
file, and run Parker
over that. This means we can study the quality of our CSS in smaller chunks
which has a few key benefits:
- We get much better results. As discussed above, we’re comparing like data
points, so get more representative metrics. - It isolates any potential refactoring work. If Parker suggests we need to
rework or refactor anything, at least we’re only dealing with discrete
features at a time, and not having to start refactoring our entire project. - We can descope certain bits of our CSS. There’s little use having Parker
report back on our reset or Normalize.css, so we can exclude any setup or
library code.
Parker is a really useful, valuable tool that presents us with some seemingly
obvious and simple data. As soon as we understand what these numbers represent,
and the principles behind them, we can quickly begin to assess and improve the
quality of our CSS.
I’d really recommend running Parker over your existing project(s) right now, and
then begin to make it a part of your regular development workflow.
Leave a Reply