The Cognitive Complexity of CSS

by Martin Donath, @squidfunk

Hi, I'm @squidfunk

  • 10+ years of debugging and fixing broken layouts
  • I love to build products front to back
  • I'm on the quest of solving cross browser layout
  • I'm actively researching (i.e. deconstructing) CSS

Yes. People are researching CSS

  • 2018 – Automated Repair of Mobile Friendly Problems in Web Pages
  • 2017 – Machine Learning and Evolutionary Computing for GUI-based Regression Testing
  • 2017 – XFIX - An Automated Tool for the Repair of Layout Cross Browser Issues
  • 2016 – Automated Reasoning for Web Page Layout
  • 2016 – Defect Prediction for Cascading Style Sheets
  • 2016 – Detecting and Localizing Visual Inconsistencies in Web Applications
  • 2016 – Using Visual Symptoms for Debugging Presentation Failures in Web Applications
  • 2015 – Code Smells in Cascading Style Sheets – An Empirical Study and a Predictive Model
  • 2015 – Detecting Redundant CSS Rules in HTML5 Applications - A Tree Rewriting Approach
  • 2015 – Detection and Localization of HTML Presentation Failures Using Computer Vision-Based Techniques
  • 2015 – Tool Support for Cascading Style Sheets' Complexity Metrics
  • 2014 – Automated Refactoring for Size Reduction of CSS Style Sheets
  • 2014 – Discovering Refactoring Opportunities in Cascading Style Sheets
  • 2014 – Finding HTML Presentation Failures Using Image Comparison Techniques
  • 2013 – Visualizing Change Impact of Cascading Style Sheets
  • 2012 – Automated Analysis of CSS Rules to Support Style Maintenance
  • 2012 – On the Analysis of Cascading Style Sheets
  • 2010 – CSS Code Quality - A Metric for Abstractness
  • 2009 – Complexity Metrics for CSS
  • ...

I like CSS

Easy to Learn, Hard to Master

  • Amazingly simple to read and write
  • Architecture: flexible and unopinionated
  • Hard to control when code base is growing
  • Complexity has to be managed strategically

Complexity

Computational Complexity

What resources (space and time) are necessary to solve a problem?
If computational complexity increases, performance decreases

Cognitive Complexity

How difficult is the code to read and understand?
If cognitive complexity increases, productivity decreases

Complexity

Computational Complexity

What resources (space and time) are necessary to solve a problem?
If computational complexity increases, performance decreases

Cognitive Complexity

How difficult is the code to read and understand?
If cognitive complexity increases, productivity decreases

What Could Possibly Go Wrong?

What's the background and text color of the first link?

                
                  
                
                  a.ext {
                    color: white;
                  }
                  ul li:first-child a {
                    background-color: white;
                  }
                  header a {
                    background-color: grey;
                    color: black;
                  }
                
              

What Could Possibly Go Wrong?

What's the background and text color of the first link?

              
                
              
                a.ext {
                  color: white;
                }
                ul li:first-child a {
                  background-color: white;
                }
                header a {
                  background-color: grey;
                  color: black;
                }
              
            

Scope and Precedence

Scope

The elements (i.e. subtrees) a rule applies to

Example

              
                /* All links */
                a { ... }

                /* All external links */
                a.ext { ... }

                /* All external header links */
                header a.ext { ... }
              
            

Precedence

The ordering of rules, in case multiple rules match an element

Algorithm

  1. Order rules by specificity
  2. Order rules with same specificity by location

Specificity

The weight of a rule given by a tuple, e.g. [0, 0, 1, 1]

Calculation

  1. 1 if inline declaration, 0 otherwise
  2. Number of id selectors
  3. Number of (pseudo-)class and attribute selectors
  4. Number of (pseudo-)element selectors

Specificity

The weight of a rule given by a tuple, e.g. [0, 0, 1, 1]

Example

            
              /* Specificity: [0, 0, 1, 3] */
              nav a:hover::before { ... }

              /* Specificity: [0, 1, 1, 2] */
              ul#primary-nav li.active { ... }
            
          

Try the specificity calculator at specificity.keegan.st

Scope and Precedence

A rule expresses scope and precedence at the same time

Example

              
                /* Large scope, low precedence */
                a { ... }

                /* Medium scope, medium precedence */
                a.ext { ... }

                /* Small scope, high precedence */
                header a.ext { ... }
              
            

What Could Possibly Go Wrong?

What's the background and text color of the first link?

              
                
              
                a.ext {
                  color: white;
                } /* [0, 0, 1, 1] */
                ul li:first-child a {
                  background-color: white;
                } /* [0, 0, 1, 3] */
                header a {
                  background-color: grey;
                  color: black;
                } /* [0, 0, 0, 2] */
              
            

Still not convinced?

Maximum number of matched selectors per element

Wait, there is more

Other factors that add to cognitive complexity

An incomplete list

  • Inherited properties
  • Conditional rules (media and feature queries)
  • Browser compatibility
  • Browser bugs

Complexity Management

Organisation Systems

BEM, OOCSS, SMACSS, Atomic CSS, ...

  • Guidance for large teams and code bases
  • Help keeping cognitive complexity low
  • Weird situations when followed religiously
  • Scope and Precedence issues still arise

Style Boundaries

Web Components, CSS Modules (CSS-in-JS)

  • Mitigate Scope and Precedence issues
  • Reduce or prevent accidental leakage
  • Mixing with global styles is cumbersome
  • Come with caveats (e.g. compatibility, duplication, ...)

Static Code Analysis

Descriptive Statistics

Analyse statistical properties of styles (#, avg, min, max)

Metrics

  • Lines of code
  • Selectors per rule, declarations per rule
  • Number of colors, font sizes, box shadows, ...
  • ...

Try the static code analyser at projectwallace.com

Descriptive Statistics

Analyse statistical properties of styles (#, avg, min, max)

  • Can give a general idea of code base health
  • Good for deduplication (colors, shadows, etc.)
  • Often (mistakenly) called complexity analysis
  • Too coarse for complexity management

Specificity Graph

Visualise precedence (location and specificity)

                
                  a { ... }
                  header a { ... }
                  nav ul a { ... }
                  header ul li a { ... }
                  a.ext { ... }
                  nav a.ext { ... }
                  a.ext:visited { ... }
                  #home header a { ... }
                
              

How the specificity graph should look

Specificity Graph

Visualise precedence (location and specificity)

              
                a { ... }
                a.ext { ... }
                a.ext:visited { ... }
                header a { ... }
                #home header a { ... }
                header ul li a { ... }
                nav ul a { ... }
                nav a.ext { ... }
              
            

How the specificity graph actually looks

Specificity Graph

Visualise precedence (location and specificity)

              
                a { ... }
                a.ext { ... }
                a.ext:visited { ... }
                header a { ... }
                #home header a { ... }
                header ul li a { ... }
                nav ul a { ... }
                nav a.ext { ... }
              
            

How the specificity graph for a component architecture looks

Runtime Analysis

Specificity Spectrogram

Visualise element-selector relations

Motivation

  1. See how the styles are actually used
  2. Quantify Cognitive Complexity per element
  3. Find (and fix) drops in specificity

Selector Networks

Visualise dependencies between selectors

Motivation

  1. See how the styles are actually used
  2. Quantify Cognitive Complexity per selector
  3. Find (and fix) accidental overrides

Demo

Conclusion

There's much to be done

Managing cognitive complexity strategically is crucial

  • Increased confidence when changing code
  • Higher productivity, reduced maintenance
  • Available tooling doesn't help at all
  • Currently no automated solutions

That's it

visit ginseng.ai to stay updated