Maintainable CSS


with visual regression testing
and functional CSS




Sebastian Witowski

Problems of CSS



  • Cascading Style Sheets
  • Hard to maintain large files
    (fighting specificity with IDs and !important)
  • Duplicated and unused code

Duplicated CSS





...

                        


.header2 {
    font-size: 24px;
}

...

h2 {
    font-size: 24px;
}
                        

Unused CSS




.menu {
    font-size: 16px;
}

...

# Chained selectors and cascading
.menu ul li.active {
    font-size: 2em;
}
                        

CSS is hard to refactor


Refactoring CSS doesn't always work well

How can we solve this problem?

We can write tests for CSS

Tests for CSS?

Find 5 differences...

Find 5 differences game

...100 times

Find 5 differences game

Visual regression test


PhantomCSS

spectre

percy.io

Other tools



Challenges of visual regression testing:



  • Dynamic content
    (changing number or random blog post)

  • Sharing tests with your team
    (different OS, different graphic card, different configuration)

Writing maintainable CSS



  • OOCSS
  • BEM
  • SMACSS

Object Oriented CSS


1. Separate style from structure


.button {
    width: ...;
    height: ...;
    overflow: ...;
}

...

.alert {
    border: ...;
    background: ...;
    color: ...;
}
                        

Object Oriented CSS


2. Separate content from container


// BAD
.sidebar h3 {
    font-family: ...;
    font-size: ...;
}

// GOOD
.fancy-header {
    font-family: ...;
    font-size: ...;
}
                        

Block, Element, Modifier


Scalable and Modular Architecture for CSS (SMACSS)


  • Base
  • Layout
  • Module
  • State
  • Theme

Functional CSS



Cat

$1,000

...


.ba { border-style: solid; border-width: 1px; }
.b--black-10 { border-color: rgba(0, 0, 0, .1); }
.br2 { border-radius: .25rem; }
.br--top { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.db { display: block; }
.dt { display: table; }
.dtc { display: table-cell; }
.lh-copy { line-height: 1.6; }
.mw5 { max-width: 16rem; }
.w-100 { width: 100%; }
.dark-gray { color: #333; }
.mid-gray { color: #555; }
.pa2 { padding: .5rem; }
.mt1 { margin-top: .25rem; }
.mt2 { margin-top: .5rem; }
.mv0 { margin-top: 0; margin-bottom: 0; }
.mv4 { margin-top: 2rem; margin-bottom: 2rem; }
.tr { text-align: right; }
.f5 { font-size: 1rem; }
.f6 { font-size: .875rem; }
.measure { max-width: 30em; }
.center { margin-right: auto; margin-left: auto; }

@media screen and (min-width: 30em) {
  .pb3-ns { padding-bottom: 1rem; }
  .ph3-ns { padding-left: 1rem; padding-right: 1rem; }
  .f4-ns { font-size: 1.25rem; } }
@media screen and (min-width: 30em) and (max-width: 60em) {
  .w-50-m { width: 50%; } }
@media screen and (min-width: 60em) {
  .w-25-l { width: 25%; } }
                        


...


...

Functional CSS is:



  • Pure (no side effects)
  • Composable
  • Immutable
  • Transparent (rem vs em)

Frameworks



The good parts


  • Modifying existing styling is easier
  • No more wrestling with the specificity
  • Very easy to add themes
  • Coding is faster
  • Encourages HTML components
    (Tachyons components )
  • Encourages design consistency
  • Cache-friendly
  • Faster websites

The bad parts



  • "It's just replacing duplicated CSS rules with duplicated CSS classes!"

.table-list-triage {
    display: none;
}
.triage-mode .table-list-non-triage, .triage-mode .table-list-filters {
    display: none;
}
.boxed-group-list>li.approved .btn-sm, .boxed-group-list>li.rejected .btn-sm {
    display: none;
}
.repo-list .participation-graph.disabled {
    display: none;
}
.payment-methods .selected-payment-method {
    display: none;
}
.payment-methods.paypal-logged-in .paypal-sign-in {
    display: none;
}
.payment-methods.has-paypal-account .paypal-sign-in {
    display: none;
}
.currency-container .local-currency, .currency-container .local-currency-block {
    display: none;
}
.currency-container.open .default-currency {
    display: none;
}
.plan-chooser-repo-menu .price-label {
    display: none;
}
                        

...

...
  • ...
  • ...
    ...
    ...

    The bad parts



    • "It's just replacing duplicated CSS rules with duplicated CSS classes!"
    • Updating existing components can be a pain
    • Won't work with non-functional frameworks

    No more writing CSS?



    
    .pixel-perfect-button {
        font-size: 18px;
        margin: 47px;
        padding-top: 153px;
    }
                            

    It's ok to write CSS



    
    .modal {
        position: fixed;
        top: 50%;
        left: 50%;
        -webkit-transform: translate(-50%,-50%);
        -ms-transform: translate(-50%,-50%);
        transform: translate(-50%,-50%);
        *width: 600px;
        *margin-left: -300px;
        *top: 50px;
    }
                            

    Functional CSS is not a silver bullet

    CSS bloat vs HTML bloat



    
    
                            

    +

    
    sidebar {
        float: left;
        margin-right: 10px;
        width: 25%;
    }
                            

    vs.

    
    
    ...
    as

    Thank you





    Questions?