ぼくのかんがえたさいきょうのしーえしゅえしゅ

Realistic preprocessors-based CSS framework for mobile.

俺ももう30だし、夏なんで、CSSフレームワークはじめました。 とりあえず、UIエレメントとか作ってないし、CSSフレームワークとか言いながら、GithubのLanguage Staticsは98.3%、JavaScriptってな感じでGrunt Taskばかり充実してるような感じです、現状。

とりあえず、設計方針としてはマシなCSSを書くことを目標としている。この一年、スマホアプリのHTML/CSSコーディングをやってきたわけだが、度重なるUIの変更に耐えうるCSS、そして肥大化しないCSSとは何かずっと考えていて、特に答えという答えもで見つかっていわけだけど、とりあえずはこうしたほうがBetterなんじゃないかというの自分的に固まってきたので、公開してみた。

てか、最強のCSSなんて存在しないからなっ!!

ありがちな落とし穴

これを作るにあたって社内のほかのプロジェクトのCSSがどんな風に書いてるのか調べった結果、以下の点が気になった。

  • 無用な Vendor Prefixes
  • Data URI 多用
  • Nest が深い
  • @extend 多用
  • ID セレクタ使用

無用な Vendor Prefixes

基本、我々はWebkitをターゲットとしたWebアプリを作ってるので、-moz-とか、-ms-とかいらないはずなんだけど、なんか付いている・・・多分、CompassのCSS3 Mixinとか使ってると、デフォルトで全部のベンダープレフィックスが吐出されてしまうので、

$experimental-support-for-opera: false;
$experimental-support-for-mozilla: false;
$experimental-support-for-microsoft: false;

そういったものを吐き出さないように、変数で指定する必要がある。また-webkit-border-radiusのようにAndroid2.3+, iOS4.3+のバージョンをターゲットとしているのであれば、この場合のベンダープレフィックスは不要だ。どのバージョンからベンダープレフィックスが必要か、必要でないかはCan I use…で調べればよい(逆に正規プロパティを書いてないとこもあった)。

Data URI 多用

WebパフォーマンスにおいてHTTPリクエストを削減することは真っ先に優先されるべきことだが、あまり何でもかんでも画像をDataURI化してCSSファイルの中に記述してしまうと、CSSファイルの肥大化が問題になってしまう。CSSファイルがパースされなければレンダリングが始まらないのでCSSファイルの肥大化は絶対に避けなければならない。画像の1KBとCSSファイルの1KBを同じように考えてはいけない。

ましてやDataURI化すれば元の画像の2,3割ファイルサイズが増えるし、仕様的にあまり大きなファイルサイズのものに適用してしまうのは気をつけたほうがいい。私の場合はログインページなど一回しか出てこないような場面において使うアイコン画像などをHTML内に埋め込んでいる。何回でも出てくるような画像であればCSSスプライトしたほうが無難だ。

Nestが深い

ロケーションに基づいたスタイル付け、ページに基づいたCSSを書いていくと当然ネストが深くなっていく(ex. body.login-page a.login-btn > span)。ここではセレクタの数が増えることによるファイルサイズ増量が問題というわけでなく、詳細度が高まれば高まるほど、そこでしか使用できないセレクタになってしまうことが問題だ。結果、同じようなスタイルでも新しくCSSを記述しなければならずファイルサイズが増えていくことになる。

これらの解決策はできるだけ小さなモジュールに基づいてスタイル付けしていくことだ。基本、ネストは3レベルまでが許容値だ。

@extend 多用

@extendは素晴らしい機能だと思うが、あまりカジュアルに使用してしまえば、セレクタの増加につながる。マルチクラス(”class=“btn btn-primary”)でマークアップすれば基本的に代用できる機能だ。確かに、CSSファイル内で.btnを@extendして.btn-primaryを作ればclass=“btn-primary”だけになって簡潔だが、それ以上の意味は無い。ただいたずらに使えばCSSファイルが増えるだけなので、マルチクラスの使用を推奨する。

ID セレクタ使用

