BEMITのレスポンシブなwidthヘルパークラスを生成する@mixin

BEMITというのは[csswizardry (Harry Roberts)](https://github.com/csswizardry)が提唱しているBEM（[MindBEMding](http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/)）をベースにした命名規則です。

[BEMIT: Taking the BEM Naming Convention a Step Further – CSS Wizardry](http://csswizardry.com/2015/08/bemit-taking-the-bem-naming-convention-a-step-further/)

その中で「Responsive Suffixes」というものがあります。これは、そのclassにブレイクポイントが指定されていることを明示するために`@md`といった接尾辞を付けるというものです。


```scss
<div class="o-media@md">
  <img src="" alt="" class="o-media__img@md" />
  <p class="o-media__body@md"></p>
</div>
```

今回はこのResponsive Suffixesに対応したレスポンシブな`width`のヘルパークラスを生成する@mixinを紹介します。この@mixinはGitHubに上がっていたソースコードに若干の変更を加えたものです。

[frcss/_trumps.widths.scss at master · csswizardry/frcss](https://github.com/csswizardry/frcss/blob/master/_trumps.widths.scss)


## 生成されるclassとマークアップ例
生成されるclassはこのようになります。

```scss
.u-1\/12 { width: 8.33333% !important; }
.u-2\/12 { width: 16.66667% !important; }
.u-3\/12 { width: 25% !important; }
.u-4\/12 { width: 33.33333% !important; }
.u-5\/12 { width: 41.66667% !important; }
.u-6\/12 { width: 50% !important; }
.u-7\/12 { width: 58.33333% !important; }
.u-8\/12 { width: 66.66667% !important; }
.u-9\/12 { width: 75% !important; }
.u-10\/12 { width: 83.33333% !important; }
.u-11\/12 { width: 91.66667% !important; }
.u-12\/12 { width: 100% !important; }

@media screen and (min-width: 400px) {
    .u-1\/12\@sm { width: 8.33333% !important; }
    .u-2\/12\@sm { width: 16.66667% !important; }
    .u-3\/12\@sm { width: 25% !important; }
    .u-4\/12\@sm { width: 33.33333% !important; }
    .u-5\/12\@sm { width: 41.66667% !important; }
    .u-6\/12\@sm { width: 50% !important; }
    .u-7\/12\@sm { width: 58.33333% !important; }
    .u-8\/12\@sm { width: 66.66667% !important; }
    .u-9\/12\@sm { width: 75% !important; }
    .u-10\/12\@sm { width: 83.33333% !important; }
    .u-11\/12\@sm { width: 91.66667% !important; }
    .u-12\/12\@sm { width: 100% !important; }
}
```

`@`をCSSで使用するためにはバックスラッシュ`\`でエスケープする必要があります。HTMLにclassを指定する時にはエスケープする必要はありません。

```html
<div class="c-grid">
  <div class="c-grid__item u-8/12@md"></div>
  <div class="c-grid__item u-4/12@md"></div>
</div>
```

## @mixin
まずブレイクポイント用の変数をマップで定義します。

```scss
$breakpoints: (
    'sm': 'screen and (min-width: 400px)',
    'md': 'screen and (min-width: 768px)',
    'lg': 'screen and (min-width: 1000px)',
    'xl': 'screen and (min-width: 1200px)',
) !default;
```

使用する@mixinはこのようになります。上記の変数と合わせて[SassMeister](http://sassmeister.com/)に貼り付けるとコンパイル結果が確認できます。

```scss
// 取得したいカラム数をリスト形式で指定します。
// 例えば`1 2`を指定すると1/1, 1/2, 2/2の`width`を取得できます。
$width-cols: 12 !default;

// @param {Number} $cols - 取得したいカラム数を指定します。
// @param {String} $breakpoint(null) - ブレイクポイント用のキーワードを付与します。
// @requires {List} $width-cols - 取得するカラム数を定義しています。
@mixin width($cols, $breakpoint: null) {
    // 受け取った値を$colに渡し、値の数だけ繰り返します。
    @each $col in $cols {
        // 格納した数値を繰り返して、その回数を$iに格納します。
        @for $i from 1 through $col {
            // .u- + 分子($i)/分母($col) + ブレイクポイント名という命名規則。
            // バックスラッシュ`\`でスラッシュ`/`をエスケープします。
            .u-#{$i}\/#{$col}#{$breakpoint} {
                // `percentage()`関数でパーセンテージに変換します。
                width: percentage($i / $col) !important;
            }
        }
    }
}

// ブレイクポイントのないclassを生成します。
@include width($width-cols);

// ブレイクポイント付きのclassを生成します。
// @requires {Map} $breakpoints - ブレイクポイントを定義しています。
@each $name, $breakpoint in $breakpoints {
    @media #{$breakpoint} {
        // @とkeyを第二引数に渡します。
        @include width($width-cols, \@#{$name})
    }
}
```

`width`という@mixinを作成し、レスポンシブ対応したclassを生成する場合は@eachを併用します。まず生成するカラム数を定義します。よく使われる12カラムのグリッドシステムだけが必要な場合は`12`と1つだけ指定をして、2カラムと3カラムも必要な場合は`2 3 12`とリストとして指定します。

```scss
$width-cols: 12 !default;
```

上記の変数に指定した数値の数だけ@eachで繰り返します。その数値は変数`$col`に格納します。

```scss
@each $col in $cols {
```

数値を格納した変数`$cols`を利用して、@each内で@forを使用します。

```scss
@for $i from 1 through $col {
```

次にセレクタを指定します。`$cols`は@for内で値が変わらないので分母になり、`$i`は@for内で数が増えていくので分子になります。`\/`はスラッシュをエスケープしていて、`$breakpoint`は`null`に指定されているので@mixinの第二引数を指定しない限り反映されません。

```scss
.u-#{$i}\/#{$col}#{$breakpoint} {
    width: percentage($i / $col) !important;
}
```

この状態で@includeするとブレイクポイントが指定されていないclassが生成されます。

```scss
@include width($width-cols);
```

最後に@eachを使用してブレイクポイントを付けたclassを生成します。マップ`$breakpoints`から`'screen and (min-width: 400px)'`などのブレイクポイントの条件を@mediaに渡します。`'sm'`などのkeyは@mixinの第二引数に渡します。

```scss
@each $name, $breakpoint in $breakpoints {
    @media #{$breakpoint} {
        @include width($width-cols, \@#{$name})
    }
}
```