小〜中規模サイトのフロントエンド・コーディング規約 CSS・JavaScript編

  • 262
    いいね
  • 0
    コメント

関係者が10名以下の小〜中規模案件の開発・保守が多い弊社のCSS・JavaScript規約(にしたい)です。
長くなってしまったコーディング規約もようやく最後です。

↓関連
環境構成編
HTML編

CSS、JavaScript は数年で書き方が変わってしまうので、
半年ごとに規約の見直しができると理想ですね。

小〜中規模サイトのフロントエンド・コーディング規約 CSS編

ディレクトリ構成

CSSに関するファイルの一般的な例を示します。

ルート
 ├ src ... 作業ディレクトリ
 │ ├ stylus
 │ │ ├ lib ... 外部ライブラリなど
 │ │ ├ sprite ... spritesmith などで生成したファイル
 │ │ ├ helpers ... 変数や関数
 │ │ ├ components / module  ... コンポーネント、モジュール
 │ │ └ page ... ページごとのレイアウト
 │ │
 │ └ styleguide ... style guide生成用ディレクトリ
 │
 └ release ... リリースディレクトリ
   └ css

記述ルール

項目 定義内容
文字コード UTF-8
インデント 半角スペース2つ
brace { } stylus でも省略しない。閉じは改行する
idセレクタ セレクタ優先順位の計算が面倒になるので id での指定は避ける
要素型(タイプ)セレクタ あとからの修正に弱いので要素型セレクタの指定は控える
ネストの深さ 可搬性が下がるので、ネストの深さは3階層程度に収める
数値 小数点以下は最初の0を省略できる。ただし、animation/transition プロパティでは省略しない
ショートハンド あとからの修正に弱いので不用意にショートハンドプロパティを使わない
特にbackground-size は含めない
line-height 単位はつけない(親要素の値が子要素に相対的に継承できないため)。(文字サイズ) / (一行の高さ) をプリプロセッサで算出させると読みやすい
z-index コンポーネントごとにプリプロセッサの変数で管理する
計算 相対的な値など、算出する値はプリプロセッサの function を使う。もしくは計算過程をコメントで残す
// 非推奨
#class div{
  margin: 10px auto auto 20px;
  padding-right: 4.6875%;
  padding-left: 4.6875%;
  z-index: 101;
  font-size: 0.9em;
  line-height: 18px;
  animation: anime .15s 0s ease-in}
// 推奨(scss記法)
$module_z_index = 100;

.class .child {
  margin-top: 10px;
  margin-left: 20px;
  padding-right: percentage(15px / 320px);
  padding-left: percentage(15px / 320px);
  z-index: $module_z_index + 1;
  font-size: .9em;
  line-height: (12 / 18); // 1.5
  animation: anime 0.15s 0s ease-in;
}

プロパティ記述ルール

見やすくするため、おおまかに下記のルールで記述してください。

selector {
  display : **;
  positon : **;
  width : **;
  height : **;
  margin : **;
  padding : **;
  border : **;
  box-sizing : **;
  z-index : **;
  // -- ↑要素の位置やかたちを算出するプロパティ -- 
  backgroud : **;
  color : **;
  font : **;
  // --  色や文字、コンテンツを調整するプロパティ --
  opacity : **;
  pointer : **;
  animate : **;
  transition : **;
  transform : **;
  // --  セレクタの状態を調整するプロパティ --
}

コメント

コメントは一定のルールに習って使い分けていれば大丈夫です。
下記は推奨です。エディターにスニペット登録して使います。

/* ==========================================================================
   CSS セクションコメントブロック
   footer や header などまとまったコンポーネントの見出しに使います
   ========================================================================== */

/* CSS サブ・セクションコメントブロック
   コンポーネントにサブセクションがある場合に使います
   ========================================================================== */

/* コメント */
// ==========================================================================
// scss, stylus セクションコメントブロック
// ==========================================================================

// scss, stylus サブ・セクションコメントブロック
// ==========================================================================

// コメント

既存ライブラリ・案件の共通リソースとの併用

既存ライブラリやクライアントさん支給の共通リソースを併用する場合は、意図しない上書きや競合を起こさないため下記に注意します。

  • 外部ライブラリは /lib や /vendor などまとまったディレクトリに配置する
  • セレクタの衝突が起きないように、併用する場合は案件特有の接頭辞を追加する
/* 非推奨 */
.button {}
/* 推奨 */
.myProject_button {}

JavaScript フック

あるクラス名で CSS 定義と JavaScript フックは同居しないようにしてください。
クラスで JavaScript のバインドを行う場合は、 js- で始まるバインド専用のクラス名をつけ、役割が異なることを明示してください。

<!-- 非推奨 -->
<div class="button">ボタン</div>

<style>
.button {
  display: inline-block;
}
</style>

<script>
let btn = document.querySelectorAll(".button");
</script>
<!-- 推奨 -->
<div class="button js-button">ボタン</div>

<script>
let btn = document.querySelectorAll(".js-button");
</script>
  1. CSS のクラス名が変わった際に Javascript にも変更を適用しなくてはいけない
  2. 同じスタイルを反映させるためにクラス名を使いまわした時、JavaScript イベントハンドラも一緒にくっついてきてコーディングの自由度が下がる

という欠点があるためです。

JavaScript から操作する場合は data 属性を使う、というルールのほうがしっかり分離できて良いですね。

<div data-month="12" id="december">12月</div>
let a = document.querySelector('[data-month="12"]');
console.log(a.id); //december

ツール

プリプロセッサ

見通しの良さや組み込み・記述の簡便さから stylus を推奨しています。

ポストプロセッサ

下記の処理はポストプロセッサで行ってください。

  • ベンダープリフィックスの追加
  • mediaquery ごとの並び替え
  • SourceMap の生成(リリース版は除く)
  • CSS lint
  • 圧縮(minify, gzip化)

