私的(あるいはテンマド社の)CSS/SCSSコーディングについてのルールまとめです。
自分自身の整理/備忘録と、自分の書いたコードを引き継ぐなどしていじることになった人に見せる用。随時更新。
最終更新:2019/09/25 (→ 変更内容 )
素のCSSを使うような苦行は避けます。
ほとんど好みの問題ですが、Lessは記法がちょっとよくないので、Sassを使います。
処理速度やツールセットの関係で、本家RubyのSassではなく、node-sassのほうを、大抵はgulp-sassで走らせて使います。
※そろそろSassを捨ててPostCSSのみで済ませるのも現実的になってきているので、プロジェクト参加者のCSS知識/経験値によってはそちらの方向もアリです。
Sassを使う場合、SASSの記法(HAMLっぽいやつ)は使わず、SCSS(よりCSSっぽい記法)のほうを使います。
学習コストが低いことと、JSライブラリ付属のCSSやネットで公開されているコードなどを取り込みやすいからです。
AutoprefixerなどPostCSS方面のツールを活用して、.scss
ファイルにはできる限り最新仕様ベースのCSSを、ベンダープレフィックスなしで書くようにします。
クラス名は、ページ上での役割や意味、構造を表すように命名します。
たとえばナビゲーションなら.nav
、ヘッダなら.header
、強調したい箇所には.strong
など、ページ上でその要素がどういう役割なのか(どういう見た目なのかではなく)をクラス名で表すようにします。
文字を赤くしたいところに.text-red
、太字にしたいから.text-bold
、6カラム分の領域だから.col-6
など、見た目を表すクラス名はNGです。
<div class="column-6 column-3-xs margin20 floated">
<h1 class="red text-large">Red Large Text</h1>
</div>
BAD<section class="main-section">
<h1 class="main-section-title">Awesome title</h1>
</section>
GOODページの見た目やレイアウトは、デザインの都合で変更することがよくあります。
見た目を表すクラス名をHTMLに書いてしまうと、デザイン変更の時に必ずHTMLの変更が必要になり、規模の大きなサイトになればなるほどデザイン変更の辛さが増していきます。
HTML側には見た目に関わる要素を入れず、見た目に関わることはできる限りCSS側で行うようにしておくほうが、デザインの変更に強く、レスポンシブ対応に向いた、メンテナンス性の高いHTMLになります。
また、「役割」を表すクラス名をつけておくと、HTMLの各部分がページ上でどういう役割で、どういう意図で書かれたものなのか一目でわかりやすくなり、実際にレンダリングされたWebページとの対照もさせやすくなるので、メンテナンスしやすくなります。
#main {
color:red;
}
#main ul{
color:green;
}
BAD.main{
color:red;
}
.main-list{
color:green;
}
GOODID指定でスタイルを書くことはしません。全てクラスで指定します。
IDは詳細度(selector's specificity)が高すぎて、他の場所でスタイルを上書きしようとした時!important
を多発しなくてはいけなくなったり、あれこれと障害になるためです。
.some-class ul{
color:red;
}
ul.some-class li{
color:red;
}
BAD.some-class-list{
color:red;
}
.some-class-list-item{
color:red;
}
GOODリセット/初期化する部分を除いて、タグ名で、あるいはタグ名を含めたスタイル指定は一切せず、面倒でも全部クラスを割り振り、クラスに対していスタイル指定をします。
タグの構造は、制作していく上の都合やJSとの兼ね合いで変えることがよくあるので、その際に書き換える手間を減らし、HTMLの構造とCSSによるスタイリングを極力分離しておくためです。
例外として、たとえばMarkdownパーサが出力するHTMLなど、クラスの割り当てられない部分で、かつタグにスタイルを指定するのが妥当な部分は除きます。
.snakecase_class{
color:red;
}
.camelCaseClass{
color:red;
}
.Has-Capital-CLASS{
color:red;
}
BAD.good-class-definition{
color:red;
}
GOODクラス名は小文字の英数字+ハイフン区切りを基本とします。
シフトキー押さなくていいので楽ですし、大文字小文字のタイプミスでスタイルが当たらなくてうんうん悩む事も減るからです。
例外として、Angularアプリを書いていてdirective名とCSSの接頭辞を合わせたい、CSS増えすぎて小文字だけだとつらい、などの理由がある時に、接頭辞部分(一番先頭の部分)に限り、camelCaseを使ったりするのはアリです。
クラス名の先頭部分(最初のハイフンが登場する前の部分)と、それが定義されているファイル名が必ず一致するようにします。
たとえば、.mypage-nav
というクラスのスタイル指定は、_mypage.scss
に書くようにします。
/* _header.scss */
.my-awesome-header{ ... }
.alternative-header{ ... }
BAD/* _header.scss */
.header{ ... }
.header-logo{ ... }
.header-nav{ ... }
GOODもし、一つのファイルにクラス定義が多くなってきた場合は、ファイルを分けてもいいのですが、その場合も「クラスの先頭部分とファイル名が一致するようにする」というルールは維持します。
例えば.main-xxxx
というクラスが非常に多くなって_main.scss
が肥大化し、メンテしづらくなってきたとして、.main-content-
で始まるものを別ファイルに切り出したら、そのファイルは_main-content.scss
にします。
こうすることで、クラス名を見た瞬間にそれがどのファイルに書かれているのかがわかるようになり、開発効率が上がります。
同時に他のファイルに書かれたクラスと同じクラスをうっかり指定して上書きしてしまうような事故も防げます。
※ 例外として、状態などを表す.mode-
で始まるクラスだけ、ファイル名と関係なく使うことができます。ただし、.mode-
はファイル名に一致するほうのクラスにつなげる(?)形でしか定義してはいけません。
/* _my.scss */
.my-btn .mode-disabled{ ... }
.mode-disabled{ ... }
BAD/* _my.scss */
.my-btn.mode-disabled{ ... }
.my-btn{
&.mode-disabled{ ... }
}
GOOD.g-
で始めるサイト全体で使うようなスタイル(たとえばボタンやform要素)は、全て.g-
で始まるクラスを使ってスタイル指定します。で、当然、_g.scss
に書きます。
/* サイト全体共通で使うボタンのスタイル */
.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にしておきましょう).g-
のクラスを割り当てます。_header.scss
上で_form.scss
に書いてある.form-
で始まるクラスのスタイルを上書きしたい、みたいな場面に出くわした場合、↓のネストのルールに触れるので、その上書きしたいクラスは.g-
に移すべきです。※DRY=Don't repeat yourself。要するに同じコードをコピペしたりして二度以上書かないようにする、ということ。何かしらのよいテンプレートエンジンを活用して実現しましょう
クラスのネストは基本的にはしません。
既存のクラスを上書きするような形で新しい定義をしたいような、必要な場合に限り1階層のネストが許されます。
.my-parent .my-child .my-element{ ... }
/* ↓ネームスペースとかコンポーネント的な発想でこうするのも害が多いのでダメです */
.my-parent{
.my-child{ }
.my-child2{ }
}
BAD.my-parent{ ... }
.my-child{ ... }
.my-parent-altenate{
.my-child{ ... }
}
GOOD同時に、.g-
ではじまるグローバルなクラス以外は、別ファイル上で定義されているクラスをネストして上書きすることは避けます。
/* _my.scss で… */
.my-list{
.user-list-item{
color:red;
}
}
BAD基本的にやっていいのは
.my-module{
.g-btn{ /* g-で始まるグローバルなクラスの定義を上書き */ }
}
といった感じで、.g-
で始まるグローバルなクラスのスタイルを、グローバルじゃないクラスの中で上書きしてカスタムするような場合と、
.my-module{ ... }
.my-module-alt{ ... }
.my-module-element{
...
.my-module-alt &{
...
}
}
といった感じで、同じファイル内に定義された既存のクラス上書きしてカスタムするためにネストさせる場合のみで、それ以外のネストはできる限り避けます。
せっかくSassなのにネストという便利機能を使わないのももったいない気はしますが、クラスをネストさせて多く重ねると詳細度が上がり、他の部分でそれを上書きするスタイルを定義したい時に、!important
の多用や、無駄に詳細度の高い記述を要求されるようになり、長くメンテナンスを続けると地獄のようなCSSになりますので避けたほうがよいです。
クラスをネストする場合、どちらを親にするかで二通りの書き方ができますが、基本的に&
を使ってスタイルを当てようとしているクラスのほうが親になる形で書きます。
.nav-list{
...
.nav-list-item{ ... }
&.mode-alt .nav-list-item{ ... }
}
.nav-list-item{ ... }
BAD.nav-list{ ... }
.nav-list-item{
...
.nav-list &{ ... }
.nav-list.mode-alt &{ ... }
}
GOODこうすることで、↑の例でいうと.nav-list-item
というクラスに関わるスタイル定義を全て一箇所に集めることができ、CSSファイル内をあちこち探し回らなくて済むようになります。
&
は使わないBEMのサンプルなどで、クラス名の補完で&
を使う例がありますが、この使い方は禁止です。
.header{
&-logo{ ... }
&-nav{ ... }
}
BAD.header{ ... }
.header-logo{ ... }
.header-nav{ ... }
GOODなど、メンテナンスしていく上でつらいCSSになりやすいからです。
@keyframe
のアニメーション名の先頭部分などもファイル名に合わせるアニメーション名などに限らず、とにかく名前を付ける必要のあるものは、名前の先頭部分とファイル名が一致するようにします。
JavaScriptで要素の取得やDOM操作のために使うclassは、全て「.x-」で始めるようにし、この「.x-」で始まるクラスに対してはCSSでスタイル指定をしてはいけません。
当然、_x.scss
という名前のファイルは作成禁止です。
「.x-」をつけるのは、HTML上で、このクラスをJavascriptで操作に使っているので変更に注意が必要、ということを明示するためです。
一方、JavaScriptでのDOM操作と、CSSでのスタイル指定は切り離しておいたほうがよいので、「.x-」のついたクラスに対してスタイルを当てることは絶対にしないようにします。
/* .x-が頭についたクラスはCSSに一切書いてはいけません */
.x-list-element{ color:red; }
BADJavaScriptとCSSの連携については、後述の「.mode-」のついたクラスでコントロールします。
jsでのDOM操作のためにIDを割り当てる場合はid="xSomeElement"
というようにxから始まるcamelCaseで指定します。
CSS側ではこのIDに対してスタイルを当てるようなことはしてはいけません(まあ、そもそもIDは使ってはいけない事になってますが…)
.mode-
というクラスを通して行うJavaScriptから要素の表示/非表示や見た目など、スタイルに関わる部分を変更したい場合は、.mode-
が頭についたクラスをつけたり外したりすることで行います。
$('.x-list-item').show();
$('.x-list-item').addClass('show');
$('.x-list-item').css({'display':'block'});
BAD$('.x-list-item').addClass('mode-show);
GOODJavaScriptで表示非表示をコントロールしたり、styleを直接いじってアニメーションさせたりするのはできるだけ避け、CSSのtransitionやanimationを活用するようにします。
ハイフンは使わず、$some_sass_vars
といったような、アンダースコア区切り+小文字のスネークケースで変数を定義します。
$base-margin: 12px;
$basePadding: 12px;
BAD$base_margin: 12px;
$base_padding: 12px;
GOODハイフンを使わないのは、クラス名と区別をつけやすくして検索の時に邪魔にならないようにするためと、SASSだと「-」が演算子としても機能するので、それとの区別をつけて意図しない演算やエラーの発生を防ぐためです。
クラス名と同じく、Sassの変数名も、ファイル名と同じ接頭辞で始めます。
/* GOOD */
$header_color: #999;
/* BAD */
$color_of_header: #999;
_header.scss_v.scss
に書き、$v_
で始める/* _v.scssにグローバル変数をまとめておく */
$v_color_border: #999;
$v_color_text: #333;
$v_radius: 5px;
$v_header_height: 30px;
...
_v.scss※かつては_vars.scss
に$g_
で始まる変数名で書いていたのですが、変数だけルールが違うのが気持ち悪くなって変更しました。
Sass変数名は、おおよそ以下のようなルールでつけます。
//フォントだったら
$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を使って似たような定義をたくさん作るなら、それを一つのclassにしてカスケードしてオーバーライドしたほうがいい場合も多いからです。
後述のレスポンシブ対応のためのユーティリティなどは除きます。
以下のようなコードで行います。
/* 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;
}
}
_mixins.scssというのを用意して(
.my-list{
font-size:16px;
@include screen_hh{
/* handheldサイズ時のスタイルをここに書く */
font-size:14px;
}
}
_my.scssとします。
↑のように、同じクラスに対する指定は、レスポンシブ指定も含めて同じ場所にひとまとめにします。
ただしこれをそのまま出力すると、@media
が出力先CSSに大量に入ってしまうので、postCSSのmqpackerを使って同じレスポンシブ指定をまとめるのがおすすめです。
色々書いてはありますが、要するにまずは
これを守ってればだいたいOKってな感じです。
Happy Coding! 🎉
本当にしっかりモジュール等切り分けて設計していけるサイトなら悪くない。
しかしどれがBlockでどれがElementかとか、どれがLayoutでどれがModuleかなんて事を考えてたら日が暮れるし、Elementのつもりで作っていたものが気づいたらBlockとして切り出すべきレベルに成長するなんていうことは普通に起こりうる。
もっと柔軟で、かつシンプルなルールのほうがいい。
conasu作ってる時に一度試したけど、管理しやすくもないし余計な手間が増えるだけで大してメリットは感じなかった。
CSSは、カスケーディングできるところによさがあり、モジュールに閉じようとか考えるのはコンセプトとして合わないと感じる。スタイルの衝突を避けるだけならクラス名ベースの規約で十分。
一度作ったら捨てておける種類のお手軽に作るにはいいものだとは思う。
しかし、たとえばグリッドの列数を変えたいと思った時にHTML側に手を入れなくてはいけない、みたいに、何かを変更したい時にHTML側に手を入れる必要が出てくることが多々あり、これがいただけない。
なぜ先人が構造(HTML)とスタイル(CSS)の分離を頑張ったのかということについてもう少しだけ考えてほしい。