英語版はこちら。
OOCSSとは異なり、CSSでちゃんとオブジェクト指向を実現するための方法について解説します。
0. まず「オブジェクト指向」とは
「オブジェクト指向」はIT業界を支えている重要なパラダイムのひとつで、JavaScriptを含む様々なプログラミング言語でサポートされています。特徴としては「型」や「クラス」といった雛形を持ち、雛形から生成された「インスタンス」とクラスとを区別します。
クラスとインスタンスとを区別することで、長期的にメンテナンスしやすいプログラムにすることができます。クラスを修正したり、インスタンスに変更を加えたり、それぞれ自由に拡張できます。
こういったオブジェクト指向プログラム言語(OOPL)に共通の特徴は、CSSでは見られることがありませんでした。2008年にOOCSSという手法が登場した後もです。
それも今日でお終いです。さっそく見ていきましょう。
1. 親クラスは先頭大文字で
.Button
appearance: none
position: relative
display: inline-block
box-sizing: border-box
border: 1px solid
border-radius: 4px
height: 2em
padding: 0 0.75em
color: inherit
background-color: white
text-decoration: none
line-height: 1.9
vertical-align: middle
ちょっと馴染みのない書き方ですが、他の言語では一般的なやり方です。
var button = new Button();
こうすることで、親クラスとインスタンスとを見分けることができるようになりました。
2. インスタンス化はハイフンふたつで
インスタンス化はこのようにします。
<button class="Button button--upload" type="button">アップロード</button>
<a href="#" class="Button button--cancel">キャンセル</a>
3. インスタンスのプロパティを追加する
.button--upload
color: white
border-color: transparent
background-color: $green
.button--cancel
color: white
border-color: transparent
background-color: $red
親クラス側で念入りに書いてあるお陰で、インスタンスのプロパティはすっきり・短く書けます。
4. インスタンスは独自の子要素を持てる
<button class="Button button--upload" type="button">
<span class="button--upload__icon"></span>
<span class="button--upload__counter">1</span>
アップロード
</button>
.button--upload
color: white
border-color: transparent
background-color: $green
& &__icon
@include icon(upload)
& &__counter
position: absolute
top: -0.75rem
right: -0.75rem
padding: 0.1em
min-width: 1em
height: 1em
border-radius: 0.5em
overflow: hidden
font-size: 0.75em
line-height: 1
background-color: $red
& &__
は打ち間違いではありません。安全なスコープを作り出しています。
ここで覚えておくべきなのは、ここにある子要素はインスタンスのものであって、親クラスのものではないということです。つまり、これらは継承されません。
5. BEMとの違い
-
--
や__
といった、セパレーターが持つ意味合いは同じです - ただし、孫要素があっても
.block__element__element
というふうにはなりません - 子孫要素はそれぞれ識別できる名前であれば十分です
- フォーマットとしては
.type--identifier__element
になります
6. OOCSSとの違い
OOCSSはOOPLが持つ主要なコンセプトを実現していません。
- カプセル化
- 継承
- メッセージング
OOCSSの「OO」は比喩だそうです。
7. 親クラスが子要素を持つ場合の例
<div class="Popup">
<div class="Popup__window">
<div class="Popup__message">
<!-- メッセージをここに -->
</div>
<div class="Popup__action">
<!-- ボタンをここに -->
</div>
</div>
</div >
これらの子要素は親クラスの構造の一部なので、将来のマークアップで継承されます。
7.1 親クラスのプロパティ
.Popup
& &__window
width: 300px
position: fixed
top: 33%
left: 50%
margin-left: -150px
border-radius: 8px
background-color: white
& &__message
padding: 1rem
& &__action
padding: 0.5rem
border-top: 1px solid $gray
text-align: center
7.2 インスタンス化
<div class="Popup popup--confirm">
<div class="Popup__window">
<div class="Popup__message">
<p class="popup--confirm__message">メッセージを送信しました。</p>
</div>
<div class="Popup__action">
<button class="Button button--close-popup" type="button">閉じる</button>
</div>
</div>
</div >
7.3 インスタンスのプロパティ
.popup--confirm
& &__message
text-align: center
ここまでは .Button
の例と同様です。
8. 親クラスの子要素を上書きしたい場合は?
8.1 不正解例
以下のやり方はよくありません。
<div class="Popup popup--confirm">
<div class="Popup__window">
<div class="Popup__message">
<p class="popup--confirm__message">メッセージを送信しました。</p>
</div>
<div class="Popup__action">
<!-- 今回は閉じるボタンなし -->
</div>
</div>
</div >
.popup--confirm
.Popup__action
display: none
上のコードは、インスタンス側から親クラスの子要素を直接操作しています。この方法だと親クラスの内部構造に変更があった場合、インスタンスにも影響が出ている可能性があり、目視での検証作業を必要とします。やめましょう。
8.2 正解例
正しくは、引数を使って対応します。
<div class="Popup popup--confirm param--auto-close"><!-- ここに引数を追加 -->
<div class="Popup__window">
<div class="Popup__message">
<p class="popup--confirm__message">メッセージを送信しました。</p>
</div>
<div class="Popup__action">
<!-- 今回は閉じるボタンなし -->
</div>
</div>
</div >
.Popup
&.param--auto-close
.Popup__action
display: none
このコードなら、親クラスが自分の子要素をハンドリングしているだけなのでとても安全です。インスタンスは親クラスの内部構造について何も知る必要がありません。
以上で、オブジェクト指向なCSSが出来上がりました。以下のチェックリストもチェックしておきましょう。
- カプセル化 ✔︎
- 継承 ✔︎
- メッセージング ✔︎
9. 謝辞
最後までお読みいただき、ありがとうございました。
気に入っていただけると幸いです。