長期かつ修正頻度の高いPJでのCSSメンテ

http://benfrain.com/enduring-css-writing-style-sheets-rapidly-changing-long-lived-projects/

1 comment | 2 points | by WazanovaNews 約3年前 edited


Jshiike 約3年前 edited | ▲upvoteする | link

長期的な大規模プロジェクト、かつ修正頻度が高い場合は、DRYよりはメンテ性を最優先にしたCSSを書くべきという、Ben Frainの方法論です。長文ですが、よくまとまってると思います。

1) テクノロジーとツール

プレプロセッサ

  • 長期のプロジェクトにおいて重要なのは、テクノロジーではなく、何ができて、どう進めるかというアプローチ。
  • Sass / LESS / Stylus / Myth などどれでも、しっかり書かれていれば、必要なときにいつでも統合はできる。プレプロセッサは個人の好みでなくて、プロジェクト全体のメリットで決めるべき。また、コンパイル後のスタイルシートではなく、どうスタイルシートを書くかという視点で選ぶべきだと思っている。
  • プリプロセッサの必須条件はそれほど厳しくない。
    • CSS variables (カスタムプロパティ) : 色の選択やgridを決めるconstantの指定などで、エラーを軽減してくれる。
    • スタイルにおけるフラグメントとパーシャル : 機能ごとのブランチやテンプレと、対の関係をつくるとことができる。
    • 基本的な計算機能 : マジックナンバー?に頼らないで済むようにする。
    • カラー操作 : 前述のvariablesで整合性のとれた操作が可能。

ビルドシステム

  • Grunt / Gulp / Brocolliなど、人手に頼らず、タスクを自動化し正確にこなしてくれるビルドツールを利用できるところは利用する。ビルドシステムのこなしてくれるタスクの中には下記のようなものがある。
    • lint : 例えば、Sassだとscss-lintが、コードを整形し、使われてないコードがデプロイされないようにしてくれる。
    • css-comb : 宣言の順やスペースなど、lintツールに向かないCSS表現の細かい整形をしてくれる。
    • Autoprefixer : スピーディで正確なベンダープレフィックスを付加 & 作成中のスタイルシートでは表示されないようにしてくれる。

2) CSSの記法

  • 極力抽象化し、サイズの小さいDRYなコードを書くことがもてはやされているが、長期的かつ修正頻度の高いCSSプロジェクトのゴールはあくまでメンテ性。再利用もしくは拡張した曖昧な抽象化で1KB節約するよりも、6ヶ月後に9KBのSassパーシャルを自信をもって削除できるほうが価値が大きい。
  • 既存の抽象化されたコンポーネントを使って新しいものをつくる作業は時間がかかる。純粋に汎用的に使い回せるものはよいが、ちょっと違う似たようなものは、イチからつくったほうが早い場合が多い。
  • もちろん再利用、抽象化自体を否定するわけではない。デザインの違いは必然となる理由があるべきで、誰かがそうしたいからという理由で既存のスタイルを変えるべきではない。
  • あらゆる意味で疎結合しているコンポーネントで構成される大きなコードベースのほうが、本質的に依存してしまっている小さなコードベースよりもよい。

FUN

  • F : フラットなセレクタの階層(Flat hierarchy of selectors)
  • U : ユーティリティのスタイル(Utility styles)
  • N : コンポーネントに名前空間を設定(Name-spaced components)

フラットなセレクタの階層

  • FUNの記法の最初のポイントは、単一のフラットな階層のclassベースのセレクタ。
    • セレクタは、特別な理由がなければ、classしか使わない。
    • 本当に事情がない限りは、セレクタをネストしない。
    • スタイルのフックとしてIDは使わない。
  • ちなみに、セレクタの型の選択は、webサイト/アプリのスピードという意味では実質的には違いはないと考えている。
  • プレプロセッサが、ネストされたスタイルシートをフラット階層にコンパイルしてくれるケースはある。個人的には、テンプレ/マークアップのHTML classとスタイルシートのCSSセレクタは1対1の関係が好み。正しい答えはないところなので、自身のチームで方針を統一するとよい。

