OOCSSモジュールのストラクチャとスキンをシングルクラスで表現


IEを除くほぼ全てのモダンブラウザでCSS変数(css variables|カスタムプロパティ)が使用できるようになりましたが、sassのmixinに相当する CSSネイティブの ＠apply 機能はcss4草案段階で実装はまだまだ先です。
[PostCSS](https://postcss.org/)の[cssnext](http://cssnext.io/)を利用することで先取り使用可能ですが、コンパイル後＠applyの場所に同じプロパティが複製されるためcssファイルが肥大化するデメリットもあります(minifyすれば気にするほどでもないかもしれませんが)。
以下、現段階のネイティブCSSのセレクタを利用し、class名によってmixin相当の機能を実現する試みです。

## css selector

OOCSSではスキンとストラクチャclassを定義し、

```:css
/* structure */
.btn {  }

/* skin */
.btn-primary {  }
```

マルチクラスで要素にスタイルを当てます。

```:html
<a href="#" class="btn btn-primary">test</a>
```

これをシングルクラス化するために、class属性セレクタの正規表現を利用します。

```
/* structure */
[class^=btn--],
[class*=\ btn--] {  }

/* skin */
.btn--primary {  }
```
btn--primary が btn-- のパターンにマッチするため、ストラクチャのプロパティを継承します。

```
<a href="#" class="btn--primary">test</a>
```

正規表現を利用したセレクタは、意図しないパターンにマッチしてしまう可能性があるため注意が必要です。

```
NG
[class*=btn--] {  }

このようなパターンにもマッチしてしまう
<a href="#" class="xxbtn--primary">test</a>
<a href="#" class="hoge-btn--primary">test</a>
```

クラスが一つだけの場合とマルチクラス指定にマッチするように二つのパターンをセレクタに使用することで、マッチする命名パターンの範囲を狭めています。

```
[class^=btn--] … class="btn--任意の文字列" にマッチ（先頭・単独class指定用）
[class*=\ btn--] … class="(半角スペース)btn--任意の文字列" にマッチ(複数class指定用)
半角スペース前のエスケープ(\)は必須。エスケープが無い場合は[class*=m-btn--]と解釈されます
```

命名規則にprefixを取り入れると影響範囲を明確にでき、より実用性が増します。

下例、[SMACSS](https://smacss.com/ja)のカテゴリ分類を取り入れ、モジュールプレフィックス(.m-)を追加した例です。

```
/* prefix rule */
.l- layout
.m- module

/* CSS */
[class^=m-btn--],
[class*=\ m-btn--] {  }
.m-btn--primary {  }
```

## sample



```html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Document</title>
<style>
/* sample layout */
li+ li { margin-top: 0.5em; }

/* structure */
.btn,
[class^=m-btn--],
[class*=\ m-btn--] {
  display: inline-block;
  margin: 0;
  padding: 0;
  padding: 8px 16px;
  font: inherit;
  font-size: 100%;
  appearance: none;
  color: #fff;
  border: 1px solid transparent;
  border-radius: 3px;
  background-color: #999;
  text-decoration: none;
}

/* skin */
.btn-primary,
.m-btn--primary {
  background-color: #00f;
}

/* skin + modifier */
.m-btn--secondary,
[class^=m-btn--secondary-],
[class*=\ m-btn--secondary-] {
  background-color: #f00;
}

.m-btn--secondary-large,
.m-btn--secondary.-large{
  font-size: 2em;
}
</style>
</head>
<body>
  <h1>sample</h1>

  <section>
    <h2>multi class(normal OOCSS)</h2>
    <h3>structure</h3>
    <ul>
      <li><a href="#" class="btn">class="btn"</a></li>
    </ul>
    <h3>skin</h3>
    <ul>
      <li><a href="#" class="btn-primary">class="btn-primary"</a></li>
    </ul>
    <h3>structure + skin</h3>
    <ul>
      <li><a href="#" class="btn btn-primary">class="btn btn-primary"</a></li>
    </ul>
  </section>

  <section>
    <h2>single class</h2>
    <h3>structure</h3>
    <ul>
      <!-- match -->
      <li><a href="#" class="m-btn--">class="m-btn--"</a></li>
      <li><a href="#" class=" m-btn--">class=" m-btn--"</a></li>
      <li><a href="#" class="hoge m-btn--">class="hoge m-btn--"</a></li>
      <li><a href="#" class="m-btn-- fuga">class="m-btn-- fuga"</a></li>
      <li><a href="#" class="hoge m-btn-- fuga">class="hoge m-btn-- fuga"</a></li>
      <!-- not match -->
      <li><a href="#" class="hogem-btn--">class="hogem-btn--"</a></li>
      <li><a href="#" class="btn--">class="btn--"</a></li>
      <li><a href="#" class="x-btn--">class="x-btn--"</a></li>
    </ul>
    <h3>structure + skin</h3>
    <ul>
      <!-- match -->
      <li><a href="#" class="m-btn--primary">class="m-btn--primary"</a></li>
      <li><a href="#" class=" m-btn--primary">class=" m-btn--primary"</a></li>
      <li><a href="#" class="hoge m-btn--primary">class="hoge m-btn--primary"</a></li>
      <li><a href="#" class="m-btn--primary fuga">class="m-btn--primary fuga"</a></li>
      <li><a href="#" class="hoge m-btn--primary fuga">class="hoge m-btn--primary fuga"</a></li>
      <!-- not match -->
      <li><a href="#" class="hogem-btn--primary">class="hogem-btn--primary"</a></li>
      <li><a href="#" class="btn--primary">class="btn--primary"</a></li>
      <li><a href="#" class="x-btn--primary">class="x-btn--primary"</a></li>
    </ul>
  </section>

  <section>
    <h2>modifier pattern</h2>
    <h3>structure + skin + modifier</h3>
    <ul>
      <!-- structure + skin -->
      <li><a href="#" class="m-btn--secondary">class="m-btn--secondary"</a></li>
      <!-- structure + skin + modifier -->
      <li><a href="#" class="m-btn--secondary-large">class="m-btn--secondary-large"</a></li>
      <!-- structure + skin + modifier(multi class) -->
      <li><a href="#" class="m-btn--secondary -large">class="m-btn--secondary -large"</a></li>
    </ul>
  </section>

</body>
</html>
```

## ＠apply sample

ネイティブCSSで実用できるのは何年後になるかは分かりませんが、＠apply の実装が進めば、命名を気にすることなくmixinできるようになります。

```css
:root {
  --m-btn-structure: {
    display: inline-block;
    margin: 0;
    padding: 0;
    padding: 8px 16px;
    font: inherit;
    font-size: 100%;
    appearance: none;
    color: #fff;
    border: 1px solid transparent;
    border-radius: 3px;
    background-color: #999;
    text-decoration: none;
  }
}

.m-btn--primary {
  @apply --m-btn-structure;
  background-color: #00f;
}

.m-btn--secondary {
  @apply --m-btn-structure;
  background-color: #f00;
}
```

## 参考
* [MDN 属性セレクター](https://developer.mozilla.org/ja/docs/Web/CSS/Attribute_selectors)
* [w3c Selectors Level 3](https://www.w3.org/TR/selectors-3/#selectors)
* [w3c CSS Custom Properties](https://www.w3.org/TR/css-variables/)
* [CSS @apply Rule](http://tabatkins.github.io/specs/css-apply-rule/)

以上、ありがとうございました。