analyze-css turns a stylesheet into a list of selectors worth fixing
CSS selectors complexity and performance analyzer
At a glance
- What is it?
- analyze-css parses a CSS file and reports metrics on it, from selector specificity and duplication to vendor prefixes and accidental property resets, with offenders attached to the rules that triggered them. It is a diagnostic tool rather than a fixer, and its rule set is the part you will want to extend.
- Who is it for?
- analyze-css suits a front end team that has inherited a large stylesheet and needs to know which parts of it are actually costing them, since the metrics and the per rule offenders make the arguments concrete rather than a matter of taste.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Rules bound to parser events, not a script that counts strings
The architecture sentence in the README is short and decides how the tool behaves. analyze-css is built as a set of rules bound to events fired by CSS parser. Each rule can generate metrics and add offenders with more detailed information. So the unit of the project is a rule, the unit of work is a parser event, and the output is the accumulated metrics plus the offenders each rule collected. That is a different shape from the usual regex and string counting approach, and the practical difference shows up in accuracy. A metric like specificity is computed from the parsed selector rather than estimated by counting characters, and a rule that fires on a declaration has access to the property and the value rather than to a line of text. The rules directory at the top level of the repository is where that set lives, next to lib/ for the analysis engine, bin/ for the command line entry point, data/ for the vendor prefix dataset, scripts/ for the maintenance jobs, and test/ for the test suite. The result is a tool where a metric is a side effect of a rule, so adding a new check means writing a rule rather than editing a counting function.
The metrics cover selector shape, not just file size
The metric list is the substance of the tool, and it falls into a few groups. Selector structure is measured with complexSelectors for selectors of more than three expressions, multiClassesSelectors for selectors with multiple classes, qualifiedSelectors for combinations like header#nav, .foo#bar or h1.title, redundantBodySelectors for things like body .foo, and redundantChildNodesSelectors for patterns such as ul li. Specificity is reported in six numbers, an average and a total for ID, for class, pseudo class or attribute, and for element, which is what lets you see a stylesheet where a handful of ID selectors carry a disproportionate amount of weight. Duplication is measured as duplicatedSelectors for selectors defined more than once and duplicatedProperties for repeated property definitions inside a selector. Legacy weight is captured by oldIEFixes for hacks like * html .foo and oldPropertyPrefixes for prefixes that are no longer needed, which the README says is powered by data provided by autoprefixer. The remaining checks are smaller but often actionable: importants, emptyRules, expressions, imports, mediaQueries, propertyResets for accidental property resets, redundantBodySelectors, parsingErrors, colors for unique colors used, comments and commentsLength, base64Length which warns about encoded data bigger than 4 kB, and notMinified which is set to 1 if the provided CSS is not minified.
The CLI reads a file, a URL or standard input
The command line tool analyzes local CSS files or remote CSS assets, and the install step is a global npm install.
npm install --global analyze-cssThere is also a version from the GitHub repository, published under the scoped name.
npm install --global @macbre/analyze-cssOnce installed, you point it at a file.
$ analyze-css --file examples/elecena.cssOr at a remote asset, and the tool can be told to ignore SSL errors when the asset is served from a host with a certificate you do not want to verify.
$ analyze-css --url https://s3.macbre.net/analyze-css/propertyResets.css --ignore-ssl-errorsCSS can also arrive on standard input, and the dash is what selects that mode.
$ echo ".foo {margin: 0 \!important}" | analyze-css -The output is JSON on standard output, and the --pretty option, with the -p shortcut, makes it readable for a person rather than a machine. Basic HTTP authentication is available through the --auth-user and --auth-pass options, and an HTTP proxy can be given with the --proxy option, its -x shortcut, or the HTTP_PROXY environment variable. That combination of proxy support and remote URL fetching is what makes the tool usable in a corporate network, and it is the part of the surface that the README spends most of its space on.
Offenders are the output that tells you what to change
The example result in the README is what makes the design legible. The JSON has a generator field, a metrics object and an offenders object, and the metrics object is a flat list of numbers, for example a selectors count of 712, a rules count of 433, a declarations count of 1288, a length of 55173, a duplicatedSelectors value of 7, a complexSelectors value of 37, an oldPropertyPrefixes value of 79, and a selectorsByClass value of 600 against a selectorsById of 25. On its own, a number like 37 complex selectors tells you nothing actionable. The offenders object is where the value is, because for the importants metric it lists the actual source that triggered the rule, here the string ".foo {margin: 0 !important}". So the tool is designed to be read twice. The metrics tell you the shape of the problem, and the offenders tell you which lines to open. That is also why the noOffenders option exists on the module interface, because in some pipelines you want the aggregate numbers without the source snippets, and that option is what lets the metrics go into a dashboard while the offenders stay in the log. The example JSON in the README carries a generator string of an older version, analyze-css v0.10.2, so treat the exact numbers there as an illustration of the shape rather than a current baseline.
As a module, in a Grunt task, and with Sass support
The CommonJS entry point returns a promise, so the analysis fits into an async script rather than a callback.
const analyze = require('analyze-css');
(async() => {
const results = await analyze('.foo {margin: 0 !important}');
console.log(results); // example? see below
})();Options are passed as a second argument, and the example shows the noOffenders flag.
const opts = {
'noOffenders': true
};
(async() => {
const results = await analyze('.foo {margin: 0 !important}', opts);
console.log(results); // example? see below
})();The package declares a bin entry at ./bin/analyze-css.js and sets preferGlobal to true, so the CLI is a first class part of the distribution rather than a wrapper. There is also a Grunt task published separately as grunt-contrib-analyze-css, created by a contributor, which uses a configurable threshold and compares the analyze-css result with it, and that is the shape you want for a build gate rather than a one-off report. The examples directory shows the range of inputs the project expects: plain CSS files, Sass sources in both .scss and .sass form, a mobile stylesheet, and a reset file. The package lists sass as an optional dependency, so preprocessor input is a supported path rather than an accident.
What the analyzer cannot tell you
Three limits are clear from how the tool is built. The first is that it measures the stylesheet you give it, not the page. The metrics say that a selector is complex or a rule is duplicated, which is a proxy for a cost, and the project positions itself as a complexity and performance analyzer, but nothing in the output connects a metric to a measured render time, so the tool ranks suspects rather than proving a regression. The second is that it works on CSS, not on the source that produced it. The Sass support means the tool can parse preprocessor input, but a large modern codebase often relies on CSS layers, custom properties, container queries or a utility framework where the selector count in the output is not the complexity the team experiences. The third is the age of some metrics. A large share of the checks exist to find old Internet Explorer hacks and obsolete vendor prefixes, which is genuinely useful when you inherit a decade old stylesheet and close to noise in a fresh project. The right way to use the tool is therefore to read the metric list as a set of indicators and let the offenders drive the work, and to extend it. The rules directory is where the checks live, the package carries its own test suite and eslint configuration, and the npm scripts include a prefixes job that regenerates the vendor prefix data with browserslist, so the project expects its own data to be maintained over time.
Editorial conclusion
analyze-css suits a front end team that has inherited a large stylesheet and needs to know which parts of it are actually costing them, since the metrics and the per rule offenders make the arguments concrete rather than a matter of taste. It is the wrong tool if you want the problems fixed, because it reports rather than rewrites, and it is the wrong tool for a Sass or modern CSS codebase where the real complexity sits in the preprocessor output rather than in the shipped file. Before you adopt it, run it on one real stylesheet and read the offenders output, because the metrics alone are numbers and the offenders are where the actionable detail lives, and check the node version, since the package declares engines node greater than 18 and the CLI is built on commander.
Frequently asked questions
How do I install analyze-css and run it on a file?
Install it globally with npm install --global analyze-css, or from the GitHub repository with npm install --global @macbre/analyze-css. Then run analyze-css --file examples/elecena.css, or point the --url option at a remote stylesheet, and use --pretty for readable JSON.
What does analyze-css measure about CSS selectors?
It reports metrics including complexSelectors for selectors of more than three expressions, multiClassesSelectors, qualifiedSelectors, redundant body and child node selectors, and six specificity averages and totals for ID, class, pseudo class or attribute and element, plus duplication, old IE fixes, obsolete vendor prefixes and accidental property resets.
Can analyze-css be used as a build step rather than a report?
Yes. The Grunt task published as grunt-contrib-analyze-css uses a configurable threshold and compares the analyze-css result with it, and the module returns a promise so the analysis can be run inside an async script with options such as noOffenders.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/macbre-analyze-css)Was this useful?