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

2021/3/16
初めて記事を書いてから３年以上経過してしまったので、
内容を見直ししました。

---

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

↓関連
[環境構成編](http://qiita.com/d2cs-kimura/items/8b8a5f60556580bc4934)
[HTML編](http://qiita.com/d2cs-kimura/items/c9cda62c4fa1ceeadfde)

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

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

## ディレクトリ構成
CSSに関するファイルの一般的な例を示します。

```
ルート
　├ src ... 作業ディレクトリ
　│　├ scss
　│　│　├ lib ... 外部ライブラリなど
　│　│　├ sprite ... spritesmith などで生成したファイル
　│　│　├ foundation (base) ... 変数や mixin, function 定義
　│　│　├ components (module)  ... コンポーネント、モジュール
　│　│　└ page ... ページごとのレイアウト
　│　│
　│　└ styleguide ... style guide生成用ディレクトリ
　│
　├ htdocs ... リリースディレクトリ
　│　└ css
　├ config ... 設定ディレクトリ
　│　└ postcss.config.js
　│
　└ .stylelintrc.json ... stylelint 設定
```

## 記述ルール

| 項目 | 定義内容 |
|:----|:----|
| 文字コード | UTF-8 |
| インデント | 半角スペース2つ |
| idセレクタ | セレクタ優先順位の計算が面倒になるので id での指定は避ける |
| 要素型（タイプ）セレクタ | あとからの修正に弱いので要素型セレクタの指定は控える |
| ネストの深さ | 可搬性が下がるので、ネストの深さは3階層程度に収める |
| 数値 | 小数点以下は最初の0を省略できる。ただし、animation/transition プロパティでは省略しない |
| ショートハンド | あとからの修正に弱いので不用意にショートハンドプロパティを使わない<br>特にbackground-size は含めない |
| line-height | 単位はつけない（親要素の値が子要素に相対的に継承できないため） |
| z-index | コンポーネントごとにプリプロセッサの変数で管理する |
| 計算 | 相対的な値など、算出する値はプリプロセッサの function を使う。もしくは計算過程をコメントで残す |

```scss
// 非推奨
#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
@use "sass:math";

// 推奨（scss記法）
$module_z_index = 100;

.class .child {
  margin-top: 10px;
  margin-left: 20px;
  padding-right: math.percentage(15px / 320px);
  padding-left: math.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;
}

``` 

### stylelint
記述ミスを防ぐため、stylelint は行うようにしてください。
ルールは [stylelint-config-recommended-scss](https://github.com/kristerkari/stylelint-config-recommended-scss) で十分です。
プロパティの記述順にルールがあると読みやすい & 圧縮効率が良いので
[stylelint-order](https://github.com/hudochenkov/stylelint-order) の設定があると良いです。
順番は好みで良いのですが、[stylelint-config-rational-order/plugin](https://github.com/constverum/stylelint-config-rational-order) の順で
並んでいると直感的で見やすいです。


### コメント

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

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

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

/* コメント */
```

```scss
// ==========================================================================
// scss セクションコメントブロック
// ==========================================================================

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

// コメント
```

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

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

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

```css
/* 非推奨 */
.button {}
```

```css
/* 推奨 */
.myProject_button {}
```

## ツール

### プリプロセッサ

汎用性の高さから sass（Dart sass） を推奨しています。
```@use``` に早めに慣れておいてください。

### ポストプロセッサ

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

- ベンダープリフィックスの追加
- 使っていない CSS のパージ
- 圧縮(minify, gzip化)

## CSS設計手法

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

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

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

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


## スタイルガイド

複数人でコーディングする場合はもちろん、社内でのUI調整にもスタイルガイドがあると便利です。

デザイン決定が

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

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

１ページに基本的なコンポーネントが網羅されているものでもいいのですが、
複数人開発では [storybook](https://storybook.js.org/) が便利でした。

## そのほか

- line-height の振る舞いは理解する<br>（[理解しておきたい、CSSによるインラインレイアウトの仕組み](https://html5experts.jp/takazudo/13339/)）
- GPUアクセラレーション使用時に合成レイヤーの巻き込みが発生し、処理が遅くなることを知っておく<br>（[GPUアクセラレーションとposition: relativeによるレイヤー生成について](https://havelog.ayumusato.com/develop/performance/e556-compositing_border_and_layer.html)）
- [Can I Use](http://caniuse.com/)で利用化になっていても、稀に意図通りに動かない場合がある。<br>（Xperia Z SO-02E Android4.4.2で flexbox が利用できない）<br>そのため、必ず実機確認をする

### Tailwind CSS

プロトタイプ開発にとても便利な Tailwind CSS ですが、
個人的には使わなくていいかな、と思います。

base, components, utilities を読み込むととにかく コンパイル時間が長い。。
使うユーティリティクラスも限定的なので恩恵が少ないと感じました。


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

## ディレクトリ構成
JavaScriptに関するファイルの一般的な例を示します。
TypeScript での作業を推奨しています。

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

```

## 記述ルール

| 項目 | 定義内容 |
|:----|:----|
| 文字コード | UTF-8 |
| インデント | 半角スペース2つ |
| 変数・関数名 | 予約語を使わない。camelCase を推奨 |
| クラス名 | １文字目は大文字にする |
| 初期化 | リテラルで行う |
| 三項演算子 | 使っても良い |

```js
// 非推奨
var case = new Array(); // case は予約語
class animal {} // クラスの1文字目は大文字にする
```

```js
// 推奨
const caseList: caseType[] = [];
class Animal {}
```

### コメント

他人にも読みやすいように、適切なところにコメントを入れるようにして下さい。
TypeScript で書くことで引数や戻り値の内容把握が楽になったのですが、
クラスやメソッドの前には jsDoc の形式でコメントをつけるようにすると
コードレビューがしやすくなります。

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

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

> - [リーダブルコード](https://www.oreilly.co.jp/books/9784873115658/)
> - [JavaScriptのコメントは不要か？](http://postd.cc/are-javascript-comments-useless/)

[リーダブルコードまとめ](http://qiita.com/fkrw/items/7646563a2b238fbcff9a)をざっと見るのもおすすめです:grin:


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

> - [Google JavaScript スタイルガイド - 日本語訳](https://www38.atwiki.jp/aias-jsstyleguide2/pages/1.html)
> - [Airbnb JavaScript スタイルガイド](http://mitsuruog.github.io/javascript-style-guide/)

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

### webpack

複数の出力されるファイルから共通のモジュールを読み込む場合は```optimization.splitChunks```で管理してください。

### babel

最新の core-js を使ってください。
webpack の babel-loader で TypeScript のトランスパイルまで行ってしまうと
設定が楽です。

### AltJS

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


### JavaScript コード生成前後の処理

- SorceMap の生成（リリース版は除く）
- Lint を通す
- 圧縮する(Uglify, gzip化。production ビルドだけでいい)


## lint、型チェック

### lint
eslint は必ず行ってください。
webpack で保存のたびに eslint が実行されるようにすると便利です。
eslint のルールは [airbnb-base](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb-base) が入っていると、
非効率な書き方を指摘してくれるので好きです。
.eslintrc の extend の記述の順番で [@typescript-eslint/recommended](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin) と併用ができます。

### 型チェック

Babel では TypeScript の型チェックができないので、
別途行う必要があります。

[husky](https://github.com/typicode/husky) を使って git のコミット前に型チェックを行うように設定すると漏れがなくなります。


## ライブラリ

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

- [Vue.js](https://jp.vuejs.org/)
- [swiper.js](http://idangero.us/swiper/)

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

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

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


## テスト

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

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

その前に、**「テストしやすい単位で機能を開発しているか」**を意識することは
とても大切だと思っています。
クリーンアーキテクチャの考え方がとても参考になるのですが、
言葉が難しいのと従来の HTML と JavaScript だとレイヤーがずれてしまうので
フロントエンドでは下記のポイントを抑えてもらえると良いと思います。

- DOM に関わる処理とデータの処理部分はできるだけ分ける
- 関数は機能を絞り、役割を明確にする
- データの保存、取得は独立した関数にする

テスティングフレームワークは [Jest](https://jestjs.io/ja/) が使いやすかったです。

## JavaScript Tips

JavaScript を書くうえで、こうしたほうが良い、というのをまとめました。

### セレクターの取得

JavaScript の queryselector などでセレクターを取得する時、
できるだけ CSS のクラス名を避けたほうが良いです。

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

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

<script>
const btn = document.querySelectorAll('.button');
</script>
```

```html
<!-- 推奨 -->
<button class="button" data-role="submit">ボタン</button>

<script>
const btn = document.querySelectorAll('[data-role="submit"]');
</script>
```

1. CSS のクラス名を変えた場合、Javascript にも変更を適用しなくてはいけない
2. 同じスタイルを反映させるためにクラス名を使いまわした時、JavaScript イベントハンドラも一緒にくっついてきてコーディングの自由度が下がる

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

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

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

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

> - [Webフロントエンドに従事するお前らはいい加減高頻度イベントとレイアウトとスタイリングの付き合い方を考えろ](http://qiita.com/damele0n/items/f4050649de023a948178)

#### 処理が遅くなる理由

![layers.png](https://qiita-image-store.s3.amazonaws.com/0/131281/02c84cbe-f5f2-f28a-17c2-2fbcb840867a.png)
> 画像：[ブラウザの仕組み: 最新ウェブブラウザの内部構造](https://www.html5rocks.com/ja/tutorials/internals/howbrowserswork/#The_main_flow)

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

### throttle, debounce

throttle は連続発生したイベントの、間引きになります。
scroll イベント向きです。

debounce は指定時間内に起こったイベントの最後のタイミングで処理されます。
resize や touchmove で使われます。

requestAnimationFrame などを使って自前で実装しても良いのですが、
[throttle-debounce](https://www.npmjs.com/package/throttle-debounce) という npm パッケージが使いやすいです。

###  intersection observer

スクロールに連動した DOM 要素の変更（パララックスとか lazyload ）が
かなり効率化できるようになります。
IE11 には非対応ですが、モダンブラウザで広く使えるようになりました。
（でも iOS safari 12.1 以前が未対応は注意）