ユーティリティのスタイル

  • ユーティリティのスタイルは、SRP (Single Responsibility Principle) に従うべき。w100は、width: 100%;であり、Tblは、display: table; table-layout: fixed;とした場合、他のセレクタや特定のストラクチャに依存してはいけない。
  • テキストのスタイルを抽象化したユーティリティスタイルが役に立つケースがある。例えば、txt-Cart_Aは、テキストという名前空間が設定され、ショッピングカートとして利用され、A variantである。自分の場合、フォントファミリーには使わないが、フォントサイズや色、ときにはフォントのウェイトを指定することはある。
  • ユーティリティclassは、需要を確信できるときのみ用意すること。例えば、全てのviewportサイズで幅100%が必要かどうかわからない場合、要素にw100は使わない。
  • ユーティリティの頭に ‘u’ をつける人がいる。例えば、’u-100’とか。自分は、小文字のuで両側にハイフンがなければユーティリティとしている。
  • 好きなだけユーティリティをつくってよいが、絶対に変更しないこと。

コンポーネントに名前空間を設定

  • ビジュアルコンポーネントに名前空間を設定することは、疎結合化を実現できる。他のコンポーネントの名前との衝突を避けることで、CSSのまとまりを別の環境(例えば、プロトタイプから本番)に移しやすくなる。
  • 自分の好みは 2, 3文字のシンプルな名前空間。ショッピングカートであれば、.sc-、次のバージョンは、.sc2-。コンポーネントを明示でき、わかりやすい名称であればよい。例えば、カートのラッパーは、.sc-Wrapper、削除ボタンがあれば、.sc-RemoveItemとするなど。
  • しかし、コードの重複の可能性は増す。スタイルを決めたコンポーネントが抽象化されて、別のコンポーネントで利用されることはありうる。しかし、概ね、コードの移行がしやすく、削除時の悪影響も少ないと言える。冗長化のコストを払って、コンポーネントの疎結合化を実現している。

コードの肥大化につながる?

  • 確かにコードは増えるが、自分の経験からすると、コードの肥大化は、重複したスタイルよりも、よくわからないので怖くて削除できない状況に起因することが多い。たくさんのメンバが関わっていると、削除するリスクを取るよりも、既存のCSSに書き足すほうを選ぶものだ。
  • まだでてきたばかりだが、UnCSSには注目している。また一つツールに任せることができるタスクを増やせるかもしれない。
  • 名前空間を設定したコンポーネントスタイルは削除しやすいが、特にパーシャルで定義された機能(例えば、バージョン管理されたブランチ)もしくはコンポーネント、例えば、_sc2-shopping-cart.scssだとより簡単に判断できる。
  • バージョン管理してフォルダを整理するのは必須。ショッピングカートのバージョン2のCSSを書き終えれば、自信をもってバージョン1のJS/CSS/HTMLは削除できる。

3) プロジェクト管理

ファイルの保管

  • my-projectというディレクトリの傘下に、html / js / sass / css という各フォルダを設けて管理することがよくある。しかし、ファイルの命名ルールを工夫したとしても、Sassのパーシャルが80個になったり、htmlのテンプレスタブが50個を超えたりすると、手に負えなくなる。こうなってくると検索性が、重要。最終的には、下記のようにビジュアルコンポーネントごとにグループ化する方がよくなる。
1.   shopping-cart-template/
2.   - shopping-cart.html
3.   - shopping-cart.css
4.   - shopping-cart.js
5.
6.   callouts-template/
7.   - callouts.html
8.   - callouts.js
9.   - callouts.css
10.
11.  products-template/
12.  - products.html
13.  - products.js
14.  - products.css
  • 各コンポーネントのコードが物理的に疎の関係に整理できる。長期プロジェクトの中、機能の変更や廃止が起きても、関連する更新/削除が簡単にできる。本当の意味でglobalなユーティリティCSSは例外だが、コンポーネントを表現する全てのコードがパーシャルとして、HTML /JSのコンポーネントと同じディレクトリで管理できる。
  • 全ての関連するファイルを一つのディレクトリにまとめるのは実務的にはできないこともあるが、例えば、_partials/_components/_shopping-cartv2.scssのように、コンポーネントに従ってパーシャルの名称を決めることのメリットは享受できる。

CSSの配信についての考察

  • Ilya Grigorikが提唱した、ファーストビューに関連するCSSはHTMLのヘッドにインラインで入れるという方式は、人気をよんだが、自分は少し違和感を感じた。最初のページの読込みは当然早くなるが、その後のアクセススピードに影響がでる。従来の手法では、スタイルシートがダウンロードされ、キャッシュされれば、ファーストビューのCSSをその後のページの読込みで再度サーバから取得する必要はない。
  • 別の方法としては、CSSをJavaScriptとともに後ほど読込むという手法だが、よほどのメリットがない限りは、JavaScriptに頼りたくはない。
  • 自分はもっとコンサバなアプローチを取る。
    • 大きなアプリやwebプロジェクトはエリアごとに必要とするCSSが変わってくる可能性が高い。このシナリオでは、単一のstyle.cssファイルに全てを含むのは意味がない。例えば全体を80KBとした際、サイトの他のエリアでそのうち60KBを必要とするのは、20%だけかもしれない。
    • サイトの各エリアは独自のスタイルシートをもつべき。極力コンポーネント化すると、それぞれのスタイルシートを扱いやすくなる。
    • styles.cssが全てをカバーするわけではないとすると、ブラウザが多くのファイルをキャッシュできればできるほど、ユーザがアクセスする各ページにおけるCSSのフットプリントは小さくなる。つまり、最初の読込み時間が短縮できる。

