Basis という CSS フレームワークを作っているのですが、CSS フレームワークが持つクラスと HTML の依存について考えたり悩んだりしています。
特に解決策とか結論とか浮かんでいないのであれですが。
例えばボタンを使う場合
例えばボタンのコンポーネントを使いたい場合、Basis だと次のようになります。
<a class="_c-btn">ボタン</a>
ワイヤーフレームとかプロトタイプであれば上記のように既存のコンポーネントのクラス名を指定してあげてバンバン組んで良いのですが、正式なデザインが決定して、これに装飾していこうとした場合どうするのがベストなのでしょうか。
デザインもすごくコンポーネント思考で「ボタンコンポーネントのデザインはこれで統一するぜ!」みたいな感じであれば良いですが、まぁひとえにボタンといっても色々なパターンがでてきちゃうのが通例かと思います。となると、._c-btn
を直接 CSS で上書きしちゃうのはまずい。ではそれぞれのボタンに対してオリジナルのクラスを追加して装飾しましょうと。こんな感じ。
// ベーシックなボタン <a class="c-btn _c-btn">ボタン</a> // パターンA的なボタン <a class="c-a-btn _c-btn">ボタン</a> // パターンB的なボタン <a class="c-b-btn _c-btn">ボタン</a>
上記で良いといえば良いのですが、これが増えてくると._c-btn
っていうクラスを一々付与しないといけないのがめんどくさいし、そもそも、本当は「CSS フレームワークが持つクラス」を使いたいんじゃなくて「CSS フレームワークが持つスタイル」を使いたいわけじゃないですが。だから理想的にはこう書きたいわけです。
// ベーシックなボタン <a class="c-btn">ボタン</a> // パターンA的なボタン <a class="c-a-btn">ボタン</a> // パターンB的なボタン <a class="c-b-btn">ボタン</a>
Basis は Stylus で書かれているため、上記を実現しようと思えば比較的簡単にできはします。
.c-btn, .c-a-btn, .c-b-btn { @extend ._c-btn; } /* あとはそれぞれ追加のスタイルを書く */ .c-btn { ... } .c-a-btn { ... } .c-b-btn { ... }
はい、これで解決。と言いたいところなのですが、これでもあと3つ問題があります。
3つの依存問題
1. JavaScript からの依存
Basis 本体は Stylus/CSS のみの構成なのですが、アドオンでドロワーとか固定ヘッダーのコンポーネントとかも提供していて、それらは JavaScript を持っています。例えばドロワーであれば「ボタンをクリックしたときにドロワーが開く」という動作をさせるために JavaScript でボタンがクリックされたときの動作を記述しているわけですが、この「ボタン」の指定をどうするのか、というのが問題になってきます。
現状はこんな感じ(本筋を伝えやすくするために実際のコードとは異なります)。
$('._c-drawer__btn').on('click', (event) => { おされたときの処理 });
「ボタン」を$('._c-drawer__btn')
、つまり._c-drawer__btn
という「CSS フレームワークがもつクラス名」で指定しています。これだと HTML 側で._c-drawer__btn
という「CSS フレームワークがもつクラス名」を付与しなければドロワーのボタンとして認識されない、つまり JavaScript からの依存が発生してしまっている、という問題があるわけです。
で、これを解決するために今は JavaScript 側で引数としてなんに対して実行するかというクラス名を渡せるようにしているのですが、これはこれでなんかダサい…。
2. IE9(Flexbox)問題
Basis はfloat
ではなくdisplay: flex
で段組みみを実現しています。そのため Flexbox に対応していない IE9 ではそのままでは段組みが再現されません。そこで、IE9については CSS を上書きし、display: inline-block
やdisplay: table
で段組みを実現するようにしています。で、もう察しはついているかと思いますが、「CSS を上書きする」ということは何らかのセレクタに対して上書きしますよ、という指定をしなければいけないわけで、そこで「CSS フレームワークがもつクラス名」への依存が発生してしまうわけです。
// モダンブラウザ用 ._c-row { display: flex } // IE9用 ._c-row { display: table }
なので、HTML側で._c-row
を付与しないと IE9 では段組みが再現されないわけで、IE9対応する場合は「CSS フレームワークがもつクラス名」のHTMLに対する依存を排除できません。
3. CSS 設計問題
Basis は CSS 設計に FLOCSS を採用しています。FLOCSS の命名規則は MindBEMding なので、例えば次のようなクラス名になります。
._c-row { ... &--middle { ... } &__col { ... } }
で、これの何が問題かというと、CSS フレームワークを利用する側でも FLOCSS(MindBEMding)が強制されてしまうという点です。普通に使うにしてもそうですし、@extend
で継承してもだめです。Stylus の継承は&
のセレクタ指定をまるごと継承してしまうため、例えば次のようにしても問題は解決できません。
// グリッドをオリジナルの命名に変更する .c-grid { @extend ._c-row; } // コンパイル結果 .c-grid { ... } .c-grid--middle { ... } .c-grid__col { ... }
利用側では OOCSS とかにしたい、となっても不可能…。
どうやって解決するのか
どれもピンときていないのですが、とりあえずいくつか思いついたものを。
js- 接頭辞を使う
これはよくある手法ですね。JavaScript のイベント割り当て先の指定を._c-drawer__btn
みたいな通常のクラスではなく._js-c-drawer-btn
のような JavaScript 専用のクラスを使う手法です。問題点としては、._js-c-drawer-btn
自体もクラス名であるため、JavaScript 専用だとしても利用側で CSS を当ててしまいやすいことです。
カスタムデータ属性を使う
js 接頭辞を持つクラスではなくカスタムデータ属性でイベント割り当ての指定ができないかとも考えてやってみたのですが、どういう属性名がベストなのかもやもやする、属性名だけで属性値は不要なんだけど、そういった指定が OK なのかよくわからない、ということでちょっとどうなのかなという感じです。属性セレクタでも CSS は当てられちゃいますが、直感的には当てずらいはずなので、うまい落としどころがあればカスタムデータ属性を使うというのはありな気もするのですが。
AMCSS みたいな属性の付け方も良さそうだなとは思ったのですが、HTMLの構文的にこれは許容されているのでしょうか…?
ミックスインを超小分けにする
JavaScript の問題ではなくて CSS 設計の問題を解決する用になりますが、@extend
だとエレメントやモディファイアがまるっと継承されちゃうということは、基本スタイル、エレメント、モディファイアをそれぞれミックスインに小分けして、それを@include
するようにすれば良いのではと。これは Sass 版の Basis でも一部やっていることではあったのですが、問題点としてはミックスインがめちゃくちゃ多くなるのでそれぞれのミックスイン名がよく把握できない、単純に作るのも使うのもめんどくさい、という点です。
まとめ
なにもまとまっていませんが。こういうのうまいことやっている CSS フレームワークってあるんですかねぇ。考えれば考えるほど、クラスと HTML の依存というのはそもそもそういうもので解決なんてないのではないか、という気もしてきますが。何か良い案があれば教えてください。