CSSのスタイル付けにおいてIDセレクタを使用しなければならない理由などない。それどころかIDを使えば詳細度が複雑になり、このスタイルを上書きするためにさらにIDを使用したりなど、チキンレースが始まり、結果ファイルサイズが肥大化する。HTML内でJSのフックとして id=“js-getElement”など使用するのは問題ないが、そのセレクタに対してスタイルをつけてはならない。

まぁ、そんな感じのことを考えつつ作りました。

CSS設計方針

CSS全体に関しては、以下の様な感じで。

  • 絶対にCSSを増やしたくない
  • class名で悩みたくない
  • 完璧じゃなくてもいい

絶対にCSSを増やしたくない

CSSとは時とともに増えるものだ。新しいページが増えるイコール、CSSを新規に追加しなければならない。そこでモジュールとして設計しておけば、新しいページができたとしても再利用ができ、新規に作らなければならないスタイルを最小限に抑えることが出来る。しかし、必ずしもモジュールの再利用ができるのかといったら、それは難しい問題だ。モジュールAとは微妙にちがうモジュールA’などが当然のごとく登場してくる。

これはデザイナーの責任とは100%言い切れない、クライアントの要望であったり、力のあるステークホルダーからの意見であったりと、UIとは最初に設計したものから変幻自在にその姿を変える。未来は誰にもわからないのである。

このため、私はモジュール単位で設計すると同時に、helper.sccsを充実させた。

# Directory structure
├── maple.css
└── sass
    ├── maple.scss
    ├── _core.scss
    ├── core
    │   ├── _base.scss
    │   ├── _helper.scss
    │   ├── _date.scss
    │   ├── _reset.scss
    │   └── _settings.scss
    ├── _modules.scss
    ├── modules
    │   ├── _bars.scss
    │   ├── _boxes.scss
    │   ├── _buttons.scss
    │   ├── _forms.scss
    │   ├── _headlines.scss
    │   ├── _listviews.scss
    │   └── _misc.scss
    └── vendors
        └── _myfont.scss

helper.scssはユーティリティ的に使える便利classをまとめたものだ。.c{ text-align:center !imporatnt} などのように単一のCSSプロパティだけを記述した短い名前のclassだ。

これらのCSSを充実させることで、パターンに外れたUIの変更、例えばこのページのこのモジュールだけ文字色はこれ!とか、このページの時は余白を大きく!などのケースは、.module.helperのようにマルチクラスで、モジュールに任意のヘルパーclassを追加するだけで対応できるようになる。

また、モジュールにはmarginやpositionなどの位置を指定するプロパティを記述しないほうが無難だ。モジュールとして設計している以上、それはどの場所にもおけることを前提としているので、そのようなスタイルをつけていると問題になることが多い。

これらの考えを適用したHTMLはclass="btn-primary mod mbx ca c"などのような属性値となるだろう、命名ルールはあとから説明するとして、このように複数のclass属性値を持つことは当然の流れなのではないかと最近痛感している。我々はWebアプリケーションを作っているのであって、Webドキュメントを作っているわけではない。しかし、CSS自体、ドキュメントをスタイル付けするために生まれたようなものである。CSS3になって表現的にはリッチになったものの、構文的には何も進化していない(だからCSSプリプロセッサがあるのだけど)、プロパティが増えただけだ。このような現状でやれば多少のほころびというか気になる点もでてくるのは仕方のないことだと思う。

モバイルアプリケーションフレームワークで有名なjQuery MobileやSencha Touchのclassの当て方を見てみれば分かるように、要素に対して3,4つのclass属性がついているのがざらだ。汎用性を持たせるにはこの方法が現実解なのだろう。

class名で悩みたくない

よく、class名とは『抽象的スギズ 具体的スギズ』なものが良いと言われているが、これを考えることはすごく難しい。ひどいときは2,3時間class名を考えているときがある。パッと思いつくこともあるが、先ほど述べたように、未来は誰にもわからない。もしこのモジュールが予定していたものと違った用途でも使われたら?などと考えはじめると、これでは具体的すぎるので、もうちょっと抽象的な名前にするか、いやこれでは抽象的すぎるといった具合に、英語辞書サイトをグルグル回ったりすることが日常である。

