--- Title: 'CSS (Sass) のコーディングルール #CSS #Sass' Keywords: - CSS - Sass Author: zk33 Web: 'https://mimemo.io/m/qOX6eWonbowPDQ8' --- 私的(あるいはテンマド社の)CSS/SCSSコーディングについてのルールまとめです。 自分自身の整理/備忘録と、自分の書いたコードを引き継ぐなどしていじることになった人に見せる用。随時更新。 ------ 最終更新:2019/09/25 (→ [変更内容](https://mimemo.io/m/qOX6eWonbowPDQ8/version/25) ) ------ # ルール設計の指針 * 覚えるべきことは必要最小限に * メンテナンス性の高さを最重要視 * 開発時に「迷う」「考える」部分を極力減らして余計な事を考えずに済むように # 前提 ## Sassを使う 素のCSSを使うような苦行は避けます。 ほとんど好みの問題ですが、Lessは記法がちょっとよくないので、Sassを使います。 処理速度やツールセットの関係で、本家RubyのSassではなく、[node-sass](https://github.com/sass/node-sass)のほうを、大抵は[gulp-sass](https://github.com/dlmanning/gulp-sass)で走らせて使います。 ※そろそろSassを捨ててPostCSSのみで済ませるのも現実的になってきているので、プロジェクト参加者のCSS知識/経験値によってはそちらの方向もアリです。 ## SASS記法ではなく、SCSSのほうを使う Sassを使う場合、SASSの記法(HAMLっぽいやつ)は使わず、SCSS(よりCSSっぽい記法)のほうを使います。 学習コストが低いことと、JSライブラリ付属のCSSやネットで公開されているコードなどを取り込みやすいからです。 ## Autoprefixerなどのツールを活用 [Autoprefixer](https://github.com/postcss/autoprefixer)など[PostCSS](http://postcss.org/)方面のツールを活用して、`.scss`ファイルにはできる限り最新仕様ベースのCSSを、ベンダープレフィックスなしで書くようにします。 # コーディングルールと命名規則 ## クラスは役割/意味/構造を表すように命名する クラス名は、**ページ上での役割や意味、構造を表す**ように命名します。 たとえばナビゲーションなら`.nav`、ヘッダなら`.header`、強調したい箇所には`.strong`など、ページ上でその要素が**どういう役割なのか**(どういう見た目なのかではなく)をクラス名で表すようにします。 文字を赤くしたいところに`.text-red`、太字にしたいから`.text-bold`、6カラム分の領域だから`.col-6`など、**見た目を表すクラス名はNG**です。 ```HTML:BAD

Red Large Text

``` ```HTML:GOOD

Awesome title

``` ページの見た目やレイアウトは、デザインの都合で変更することがよくあります。 見た目を表すクラス名をHTMLに書いてしまうと、デザイン変更の時に必ずHTMLの変更が必要になり、規模の大きなサイトになればなるほどデザイン変更の辛さが増していきます。 HTML側には見た目に関わる要素を入れず、見た目に関わることはできる限りCSS側で行うようにしておくほうが、デザインの変更に強く、レスポンシブ対応に向いた、メンテナンス性の高いHTMLになります。 また、「役割」を表すクラス名をつけておくと、HTMLの各部分がページ上でどういう役割で、どういう意図で書かれたものなのか一目でわかりやすくなり、実際にレンダリングされたWebページとの対照もさせやすくなるので、メンテナンスしやすくなります。 ## すべてクラスに対してスタイル指定し、IDで指定はしない ```scss:BAD #main { color:red; } #main ul{ color:green; } ``` ```scss:GOOD .main{ color:red; } .main-list{ color:green; } ``` ID指定でスタイルを書くことはしません。全てクラスで指定します。 IDは詳細度(selector's specificity)が高すぎて、他の場所でスタイルを上書きしようとした時`!important`を多発しなくてはいけなくなったり、あれこれと障害になるためです。 ## 初期化/リセットする部分以外で、タグに対してスタイル指定をしない ```scss:BAD .some-class ul{ color:red; } ul.some-class li{ color:red; } ``` ```scss:GOOD .some-class-list{ color:red; } .some-class-list-item{ color:red; } ``` リセット/初期化する部分を除いて、タグ名で、あるいはタグ名を含めたスタイル指定は一切せず、面倒でも全部クラスを割り振り、クラスに対していスタイル指定をします。 タグの構造は、制作していく上の都合やJSとの兼ね合いで変えることがよくあるので、その際に書き換える手間を減らし、HTMLの構造とCSSによるスタイリングを極力分離しておくためです。 例外として、たとえばMarkdownパーサが出力するHTMLなど、クラスの割り当てられない部分で、かつタグにスタイルを指定するのが妥当な部分は除きます。 ## クラス名はハイフン区切りで全て小文字 ```scss:BAD .snakecase_class{ color:red; } .camelCaseClass{ color:red; } .Has-Capital-CLASS{ color:red; } ``` ```scss:GOOD .good-class-definition{ color:red; } ``` クラス名は小文字の英数字+ハイフン区切りを基本とします。 シフトキー押さなくていいので楽ですし、大文字小文字のタイプミスでスタイルが当たらなくてうんうん悩む事も減るからです。 例外として、Angularアプリを書いていてdirective名とCSSの接頭辞を合わせたい、CSS増えすぎて小文字だけだとつらい、などの理由がある時に、接頭辞部分(一番先頭の部分)に限り、camelCaseを使ったりするのはアリです。 ## クラス名の先頭部分と、それが定義されているファイル名を一致させる **【重要】** クラス名の先頭部分(最初のハイフンが登場する前の部分)と、それが定義されているファイル名が必ず一致するようにします。 たとえば、`.mypage-nav`というクラスのスタイル指定は、`_mypage.scss`に書くようにします。 ```css:BAD /* _header.scss */ .my-awesome-header{ ... } .alternative-header{ ... } ``` ```css:GOOD /* _header.scss */ .header{ ... } .header-logo{ ... } .header-nav{ ... } ``` もし、一つのファイルにクラス定義が多くなってきた場合は、ファイルを分けてもいいのですが、その場合も「クラスの先頭部分とファイル名が一致するようにする」というルールは維持します。 例えば`.main-xxxx`というクラスが非常に多くなって`_main.scss`が肥大化し、メンテしづらくなってきたとして、`.main-content-`で始まるものを別ファイルに切り出したら、そのファイルは`_main-content.scss`にします。 こうすることで、クラス名を見た瞬間にそれがどのファイルに書かれているのかがわかるようになり、開発効率が上がります。 同時に他のファイルに書かれたクラスと同じクラスをうっかり指定して上書きしてしまうような事故も防げます。 ※ 例外として、状態などを表す`.mode-`で始まるクラスだけ、ファイル名と関係なく使うことができます。ただし、`.mode-`はファイル名に一致するほうのクラスにつなげる(?)形でしか定義してはいけません。 ```css:BAD /* _my.scss */ .my-btn .mode-disabled{ ... } .mode-disabled{ ... } ``` ```css:GOOD /* _my.scss */ .my-btn.mode-disabled{ ... } .my-btn{ &.mode-disabled{ ... } } ``` ## グローバルなクラスは必ず`.g-`で始める サイト全体で使うようなスタイル(たとえばボタンやform要素)は、全て`.g-`で始まるクラスを使ってスタイル指定します。で、当然、`_g.scss`に書きます。 ```css:例 /* サイト全体共通で使うボタンのスタイル */ .g-btn{ background:$g_color_brand; } /* サイト全体共通で使うinput type="text" の要素のスタイル */ .g-input-text{ font-size:1rem; } ``` `.g-`が頭についたクラスはサイト全体で使われているということになるので、スタイルを変更する時には細心の注意が必要です。それを明示するためにこのようなルールにしています。 ### ※グローバルなクラス(`.g-`のクラス)を使うのはどういう時か? `.g-`は「編集に注意の要るもの」であることを知らせるためにあるルールなので、闇雲に`.g-`をつけては**いけません** たとえば、サイト上のサイトの共通のヘッダであれば、それは確かにサイトの色々なページで使われていますので「`.g-`」とすべきかもと思うところですが、それは「`.header-`」として「_header.scss」に書かれるべきです。(そのほうが管理上もわかりやすいです) `.g-`の基本的な指針としては * 単純にほぼ同じマークアップで何度も使いまわすだけのもの(たとえばサイトの共通ヘッダやコンポーネント的なもの)には`.g-`はつけません。その要素をよく表す名前(`.header-`など)をつけましょう。(そしてHTML側もマクロとかパーシャルに切り出す、継承を活用するなどしてDRYにしておきましょう) * **WebサイトのテンプレートをできるだけDRYに書いて、それでも複数の箇所で(さらにそれぞれ微妙にスタイルが違ったりしてスタイルを上書きするような形で)使いまわすことになった**ものに`.g-`のクラスを割り当てます。 * その他、例えば`_header.scss`上で`_form.scss`に書いてある`.form-`で始まるクラスのスタイルを上書きしたい、みたいな場面に出くわした場合、↓のネストのルールに触れるので、その上書きしたいクラスは`.g-`に移すべきです。 ※DRY=Don't repeat yourself。要するに同じコードをコピペしたりして二度以上書かないようにする、ということ。何かしらのよいテンプレートエンジンを活用して実現しましょう ## クラスのネストはしない。必要な場合に限りに1階層のネストまではOK クラスのネストは基本的には**しません**。 既存のクラスを上書きするような形で新しい定義をしたいような、必要な場合に限り1階層のネストが許されます。 ```css:BAD .my-parent .my-child .my-element{ ... } /* ↓ネームスペースとかコンポーネント的な発想でこうするのも害が多いのでダメです */ .my-parent{ .my-child{ } .my-child2{ } } ``` ```css:GOOD .my-parent{ ... } .my-child{ ... } .my-parent-altenate{ .my-child{ ... } } ``` 同時に、`.g-`ではじまるグローバルなクラス以外は、別ファイル上で定義されているクラスをネストして上書きすることは避けます。 ```css:BAD /* _my.scss で… */ .my-list{ .user-list-item{ color:red; } } ``` 基本的にやっていいのは ```css .my-module{ .g-btn{ /* g-で始まるグローバルなクラスの定義を上書き */ } } ``` といった感じで、`.g-`で始まるグローバルなクラスのスタイルを、グローバルじゃないクラスの中で上書きしてカスタムするような場合と、 ```css .my-module{ ... } .my-module-alt{ ... } .my-module-element{ ... .my-module-alt &{ ... } } ``` といった感じで、同じファイル内に定義された既存のクラス上書きしてカスタムするためにネストさせる場合のみで、それ以外のネストはできる限り避けます。 せっかくSassなのにネストという便利機能を使わないのももったいない気はしますが、クラスをネストさせて多く重ねると詳細度が上がり、他の部分でそれを上書きするスタイルを定義したい時に、`!important`の多用や、無駄に詳細度の高い記述を要求されるようになり、長くメンテナンスを続けると地獄のようなCSSになりますので避けたほうがよいです。 ## 同一ファイル内でのネスト時は、スタイルを当てるクラスが親になるようにする クラスをネストする場合、どちらを親にするかで二通りの書き方ができますが、基本的に`&`を使って**スタイルを当てようとしているクラスのほうが親になる**形で書きます。 ```css:BAD .nav-list{ ... .nav-list-item{ ... } &.mode-alt .nav-list-item{ ... } } .nav-list-item{ ... } ``` ```css:GOOD .nav-list{ ... } .nav-list-item{ ... .nav-list &{ ... } .nav-list.mode-alt &{ ... } } ``` こうすることで、↑の例でいうと`.nav-list-item`というクラスに関わるスタイル定義を全て一箇所に集めることができ、CSSファイル内をあちこち探し回らなくて済むようになります。 ## クラス名を楽に書く方法としての`&`は使わない BEMのサンプルなどで、クラス名の補完で`&`を使う例がありますが、この使い方は禁止です。 ```css:BAD .header{ &-logo{ ... } &-nav{ ... } } ``` ```css:GOOD .header{ ... } .header-logo{ ... } .header-nav{ ... } ``` * クラス名で検索できなくなるため、そのクラスの書かれた場所を探すのが面倒になる * CSSが長くなってくると、何のクラスにスタイル当ててるのか分からなくなる など、メンテナンスしていく上でつらいCSSになりやすいからです。 ## `@keyframe`のアニメーション名の先頭部分などもファイル名に合わせる アニメーション名などに限らず、とにかく名前を付ける必要のあるものは、名前の先頭部分とファイル名が一致するようにします。 # JavaScriptとの連携 ## JavaScriptから使うクラスは「.x-」で始め、CSS側でそのクラスは絶対に使わない JavaScriptで要素の取得やDOM操作のために使うclassは、全て「.x-」で始めるようにし、この「.x-」で始まるクラスに対してはCSSでスタイル指定をしては**いけません**。 当然、`_x.scss`という名前のファイルは作成禁止です。 「.x-」をつけるのは、HTML上で、このクラスをJavascriptで操作に使っているので変更に注意が必要、ということを明示するためです。 一方、JavaScriptでのDOM操作と、CSSでのスタイル指定は切り離しておいたほうがよいので、「.x-」のついたクラスに対してスタイルを当てることは絶対にしないようにします。 ```css:BAD /* .x-が頭についたクラスはCSSに一切書いてはいけません */ .x-list-element{ color:red; } ``` JavaScriptとCSSの連携については、後述の「.mode-」のついたクラスでコントロールします。 ## JavaScriptで使うIDは「x」で始めたcamelCaseとし、CSS側でそのIDは絶対に使わない jsでのDOM操作のためにIDを割り当てる場合は`id="xSomeElement"`というようにxから始まるcamelCaseで指定します。 CSS側ではこのIDに対してスタイルを当てるようなことはしてはいけません(まあ、そもそもIDは使ってはいけない事になってますが…) ## JavaScriptからスタイルに関わる変更をする場合は`.mode-`というクラスを通して行う JavaScriptから要素の表示/非表示や見た目など、スタイルに関わる部分を変更したい場合は、`.mode-`が頭についたクラスをつけたり外したりすることで行います。 ```js:BAD $('.x-list-item').show(); $('.x-list-item').addClass('show'); $('.x-list-item').css({'display':'block'}); ``` ```js:GOOD $('.x-list-item').addClass('mode-show); ``` JavaScriptで表示非表示をコントロールしたり、styleを直接いじってアニメーションさせたりするのはできるだけ避け、CSSのtransitionやanimationを活用するようにします。 # Sass ## Sassの変数名はスネークケースで書く ハイフンは使わず、`$some_sass_vars`といったような、アンダースコア区切り+小文字のスネークケースで変数を定義します。 ```scss:BAD $base-margin: 12px; $basePadding: 12px; ``` ```scss:GOOD $base_margin: 12px; $base_padding: 12px; ``` ハイフンを使わないのは、クラス名と区別をつけやすくして検索の時に邪魔にならないようにするためと、SASSだと「-」が演算子としても機能するので、それとの区別をつけて意図しない演算やエラーの発生を防ぐためです。 ## Sassの変数名も、ファイル名と一致させる クラス名と同じく、Sassの変数名も、ファイル名と同じ接頭辞で始めます。 ```scss:_header.scss /* GOOD */ $header_color: #999; /* BAD */ $color_of_header: #999; ``` ## Sassのグローバル変数は`_v.scss`に書き、`$v_`で始める ```scss:_v.scss /* _v.scssにグローバル変数をまとめておく */ $v_color_border: #999; $v_color_text: #333; $v_radius: 5px; $v_header_height: 30px; ... ``` ※かつては`_vars.scss`に`$g_`で始まる変数名で書いていたのですが、変数だけルールが違うのが気持ち悪くなって変更しました。 ## Sass変数名の命名ルール Sass変数名は、おおよそ以下のようなルールでつけます。 ```scss //フォントだったら $v_font_text : sans-serif; $v_font_display: serif; //色だったら $v_color_text : #333; $v_color_link : #069; //高さだったら $v_height_header : 60px; $v_height_hero : 200px; ``` 先頭から、prefix(ファイル名に一致させる部分)、おおまかな種別、細かい種別、というように、後ろにいくほど細かい指定になるように指定します。 最近のエディタは補完がよくきくので、この形で指定しておくと、色の指定の変数だったらとりあえず「$v_color」と打てば色の変数一覧が出るので楽、ということでこういう指定にしています。 ## `@extend`は避ける `@extend`は、**extend元のスタイルを変更した時**に影響する範囲が見えず、意図しない部分に影響が出たりするのでできる限り避けます。 ※ `@extend`を活用したSASSライブラリなどを使う場合は除きます。 ## mixinも極力避ける 何かと便利なので使ったりしますが、mixinも極力使わないようにします。 mixinを使って似たような定義をたくさん作るなら、それを一つのclassにしてカスケードしてオーバーライドしたほうがいい場合も多いからです。 後述のレスポンシブ対応のためのユーティリティなどは除きます。 ## レスポンシブ対応はmixinで行う 以下のようなコードで行います。 ```scss:_mixins.scss /* responsive utility */ @mixin screen_hh{ @media screen and (max-width: $v_width_hh_max){ @content; } } @mixin screen_sm{ @media screen and (max-width: $v_width_sm_max){ @content; } } @mixin screen_md{ @media screen and (max-width: $v_width_md_max){ @content; } } @mixin screen_sm_only{ @media screen and (min-width: $v_width_hh_max + 1) and (max-width: $v_width_sm_max){ @content; } } @mixin screen_md_only{ @media screen and (min-width: $v_width_sm_max + 1) and (max-width: $v_width_md_max){ @content; } } @mixin screen_lg_only{ @media screen and (min-width: $v_width_md_max + 1){ @content; } } ``` というのを用意して( ```css:_my.scss .my-list{ font-size:16px; @include screen_hh{ /* handheldサイズ時のスタイルをここに書く */ font-size:14px; } } ``` とします。 ↑のように、同じクラスに対する指定は、レスポンシブ指定も含めて同じ場所にひとまとめにします。 ただしこれをそのまま出力すると、`@media`が出力先CSSに大量に入ってしまうので、postCSSの[mqpacker](https://github.com/hail2u/node-css-mqpacker)を使って同じレスポンシブ指定をまとめるのがおすすめです。 # まとめ 色々書いてはありますが、要するにまずは * ハイフン区切りで小文字のclassしか書かず、classを可能な限りネストしない * class(など、何かしらの名前をつけるもの)の名前の先頭部分と、それが書いてあるファイル名を一致させる * 「g」がついてたらグローバルなので変更は要注意 これを守ってればだいたいOKってな感じです。 Happy Coding! 🎉 ---- # おまけ:様々なCSS記法やツール等についての見解 ## BEM, SMACSS 本当にしっかりモジュール等切り分けて設計していけるサイトなら悪くない。 しかしどれがBlockでどれがElementかとか、どれがLayoutでどれがModuleかなんて事を考えてたら日が暮れるし、Elementのつもりで作っていたものが気づいたらBlockとして切り出すべきレベルに成長するなんていうことは普通に起こりうる。 もっと柔軟で、かつシンプルなルールのほうがいい。 ## CSS ModuleなどJS内に入れ込んでモジュール単位でCSSを書く手法 conasu作ってる時に一度試したけど、管理しやすくもないし余計な手間が増えるだけで大してメリットは感じなかった。 CSSは、カスケーディングできるところによさがあり、モジュールに閉じようとか考えるのはコンセプトとして合わないと感じる。スタイルの衝突を避けるだけならクラス名ベースの規約で十分。 ## BootstrapなどのCSSフレームワーク 一度作ったら捨てておける種類のお手軽に作るにはいいものだとは思う。 しかし、たとえばグリッドの列数を変えたいと思った時にHTML側に手を入れなくてはいけない、みたいに、何かを変更したい時にHTML側に手を入れる必要が出てくることが多々あり、これがいただけない。 なぜ先人が構造(HTML)とスタイル(CSS)の分離を頑張ったのかということについてもう少しだけ考えてほしい。