CSS フレームワーク Basis の CSS 設計

mimizuku

Mimizuku Advent Calendar 9日目の記事です。昨日に引き続き CSS に関する話題です。今日は Mimizuku に使っている CSS フレームワーク Basis の CSS 設計について。

先日「CSSフレームワークを開発していて悩む、CSSフレームワークが持つクラスのHTMLへの依存について」「続・CSSフレームワークを開発していて悩む、CSSフレームワークが持つクラスのHTMLへの依存について」と CSS 設計に関する2つの記事を書きました。2つめの記事のほうで BEM でいう Block と Element それぞれについて個別の Mixin を定義してコンポーネントを組み立てるようにした、ということを書きましたが、実際に使ったり、その構成で新しいコンポーネントを作ろうとすると問題があることがわかりました。そこで、何が問題だったのか、ということも含めて現在の Basis の CSS 設計について書いていきたいと思います。

Block、Element それぞれを個別の Mixin で構成する問題点

Block、Element を個別の Mixin で構成する場合、いずれの Mixin も「完全に独立した状態」であることが必須になります。そうじゃないとわざわざ個別の Mixin にする意味が無いためです。しかしそうしてしまうと「Block、Element で共通して参照したい値」の扱いに困ることになります。例えば次のような場合です。

Block() {
    padding-left: 10px;
}
Block__Element() {
    margin-left: -10px;
}

何の意味があるんだ…みたいなコードですが考えるのがめんどくさいのであくまでサンプルとしてご覧ください。Block で 10px 内側に寄せたものを Element で同じ分だけネガティブマージンするという例です。ここで、この「10px」という数値はあくまで初期値であり、呼び出し側からは好きな値を設定して呼び出したい、とします。PHP などのプログラミング言語であれば、例えば Class にしてコンストラクタで渡したり、メソッドを通じて値を設定したりすることができますが、Stylus や Sass には Class の概念はなく、Mixin は単一の関数的なものであるため共通のプロパティを参照するということができません。

// 無理だけど本当はこう書きたい。無理だけど
Block(padding) {
    padding-left: padding;

    Element() {
        margin-left: (padding * -1);
    }
}
.Block {
    Block();
    &__Element {
        Block()->Element();
    }
}

どうしてもしようとすればグローバル変数を定義してそれを見る形になりますが、CSS フレームワークである以上、コンポーネントは複数箇所で使用されるわけで、グローバル変数で値を変更したりしてしまうと全てのコンポーネントに影響が出てしまい使いものにならないわけです。

Basis の CSS 設計

で、どうするか。Block、Element を個別の Mixin にするのをやめました。そもそもなぜ個別の Mixin にしたかというと Mixin を利用する側でフレームワークの命名規則に縛られず自由な命名でセレクタを定義できるようにしたかったためです。Block ごとに Mixin が定義される形、つまり Element が Block に内容される形だとどうしても Basis の命名規則である MindBEMding を使わざるを得なくなるためです。どちらもはとれないので検討した結果、命名規則を自由にしたいというこだわりを諦めることにしました。ということで、現状 Basis の CSS の構成は次のようになっています。

├ core
│ ├ variables          // グローバルな変数郡
│ ├ mixin                // 便利 Mixin。ヘルパー等
│ ├ abstract          // component の骨格を定義する Mixin
│ └ placeholder     // component の placeholder
├ foundation           // HTML 要素などのベーススタイル
└ object
     ├ component     // Modifier を含む component の完全なスタイル
     └ utility              // ヘルパークラス

生成された CSS は FLOCSS です(layout、project は提供していません。CSS フレームワークがプロジェクト固有のスタイルを持っているというのも変ですよね)。

variables

全体を通して共通化しておいたほうが良いグローバル変数を定義しているレイヤーです。例えばベースとなる文字サイズや行間サイズ、文字色などが定義されています。

mixin

mixin レイヤーでは便利機能的な Mixin やヘルパー的な役割の Mixin を提供しています。いわゆる Mixin ライブラリ的なもので、Bourbon みたいな感じのものです。

abstract

component は abstract → placeholder → component という3段階で具現化されるようにしています。命名規則は MindBEMding 縛りになってしまいますが、abstract で定義されているコンポーネントの骨格は必要最小限なので独自の component を作る場合にもベースとして使用しやすいようになっています。Basis は core にあたる部分だけを使用するオプションがありますので、CSS フレームワークは容量が気になるとか、CSS フレームワークのクラス名はセマンティックじゃねぇ、ということが気になる場合はこの abstract や mixin を使って独自にコンポーネントやスタイルを組み上げていくこともできるというわけです。

なるべく無駄な上書きは避けたいので、最低限それがないとコンポーネントとして成立しないというスタイルは固定で記述し、色や線などの無くても良いけどあったほうがよりコンポーネントらしくなる、というスタイルについては引数で渡すようにしています。例えばボタンコンポーネントだとこんな感じ。何が必須で何をオプションにするか、また何を必須にもオプションにもしないか、という判断かなかなか難しく、そこは試行錯誤する必要があるかなぁと思います。結構それは CSS 設計をするスキルとして重要かもなぁと思ったり。

placeholder

placeholder では abstract を作ってコンポーネントとしてそのまま使用可能な状態のスタイルを提供しています。もともとは abstract にあたるレイヤーが無く、その役割を placeholder が担っていましたが、abstract を追加したことで placeholder は別に無くても良いのでは?という気がしています。

component

component では placeholder を具現化 + Modifier を追加したクラスを提供しています。なぜ Modifier を placeholder ではなく component で提供しているのかというと、Modifier は結構使う人で必要だったり必要じゃなかったりがばらけるのではないか、という気がしたので、core にあたる abstract や placeholder では提供せずに具現化されたクラスである component で提供するようにしています。abstract や placeholder で独自にコンポーネントやスタイルを組み上げたい人の場合は Modifier も自分で考えて作りたいだろうなぁと、そういうことです。

まとめ

ということで、今回は CSS 設計について書きました。実際のサイトで使う想定での CSS 設計の記事は最近よく見るようになりましたが、CSS フレームワークを作る上での CSS 設計というのはあんまり見ないので、そういう意味では興味深い記事なのではと思ったのですがいかがでしょうか(自画自賛…

次回はもう1回 Basis について、変数を上書きするだけで文字サイズとか余白をバランスをとりながら良い感じでやってくれるよという話を書きます。ではではー

MW WP Form

MW WP Form はショートコードベースのフォームプラグインです。多くの機能を持っており、例えば、多くのバリデーションルール、問い合わせデータの保存、そしてグラフ機能集計などを使用することができます。

さらに詳しく
Habakiri

Habakiri

Bootstrap ベースのシンプルな WordPress テーマ。レスポンシブ、多くのカスタマイズ機能。圧縮された CSS・JS を使用する高速化対策。Microformats 対応。Sass、クラスベースの functions.php。

さらに詳しく
basis-stylus

Basis

軽量なレスポンシブ Stylus/CSS フレームワーク。Flexbox ベースのグリッドシステム、疎結合なコンポーネント、バーティカルリズム。

さらに詳しく