サーバサイドエンジニアがCSS設計をした話

この記事はmwedアドベントカレンダー11日目の記事です。

昨日の 最近使い始めた開発支援系サービスの話 に引き続き @koheisg が担当させていただきます。(2回目)

エンジニアの幅を広げるという大層なことを書くと言ってしまいましたが、 この一年、みんなのウェディングで経験したCSS設計の経験を書こうと思います。

CSSの設計の話

CSSからサーバサイドのプログラミングまですべて、プログラマが担当するようになったとき、 まずオススメするのはシングルクラスのCSS設計です。 CSSはカスケーティングによって、記述をDRYにすることができますが、これはプログラミングの記述をDRYにする機能の継承やmixinよりも強力です。ですが、一歩間違うとすぐに設計が負債となってしまいます。

安易に継承しまくったオブジェクトの正体は突き止めにくいですよね?

ですので、カスケーティングでクラスを記述するマルチクラス設計は片手間マークアップエンジニアは手を出すべきではないでしょう。

CSSでのDRYを捨てて、SassのmixinでDRYにする

シングルクラスが嫌われるのは、マルチクラスと違ってCSSの記述量が増えるからです。 ブラウザの通信も大きくなるし、単純に作業効率も下がるります。

名前大事

どんなプログラミングであろうとも名前は大事ですね。 CSSは名前空間がグローバルであるため、さらに大事です。 そして、シングルクラスでCSSを設計することのメリットは可読性ですので、 込み入ったCSSのルールに縛られる必要がありません。

様々な設計手法や命名ルールがありますが、 私は1つ1つの名前が説明的にできるBEM(BEMbinding)をルールに採用しました。

.block {}
.block__element {}
.block--modifier {}

MindBEMding - getting your head ‘round BEM syntax

名前衝突を避ける

名前を衝突を避けるためにかなりストイックな取り組みをしました。 Blockにつきファイルを別の名前にしたのです。 sproketsなど最後に1つのCSSファイルとして生成する仕組みがないと難しいです。

Railsでおこなうときは下記のようなファイルの構成になります。

app/assets/stylesheets/blocks/one-block.scss
                              two-block.scss
                              three-block.scss
                              ...

また、みなさんはファイル数が多くなりすぎるのでは? という危機感を持たられると思います。実際なりました。 しかし、RubyMineのジャンプ機能を使えば思ったよりも苦にはならないこともわかりました。

Sassの機能を積極的に使う

BEMを使い、ファイルごとに管理するということは上記で説明しましたが、 これをscss記法を用い各ファイルごとは下記のような構成になっています。

.one-block {
  &__element {
    &__modifier {
    }
  }
}

modifierをマルチクラスではなくシングルクラスで運用しています。

<div class="menu">
  <div class="menu__tab menu__tab--current"></div>
</div>

ではなく、

<div class="menu">
  <div class="menu__tab--current"></div>
</div>

とかけるようにしています。

  • これはhtmlテンプレート側で同じ記述を書ける
  • CSSが苦手な人でもクラスの修正を行いやすい

というメリットがある一方で、

  • CSSをDRYにできない
  • 生成したCSSが多くなってしまう

というデメリットがあります。

生成した際のCSSが多くなってしまうので、パフォーマンスの観点はファイルが非常に大きくなったときに改善するタスクとして、 今は技術的負債としてあえてかかえることにしています。

CSSをDRYにできないというデメリットは、開発効率が下がってしまう大きな課題です。 対策としてmixinを使います。 mixinは同一block内で使い回すことからはじめ、徐々に他のblockのクラスでも使うように移していくようにしました。

第一段階ではblock内でmixinを使いまわします。

.menu {
  @mixin menu {
  }
  @include menu;

  &__current {
    @include menu;
    color: #fff;
  }
}

第二段階ではglobalなmixinとして使いまわします。 globalに使い回す時はよりデザインの説明的な名前にし、 将来的にはデザインフレームワークとしての役割を果たすことを狙いたいところです。

この辺りはスタイルガイドとしてドキュメント化していきたいです。

@mixin menu-box {
}
.menu {
  @include menu-box;

  &__current {
    @include menu-box;
    color: #fff;
  }
}

scss-lintを積極的に使う

ともすると、しつこいくらいの指摘をしてくるscss-lintですが、 CSS設計初心者には非常に便利です。

その中でもPropertySortOrderという仕組みはmixinに切り出すの際にコピペ効率が上がる便利な機能です。 この機能のおかげで他のクラスとの重複を見つけ出し、mixinをさくっと作っていくことができます。

これもRubyMineの機能を使うと簡単にオートコレクトできます。 PropertySortOrder で順番を決めておくことで、gzip圧縮した際の圧縮効率もよくなります。

詳細度を単純にする

また、CSSの設計をする上で当たり前に重要になるのは詳細度の問題です。 詳細度の高い記述は避けるようにしましょう。

  1. htmlタグにcssを装飾しない
  2. idを使わない
  3. importantを使わない

これらもscss-lintによる指摘によって、地雷を踏まずに行えます。 私のプロジェクトではnormalize.cssやreset.cssが担当する層、以外で詳細度の上がるCSSの記述を行わないようにしています。

ちょっと後悔していること/これからひょっとしたら、やろうとしていること

<body class="<%= controller.controller_path >">

と書くことで、名前空間を分ける技法をrailsでは一般的に使われていると思いますが、 これと、シングルクラスのBEMを同時に採用すると、blockの数ほどファイルの数が増えなくて良かったのではないか? と少し感じます。 Partynoteはコントローラが十分に分割されているアプリなので、この手法でもかなりうまくいったかもしれないと感じています。また、railsのgeneratorの機能を使って、CSSを生成できるので、手離れも良いような気がします。

さて、明日のアドベンドカレンダーは?

計測さえすればいい。〜MySQLのorder by rand()を通して思ったこと〜 です!