Refactor My Stylesheets: The Negative Value Mixin

I stumbled across this tweet linking to a sass file that provides “semantic 3-column layouts for drupal and a configurable grid with #right, #left columns.” Always pleased to see what people have made with Sass, I took a look.

Original Sass
@import 'blueprint/grid'
/*
  requires compass gem
  usage: compass compile path/to/grid.sass -e production --output-style nested
//
  defines Blueprint grid variables 

$blueprint_grid_columns: 24
$blueprint_grid_width: 30px
$blueprint_grid_margin: 10px
$blueprint_grid_outer_width: $blueprint_grid_width + $blueprint_grid_margin
$blueprint_container_size: $blueprint_grid_outer_width * $blueprint_grid_columns - $blueprint_grid_margin
  
//
  defines column widths for #left, #right. #main is calculated off these values
$grid_left: 5
$grid_right: 6

=body_columns( $left: 4, $right: 5, $columns: $blueprint_grid_columns )
  @if $left > 0
    #left
      +span( $left )
  @if ( ( $left + $right ) < $columns )
    #main
      +span( $columns - $left - $right )
      @if ( $right <= 0 )
        @if ( $blueprint_grid_margin > 0px )
          +last
  @if $right > 0
    #right
      +span( $right )
      @if ( $blueprint_grid_margin > 0px  )
        +last

body
  /*
    These class names are used by Drupal to indicate what kind of layout we have
    Handles visibility of #left, #right individually plus both hidden or both visible
  &.sidebar-left
    +body_columns( $grid_left, 0 )
  &.sidebar-right
    +body_columns( 0, $grid_right )
  &.two-sidebars
    +body_columns( $grid_left, $grid_right )
  &.no-sidebars
    +body_columns( 0, 0 )


#left, #main, #right
  +column-base

#page
  +span( 24 )
  margin: 0 auto
  +clearfix

What I found was a Sass file that I could have written myself a year ago. Enamored with mixins, I sometimes used them where they weren’t necessary. Now, with @extend in my arsenal, I find myself viewing sass files with new eyes. So as a fresh reader to this Sass file, I found it hard to understand what was going on. But abstraction should add clarity, not remove it. So I put on my refactoring hat and got to work.

First, I converted the file to use dashes. I just find them more readable and I find that getting started with a relatively simple change gets my coding momentum going. Next I noticed that the #page selector was effectively creating a blueprint container but not using the blueprint container mixin, so I changed it. Next, I noticed that the mixin was used four times – it was clearly the core of this module. But what was it doing? Styling body columns. Yes it was appropriately named. But what concept or trait was shared between those body columns? (That reason would often make a better name.) But I could not find that trait. There were 10 different branch paths possible with the mixin but only four uses of the mixin. It seemed that the only convention the mixin provided was an enforcement of the selector names of #main, #left, and #right. That might be useful if I, the reader, don’t need to know what those selectors are – but I do and so the only value provided by this mixin only served to move information I needed to know when looking at the parent selector to another place. So I deleted the mixin and replaced it with the equivalent sass. Then I extracted the last mixin to a .last class and @extended it in those places where the mixin was in use.

When I was done, the refactored version had exactly the same number of lines but I find this version of the stylesheet to be much easier to understand. I hope you do as well. I know that bbttxu did and I would like to thank him for letting me pick on his stylesheet.

Refactored Sass
@import 'blueprint/grid'
//
  requires compass gem
  usage: compass compile path/to/grid.sass -e production --output-style nested
//
  defines Blueprint grid variables 

$blueprint-grid-columns: 24
$blueprint-grid-width: 30px
$blueprint-grid-margin: 10px
$blueprint-grid-outer-width: $blueprint-grid-width + $blueprint-grid-margin
$blueprint-container-size: $blueprint-grid-outer-width * $blueprint-grid-columns - $blueprint-grid-margin
  
//
  defines column widths for #left, #right. #main is calculated off these values
$grid-left: 5
$grid-right: 6

body
  //
    These class names are used by Drupal to indicate what kind of layout we have
    Handles visibility of #left, #right individually plus both hidden or both visible
  &.sidebar-left
    #left
      +span($grid-left)
    #main
      +span($blueprint-grid-columns - $grid-left)
      @extend .last
  &.sidebar-right
    #right
      +span($grid-right)
      @extend .last
    #main
      +span($blueprint-grid-columns - $grid-right)
  &.two-sidebars
    #left
      +span($grid-left)
    #right
      +span($grid-right)
      @extend .last
    #main
      +span($blueprint-grid-columns - $grid-left - $grid-right)
  &.no-sidebars
    #main
      +span($blueprint-grid-columns)

#left, #main, #right
  +column-base

#page
  +container

@if ( $blueprint-grid-margin > 0px )
  .last
    +last