Agent 5 CSS Refactor

Overview

  • Current State
  • Why Refactor
  • OOCSS
  • Practical Application
  • Results

Current State

  • 4 Files
    • bootstrap.css
    • styles.css (our stuff)
    • kendo.css
    • print.css (print-specific styles)
  • Size of styles.css - 208kB
  • # Rules - 1898
  • # Selectors - 2903

Why Refactor?

Codebase was a mess

@import "form_add-cc.less";
@import "form_create-case.less";
@import "form_create-contact.less";
@import "form_modal_create-contact.less";
@import "form_create-solution.less";
@import "form_edit-case.less";
@import "form_edit-contact.less";
@import "form_edit-employee.less";
@import "form_edit-site.less";
@import "form_edit-solution.less";
.cc-listing .alert {
  padding: 4px 35px 4px 14px;
}

.add-cc-form .contact-picker .contact-input-control {
  margin-left: 0px;
}

form_add-cc.less

form_create-case.less

div.contact-input-control {
  padding-bottom: 12px;
  .pick-contact {
    width: 100%;
    margin-bottom: 0;
  }
}

div.site-input-control {
  padding-bottom: 25px;
  .pick-site {
    margin-bottom: -10px;
  }
}
.content-row{
  margin-bottom: @baseLineHeight;
  padding-top: @baseLineHeight;
  position: relative;
}

solution-description.less