4) CSSの記法での落とし穴

チームにフィットした命名ルールを定める

  • 正しい命名法というのはないが、命名ルールを設けるのは常に正しい。(参考: BEM)自分たちにフィットする命名規則を見つけるために時間を使うことは、将来にわかって、多いにおつりがくるメリットがある。
  • 自分用にまとめたルールでは、[namespace]-[ComponentPart]_[Variant]-[optional-adjuster]としている。例えば、sc-Wrappersc-InnerItem_Oddなど。

完璧な抽象化の恐怖

  • かつて、”Position Structure Them” を略して、”Pst!” というプロジェクトをやった際、セマンティックなclass名ではなく、要素を、ポジションclass / ストラクチャclass / オプションテーマclassでスタイル化した。<div class=“p1 s3 t4”>Content</div> -見た目がすっきりして、規則はロジカルではあるが、まったく実用的ではなかった。そもそも見分けがつかない。
  • s3をどのviewportサイズでがどうするべきか?別のストラクチャの中ならどうするか?という問題もでる。
  • これが学びになって、以降は意図がわかる命名規則にした。.WarnUserとか.sc-CurrencyDropDownなど。

作成中のスタイルシートでベンダープレフィックスを指定しないこと

  • 自分が作成中のスタイルシートでベンダープレフィックスを指定しないこと。必要とされるブラウザのサポートは将来変わっていくので、無駄な作業になる。自動的なプレフィックスはツールができるし、人間よりもうまくやる。半日時間をかけてもツールのセットをしたほうがよい。

ライブラリmixinは避ける

  • clearfixやバックグランドgradientなど、標準のCSSタスクをこなすのに、あらかじめ用意されたmixinを提供してくれるプリプロセッサライブラリがよくあるが、そのmixinを使ってはいけない。W3Cに準拠するCSSコードでのみ書くべき。プリプロセッサを変えたり、コードを新しいプロジェクトにコピーする際に、コードのポータビリティがあがるし、依存関係を減らすことができる。更なるメリットとして、Sassだとコンパイル時間を大幅に短縮できる。

CSSのコメントにマークアップのサンプルを載せない

  • CSSのコメントにマークアップのサンプルは載せない。コメントを書くのはよいが、スタイルシートにマークアップのサンプルを残すと、情報が古くなり、混乱する元になる。替わりに、スタイルシートとペアになったテンプレスタブが全てのマークアップを提供すべき。テンプレは必要であれば、必ずアップデートされるので、意味のある参照情報になる。

過度に凝った抽象化は避ける

  • 理解不能なコードは書かない。冗長性や繰り返しを防いでくれるという意味で、mixinやロジカルなloopが好都合な場合はあるが、シンプルなコードを必要以上に複雑にしない。Sassで著名なHugo Giraudelでさえ、ラインは引いている
  • 例えばSassでは、下記のようなwidthスタイルのユーティリティテーブルをつくるととができるが、複雑さという意味では自分としては限界。
$widths: 5 10 20 30 40 50 60 70 80 90 100;
%Tbl-base {
    display: table;
    float: left;
    vertical=align: middle;
}
@each $width in $widths {
    .tbl#{$width} {
        @extend %Tbl-base;
        width: $width * 1%;
    }
}
  • 一方、ボーダースタイル / バックグランドカラー / テキストサイズを定義するためだけに必要とする引数のあるボタンを用意するmixinは、不必要に複雑。

@extendに要注意

  • Sassにおいて、%Placeholderのようなプレースホルダー以外は@extendしないこと。また、プレースホルダにネストはしない。思いがけないCSSができてしまうことが多い。

本番にアップしたCSSを確認すること

  • たまにはコンパイルされたCSSファイルを開けて、手動で実行し、気になることを検知してくれる自分で用意したCSS Lintをかけてみること。大体、思いもしないコードが入っていることが多い。本番のコードをチェックしないと、質の悪いコードを残してしまうことになる。

#css

Back