我々の仕事はclass名を考えることではない。確かに良い名前をつけることは良いことだが、頻繁なUI変更を繰り返せば、当初予定していた意味とは違った用途で使われることは予想できる。そこで私は、アルファベットの連番を採用した。

.{moduleName}-{alphabet}のように、btn-a, btn-b, btn-cといった具合だ。このような命名パターンで有名なのはbtn-primary, btn-secondaryといったものだろう。しかし、3つめのパターンのボタンが出てきた場合、なんてclass名になるのだろうか?btn-thirdlyだろうか?正解はbtn-tertiaryだそうだ。ちなみに、 quaternary, quinary, senary と続くそうだ。そもそも1つのサイトで何種類ものボタンが出てくること自体おかしいのだと言えばそうかもしれない。

が、現実問題、サイトにおいてボタンは何種類も出てくる。そこで、btn-tertiaryといった正しい名前をつけても我々はネイティブではないし、大抵の人が意味が分からないだろう。またbtn-primaryといった名前のつけたボタンも時がたてば’主要的な’使われ方もしなくなってくるのが現実世界だ。

そこで、できるかぎり意味性を排除したa,b,cであれば、破綻しない。なぜならもともと意味が無い、ニュートラルなものであるから。いかようにもパターンを増やしていける(増やさないほうがいいんだけど)。

よく、class名はセマンティックでなければならないとかほざいてる人がいるんだけど、

There are no additional restrictions on the tokens authors can use in the class attribute, but authors are encouraged to use values that describe the nature of the content, rather than values that describe the desired presentation of the content.

そんなこと仕様書に書いていない。Webにおいてセマンティックと言えば、セマンティック・ウェブを指していると思われ、機械的にデータを再利用できるような仕組みのことを言っている。つまりあなたがセマンティックだと思ってつけたclass=“primaryButton”はclass属性値がprimaryButtonということでしかなく、ブラウザがそれを読み取って主要なボタンを抽出するといった芸当なんてことはしてくれない。

セマンティックなマークアップをしたければ、microdataを利用するのが妥当だろう。(microformatsと混同してはいけない)

完璧じゃなくてもいい

最近読んだ記事に、『技術的負債を管理する』というものがある。技術的負債というのは素早くいい加減に実装したコードのことを言っている。この記事では技術的負債を受け入れるということが述べられている。

  • 技術的負債が常にあること
  • 技術的負債が常に悪い訳ではないこと
  • 技術的負債は完全に支払わなければならないものではないこと

CSSにおいて、技術的負債と言えば、style="margin-right:54px"などのようにstyle属性で書かれたものや、style要素など書かれたものを言うだろうか。CSS(じゃなくてもいいが)を管理する上において外部ファイルにまとめるのは当然だが、それではCSSファイルが肥大化する。

例えば、キャンペーンページなどそのページでしか存在しないスタイルを全ページで読み込む1つの外部CSSファイルに記述してしまうことは本当によいことだろうか?そのページを見ない人もそのスタイルの記述分のコストを負担させることになる。

私はそのような場合はキャンペーンページのビューにstyle要素として埋め込めば良いと思う。キャンペーンページなどは一度作ってしまえば、またキャンペーンが終わったりすればあまりコードを弄ることはない。この場合、このstyle要素で書かれたコードは技術的負債といえるのか?ということだが、負債として我々にのしかかって来るのかといえばそうでもない。

このキャンペーンが頻繁に更新する要な場合であればまた別途違った方法を考えるべきだが、そうなっていない以上はこのようなコードを受け入れる必要があると思う。同じようにヘルパーにないパターンのmarginが出てきた時も新しいヘルパーとしてhelper.scssに登録するのではなく、まずはstyle属性に書いて様子を見るのも1つの手段だと思う。

だって人間だもの、完璧じゃなくてもいいと思う。


まぁそうゆう方針で作っています。前提として、CSSは1つの外部ファイルとしてまとめているもので、UIの変更が頻繁にあるケースを想定しています。なので、一回作って終わり!と言ったケースであれば、別にクラス名はシングルでもいいし、ロケーションに基づいたスタイル付けでもなんでも良いと思う。