現在は gulp の処理でこれらを行っていることが多いですが、今後は PostCSS にまとめていきます。

PostCSS

PostCSS の普及が安定的になってきたので、徐々に gulp-stylus + gulp 他のプラグインの構成から、poststylus を使った PostCSS を中心とした構成に変えていきます。

CSS設計手法

これまでは MCSS や ITCSS のような遡上できないレイヤー構造の設計で作ってくることが多かったのですが、

  • 明確な一定の記述ルールに従っている
  • コンポーネントの可搬性が高い
  • コンポーネントの親子構造・状態がクラス名から判断できる
  • 追加・修正・再編集・破棄の影響が最小限になるように考えられている

設計手法なら何でもよいと思っています。

ECSS のようにファイルが大きくなっても共通要素でまとめず、ページの機能単位で管理していく方法や、
vue.js や riot.js のようにコンポーネントごとに HTML, CSS, JavaScript をカプセル化する作り方もあるので
案件に適した設計手法を採用してください。

スタイルガイド

複数人でコーディングする場合はもちろん、社内でのUI調整にもスタイルガイドがあると便利です。
1.スタイルガイドは日本語のドキュメントがある、2.gulp で利用出来る、3.stylus に対応しているという理由で aigis を推奨します。

デザイン決定が

  • コーダーの意思が反映されないアジャイル的なもの
  • 進行が遅れている場合

は、無理に用意しなくてもかまいません。

そのほか

小〜中規模サイトのフロントエンド・コーディング規約 JavaScript編

ディレクトリ構成

JavaScriptに関するファイルの一般的な例を示します。

ルート
 ├ src ... 作業ディレクトリ
 │ └ js
 │   ├ lib ... 外部ライブラリ
 │   ├ component  ... コンポーネント化したテンプレート
 │   └ module  ... モジュール
 │     └ common ... サイト共通モジュール
 │
 ├ release ... リリースディレクトリ
 │ └ js
 └ test ... JSテストディレクトリ

記述ルール

項目 定義内容
文字コード UTF-8
インデント 半角スペース2つ
変数・関数名 予約語を使わない。
クラス名 1文字目は大文字にする
初期化 リテラルで行う
三項演算子 使っても良い
// 非推奨
var case = new Array(); // case は予約語
class animal {} // クラスの1文字目は大文字にする
// 推奨
var case_list = [];
class Animal {}

コメント

他人にも読みやすいように、適切なところにコメントを入れるようにして下さい。
クラスやメソッドの前には、必ずjsDoc の形式でコメントをつけるようにするとコードレビューがしやすくなります。

/**
 * 概要を記述。
 * 下記の引数、戻り値にはどんな型が入るか明らかにする
 * @param {string} arg 引数の説明
 * @return {string} 戻り値の説明
 */
function returnStrings(arg){
  return String(arg);
}

コメントのいれ方も程度が難しいのですが、下記を参考にしてみてください。

リーダブルコードまとめをざっと見るのもおすすめです:grin:

文法から意味のある規約

上記以外で JavaScript の仕様上、推奨される規約は下記のまとめを参考にしてください。

アセット管理、トランスパイラ、AltJS

読み込むライブラリが複数存在していたり、コンポーネント管理の開発が合理的だったら webpack を使用することを推奨しています。

webpack

複数のファイルから共通のモジュールを読み込む場合はCommonsChunkPluginで管理してください。

babel

IE8対応でなければ、babel を使い、ES2015 で記述してください。

AltJS

型を定義するため TypeScript の利用を推奨します。

JavaScript コード生成前後の処理

  • SorceMap の生成(リリース版は除く)
  • Lint を通す
  • 圧縮する(Uglify, gzip化。webpack プラグインでは処理が重いので確認とリリース版のみ実行されるようにする)

ライブラリ

案件でよく使っているもの

案件でよく使っているライブラリです。
必要に応じてカスタムビルドを行うなど、サイズを調整しつつ利用してください。

使用ライブラリ選定について

使用するライブラリはなるべく下記の項目を基準にして選定してください。

  • 広く動作確認が行われている
  • 多機能すぎない

テスト

  • 不安なところ
  • 複雑になってしまったところ

は、出力結果が確認できるテストをコードに残すようにしてください。
(自動化はできる範囲で実装していきましょう!)

「テストしやすい単位で機能を開発しているか」を意識することはとても大切です。

高頻度イベント処理を最適化する

スクロールやリサイズなど連続して発火するイベントで、DOM のレイアウトプロパティにアクセスするとブラウザの処理能力が落ちます。
そのため、下記の最適化を行ってください。

  1. イベント検知と描画ロジックを分ける書き方をする
  2. イベントの間引き(throttle, debounce)を行う

処理が遅くなる理由

layers.png

画像:ブラウザの仕組み: 最新ウェブブラウザの内部構造

JavaScript で DOM レイアウトプロパティにアクセスすると、
JavaScript インタプリタの処理が中断され、レンダリングエンジンにレイアウトの再計算を求めます。
レイアウト計算結果が戻ってから JavaScript の処理が再開されるので、
スクロールなどで高頻度に DOM にアクセスすると
レンダリングエンジン - JavaScript インタプリタでの処理が増え、
結果として描画(レイアウト処理後に行われる)が遅くなります。

throttle

throttle の実装は requestAnimationFrameをつかって実装できます。
requestAnimationFrame が使えないブラウザでは setTimeout やライブラリで実装してください。

intersection observer

今時点(2017年3月)では chrome しか実装されていませんが、intersection observerという web API があります。
スクロールに連動した DOM 要素の変更(パララックスとか lazyload )がかなり効率化できるようになります。