.nav-header {
  // 28 lines
  &:hover {
    .badge{
      background-color: @grayDarker;
      color: @grayLight;
      //right: 14px;
    }
  }
  .badge{
    .badge-gray();
    background-color: darken(@grayLight, 5%);
    top:6px;
  }
  &.active{
  // 8 lines
  .badge{
    background-color: @blue;
    color: @grayLight;
  }
}
// 12 lines
.nav-header {
  a.due-soon-navbadge{
    // 13 lines
    .badge{
    background: @grayDarker;
    top: 18px;
    color: @grayLight;
    }
    &:hover{
      background-color: @blue;
      color: @white;
      .badge{background-color: @white; color: @blue;}
      text-shadow: none;
      i{text-shadow: none;}
    }
  }
}
// At bottom
.badge{
  .badge-gray();
  background-color: darken(@grayLight, 5%);
  text-indent: 0;
}
.toolbar-wrapper{
  line-height: 10px;
  color: @grayDark;
  background-color: @white;
  float: right;
  margin-left: 15px;
  margin-bottom: 15px;	
  margin-top:-25px;
  margin-right: -25px;
  //...
}

mixins.less

.account-and-addresses{
  //...
  .toolbar-wrapper {
    margin-top: -15px;
    margin-right: -15px;
  }
}

containers.less

.history {
  .toolbar-wrapper{
    margin-top: 0;
    z-index: 1;
  }
}

tab_history.less

.toolbar-wrapper{
  margin-top: -10px;
  margin-right: -15px;
  float: right;
  position: relative;

  .cases-linked-badge {
    float: left;
    margin-right: 5px;
    margin-left: 0;
    margin-bottom: @baseLineHeight;
  }

  .icon-toolset-menu {
    float: right;
    margin: 0 5px;
  }
}

tools-menus.less

  • We used to have bootstrap and our styles build into one stylesheet. Why split them?
  • IE9 is smart

Summary

  • Code organization was terrible (hard to find rules you wanted to)
  • Rules were all over the place (hard to find which rule you cared about in your specific situation)
  • Specificity wars galore

Let's Try Something Different

(You guys are gonna hate the name)

Object Oriented CSS

OOCSS History

  • Created by Nicole Sullivan in 2009
  • Claim to fame was helping Facebook go on a stylesheet diet (19% less css, 44% less html, 50% gain in loading performance)
  • Challenges css "best-practices"
    • Use specificity
    • Create modules

OOCSS Goals

  • Code reuse
  • Easy dev introduction
  • Robust

OOCSS Principles

  • Separate Structure & Skin
  • Separate Content & Container

Structure vs Skin

.notification-info {
  border: 1px solid black;
  height: 200px;
  width: 400px;
  background: blue;
  color: black;
}

.popup-red {
  border: 1px solid black;
  height: 200px;
  width: 400px;
  background: red;
  color: white;
}

.box-narrow {
  border: 1px solid black;
  height: 200px;
  width: 200px;

  &.red {
    background: red;
    color: white;
  }

  .blue {
    background: blue;
    color: black;
  }
}
.box {
  border: 1px solid black
  height: 200px;
}

.box-wide {
  width: 400px;
}

.box-narrow {
  width: 200px;
}

.box-red { // Bad naming
  background: red;
  color: white;
}

.box-info {
  background: blue;
  color: black;
}
<div class="box box-wide box-red"></div>
<div class="box box-narrow box-info"></div>

Content vs Container

A Heading should not be a Heading on another part of the page

h1 { ... }

#module1 h1 { ... }

#module2 h1 { ... }

/* It's impossible to have a normal h1 in #moduleX now */
.nav-header {
  // 28 lines
  &:hover {
    .badge{
      background-color: @grayDarker;
      color: @grayLight;
      //right: 14px;
    }
  }
  .badge{
    .badge-gray();
    background-color: darken(@grayLight, 5%);
    top:6px;
  }
  &.active{
  // 8 lines
  .badge{
    background-color: @blue;
    color: @grayLight;
  }
}
// 12 lines
.nav-header {
  a.due-soon-navbadge{
    // 13 lines
    .badge{
    background: @grayDarker;
    top: 18px;
    color: @grayLight;
    }
    &:hover{
      background-color: @blue;
      color: @white;
      .badge{background-color: @white; color: @blue;}
      text-shadow: none;
      i{text-shadow: none;}
    }
  }
}
// At bottom
.badge{
  .badge-gray();
  background-color: darken(@grayLight, 5%);
  text-indent: 0;
}

Practical Application

  • Identify common elements
  • Think in terms of structure and skin, not in modules
  • Don't be afraid to have many classes on an element
.a5-card {
  .border-box;
  display: block;
  height: 190px;
  max-height: 190px;
  max-width: 33%;
  min-height: 190px;
  min-width: 32%;
}

.a5-card-small:extend(.a5-card) {
  height: 120px;
  max-height: 120px;
  min-height: 120px;
}

.a5-card-med:extend(.a5-card) {
  height: 150px;
  max-height: 150px;
  min-height: 150px;
}

.a5-card-full-width:extend(a5-card) {
  min-height: 20px;
  min-width: 95%;
}

Naming is Hard

.box {...}
.box-blue { background: blue; }
.box-red { background: red; }
.box {...}
.box-info {...}
.box-error {...}
.box {...}
.box-blue { background: teal; }
.box-red { background: orange; }

Enabling Customization

// Agent 5 Variables
// ------------------------------
// Colors
@grayLightest:          #f9f9f7;
@grayBorder:            #c3c3c3;

@greenDarkest:          #368959;
@greenLight:            #74cf9b;
@greenLightest:         #b1f4ce;
// Variables
// ------------------------------
@a5-card-bg: @white;
@a5-card-border-color: @grayLight;
@a5-card-wrapper-bg: @grayLighter;

// Structure
// ------------------------------
.a5-card-default {
  .border-radius(4px);
  background: @a5-card-bg;
  border: 1px solid @a5-card-border-color;
}

variables.less

a5-cards.less

@a5-card-bg: gray;
@grayLightest: #c9c9c9;

custom.less

Results

Red Flags

  • Overwriting rules through specificity
  • Re-declaring attributes often (float, margin, color, border, etc.)
  • Using "!important"
  • Getting deeper than 3 levels

Wrap-up

  • Old way
    • Code base gets bloated, unorganized over time
    • Have to be an expert in css just to add simple styles
    • Specificity Wars
  • New way
    • Easy developer ramp-up
    • Easy customization
    • More reuse
    • Smaller code base
    • Avoid specificity wars
    • Possible HTML bloat
    • More classes to know

Resources

  • Me
  • https://fronteers.nl/congres/2009/sessions/object-oriented-css
  • Google

Any Questions?

Agent5 CSS

By Craig Jennings

Agent5 CSS

  • 81