世の中ベストなCSSといったものがあるかどうかわからないけど、あったとしてもそのプロジェクト(運用)で、ベストなわけであって、自分のプロジェクトでもベストなことはないと思う。ただ、『これどこからいじったらいいんじゃい!』ってな最悪な状況にならないためにも最低限なルールなり決まりをつくったほうがいいかなと思っている。

ロードマップ

v1.0の正式リリースに向けて、UIスタイルを設けるつもりだけど、そんな格好いいものは用意できないので期待しないでほしい。むしろ、コーディングスタイルの例として設けるのが狙いです。

あと、この命名規則の弱点としてclass名からスタイルを類推しづらいというのが弱点なので、そこをカバーする上でスタイルガイドの作成というのは必須だろうと思っている。前にstyledoccoを使ったことがあるが自分的にしっくりきてないので、いいのを見つける必要性があるなーと。

CSSのためのGrunt Task

現時点でCSSは全然書いてないのはずーっとGrunt Taskの設定をしていたり、プラグイン作ってたりしてたという理由があります。マシなCSSを書くために必要なGrunt用意しましたー。

Mapleプロジェクトはgrunt-init-mapleを使えば、gurnt-init mapleでプロジェクトをスキャフォルドできます。あとはプロジェクトに必要なnode_moduleを落としてくれば、すぐに使えます!

$ git clone https://github.com/t32k/grunt-init-maple.git ~/.grunt-init/maple --recursive
$ git-init maple
$ cd /path/to/maple_project/src/tools 
$ npm install
# ローカルサーバが立ち上がってSassがwatchの状態になる。
$ grunt develop

grunt-contrib-connect / grunt-contrib-watch

ローカルサーバーとウォッチ、ライブリロード機能を有効にしています。コンポーネントページでモジュールを開発したりするとき便利です。

grunt-contrib-compass

Sass/Compassのコンパイルのために入れてます。Compassだけど、Retina用のCSSスプライト、新たにリライトしてみました。良かったら使ってね。

grunt-contrib-csslint

CSSのLintです。IDとか使ってたら怒られます。IEとか古いブラウザ対応のためのlintはオフにしてあります。どうゆう理由でそういったルールが設けてあるのか一度読んでおいたらタメになります。

grunt-csso

より高い率でミニファイしてくれるCSSOのgrunt pluginです。過去に記事かいたのでそこ参照。

grunt-csscomb

CSSプロパティをソートしてくれるgrunt pluginです。過去に記事かいたのでそこ参照。最初からMapleのルールでCSSでかけばそこまであれだけど、1つのセレクタに何十ものプロパティを書いてる時などは、これでプロパティ順を揃えればgzipの圧縮率を高めてくれます。

grunt-webfont

あと、最近webフォントを使ったのだけど、これを使う前はオンラインのwebフォント管理ツールとか使っていてすごく面倒くさいなと思っていた。けどこれ使えば、webフォントしたいSVGを任意のディレクトリにいれてgrunt webfontカマスだけさ!

grunt-imageoptim

Compassで生成されたスプライト画像(PNG)とかはPNG-24なので重いです。それをアルファ透過つきPNG-8にダウンコンバートしてサイズ量を減らしてくれます。要ImageOptim (Mac app) です。忘れがちなので、buildタスクに入れておきましょう。

grunt-kss

kss-nodeでスタイルガイドを生成してくれます。.btn-aとかクラス名からスタイルが類推しづらいのでスタイルガイドの作成は必須になってきます。

終わりに

別にCSS使わなくても単純にGruntfileだけ拝借して使ってもらっても構わない、何かの役に立てて貰えればさいわいです。

フレームワークは所詮ツールでしかありません。これ使えばすべてがOKということは、もちろんなく。CSSだけでどんだけがんばってもデザインがくそだったらCSSもくそになります。逆を言えば、デザイナーさんとうまくルール(パターン)を予め決めて開発をすれば驚くほどCSSは軽くなります。その辺はデザイナーさんと協力・コミュニケーションしながら共通認識(ゴール)を持つことが一番重要です。