OOCSS の Spacing クラスのようなものを Sass で

OOCSS (Object Oriented CSS) の Spacing クラス のようなもの」というのは、よくあるこういったやつです:

/*
p,m = padding,margin
a,t,r,b,l,h,v = all,top,right,bottom,left,horizontal,vertical
*/

.pt0,  .pv0,  .pa0  { padding-top:     0px !important; }
.pr0,  .ph0,  .pa0  { padding-right:   0px !important; }
.pb0,  .pv0,  .pa0  { padding-bottom:  0px !important; }
.pl0,  .ph0,  .pa0  { padding-left:    0px !important; }

.pt10, .pv10, .pa10 { padding-top:    10px !important; }
.pr10, .ph10, .pa10 { padding-right:  10px !important; }
.pb10, .pv10, .pa10 { padding-bottom: 10px !important; }
.pl10, .ph10, .pa10 { padding-left:   10px !important; }

.pt20, .pv20, .pa20 { padding-top:    20px !important; }
...

.mt0,  .mv0,  .ma0  { margin-top:      0px !important; }
.mr0,  .mh0,  .ma0  { margin-right:    0px !important; }
.mb0,  .mv0,  .ma0  { margin-bottom:   0px !important; }
.ml0,  .mh0,  .ma0  { margin-left:     0px !important; }

.mt10, .mv10, .ma10 { margin-top:     10px !important; }
.mr10, .mh10, .ma10 { margin-right:   10px !important; }
.mb10, .mv10, .ma10 { margin-bottom:  10px !important; }
.ml10, .mh10, .ma10 { margin-left:    10px !important; }

.mt20, .mv20, .ma20 { margin-top:     20px !important; }
...

こんなふうに上下左右の paddingmargin を定義したクラスをあらかじめ用意しておき、必要に応じて HTML にクラスを付与して余白をコントロールしようというわけです。

これの扱いにくい点としては、用意したけど実際には使わない無駄なクラスが発生してしまうことや、新たなクラスを追加する際の編集が面倒なことなどが挙げられます。

で、この手法そのものの是非とかはとりあえず置いといて、Sass を使うとこういったコードを効率的に生成できますよ、という話。

/* SCSS */

@mixin spacing($size) {
  $type: 'p';
  $prop: 'padding';
  @for $i from 1 through 2 {
    @if $i > 1 {
      $type: 'm';
      $prop: 'margin';
    }
    .#{$type}t#{$size} { #{$prop}-top:    #{$size}px !important; }
    .#{$type}r#{$size} { #{$prop}-right:  #{$size}px !important; }
    .#{$type}b#{$size} { #{$prop}-bottom: #{$size}px !important; }
    .#{$type}l#{$size} { #{$prop}-left:   #{$size}px !important; }
    .#{$type}v#{$size},
    .#{$type}a#{$size} {
      @extend .#{$type}t#{$size};
      @extend .#{$type}b#{$size};
    }
    .#{$type}h#{$size},
    .#{$type}a#{$size} {
      @extend .#{$type}r#{$size};
      @extend .#{$type}l#{$size};
    }
  }
}

なんか無駄に仰々しい気もしますが、こんな感じのミックスインを定義しておいて、あとは必要な余白のサイズを引数にして @include します:

/* SCSS */

@include spacing(0);
@include spacing(10);
@include spacing(20);

これをコンパイルすると以下の CSS が生成されます (改行やスペースは読みやすいようにいじってます):

/* CSS */

.pt0,  .pv0,  .pa0  { padding-top:     0px !important; }
.pr0,  .ph0,  .pa0  { padding-right:   0px !important; }
.pb0,  .pv0,  .pa0  { padding-bottom:  0px !important; }
.pl0,  .ph0,  .pa0  { padding-left:    0px !important; }

.mt0,  .mv0,  .ma0  { margin-top:      0px !important; }
.mr0,  .mh0,  .ma0  { margin-right:    0px !important; }
.mb0,  .mv0,  .ma0  { margin-bottom:   0px !important; }
.ml0,  .mh0,  .ma0  { margin-left:     0px !important; }

.pt10, .pv10, .pa10 { padding-top:    10px !important; }
.pr10, .ph10, .pa10 { padding-right:  10px !important; }
.pb10, .pv10, .pa10 { padding-bottom: 10px !important; }
.pl10, .ph10, .pa10 { padding-left:   10px !important; }

.mt10, .mv10, .ma10 { margin-top:     10px !important; }
.mr10, .mh10, .ma10 { margin-right:   10px !important; }
.mb10, .mv10, .ma10 { margin-bottom:  10px !important; }
.ml10, .mh10, .ma10 { margin-left:    10px !important; }

.pt20, .pv20, .pa20 { padding-top:    20px !important; }
.pr20, .ph20, .pa20 { padding-right:  20px !important; }
.pb20, .pv20, .pa20 { padding-bottom: 20px !important; }
.pl20, .ph20, .pa20 { padding-left:   20px !important; }

.mt20, .mv20, .ma20 { margin-top:     20px !important; }
.mr20, .mh20, .ma20 { margin-right:   20px !important; }
.mb20, .mv20, .ma20 { margin-bottom:  20px !important; }
.ml20, .mh20, .ma20 { margin-left:    20px !important; }

たとえばもし 25px のがあとから必要になったら、@include spacing(25); と 1 行加えるだけ。これならあらかじめ大量のクラスを用意せずに必要なクラスだけを作るのが容易だし、無駄を少なく抑えられます。プロジェクト間で使い回すにもミックスインだけを持っていけばいいので楽。

あと、この手の CSS にはある種の後ろめたさのようなものがつきまとうものですが、それが Sass によって少しは軽減されるというか…