読者です 読者をやめる 読者になる 読者になる

リクルート住まいカンパニー Tech Blog

ITのちからで暮らしをよくしたい、エンジニア・デザイナーが発信するTech情報メディア

【全訳】JSだけじゃない!AirbnbのCSS/SCSSスタイルガイド

f:id:recruit-sumai:20170310185752p:plain

こんにちは!2016年に新卒で入社した、SUUMOのフロント開発グループでエンジニアをしているやなぎさわです。

入社早々SUUMOのCSSを再設計をする機会があり、CSS/SASSのスタイルガイドを研究していたのですが、JavaScriptのスタイルガイドで有名なAirbnbがCSSのスタイルガイドを公開しているのを見つけました。

一人で開発していたり、CSS/SASSを書く人が少人数である場合は意外とその重要性に気付きにくいのがこのCSS/SASSのコーディング規約です。CSSは単純な言語であり単にスタイリングを当てるだけであればわりと簡単にできてしまうのですが、大規模開発や修正が頻繁に必要となる運用フェーズになるとその単純さがゆえにバグを作り出しやすくなってしまいます。

特にCSSでは全てのセレクタのスコープがグローバルであり、気を抜くとすぐにクラス名などが被ってしまいます。BEMとOOCSSを組み合わせた命名規則についてこのAirbnb CSS/SASSスタイルガイドでも語られていますが、このような命名規則によるCSSのグローバルスコープへの対処方法を一度読んでみると大規模開発やより運用しやすい開発のヒントが得られるかもしれません!

では以下日本語訳になります。特に用語のセクションの後から大事なポイントがいくつか出てきます。

翻訳元

Airbnb CSS / Sass スタイルガイド

CSSとSASSを合理的に書くためのガイド

目次

  1. 用語
  2. CSS
  3. Sass
  4. 翻訳

用語

スタイルの宣言

“スタイルの宣言"とはセレクタ(もしくは複数のセレクタ)とそれに伴う1つ以上のプロパティのことである。 以下、例。

.listing {
  font-size: 18px;
  line-height: 1.2;
}

セレクタ

スタイルの宣言において、"セレクタ"はプロパティによってどのDOMにおける要素をスタイリングするかを決定するものである。セレクタはHTML要素のclass, id, もしくはその他のattributeのどれかにあたるものである。以下はセレクタの例である。

.my-element-class {
  /* ... */
}

[aria-hidden] {
  /* ... */
}

プロパティ

そして、プロパティは要素に対してスタイルを実際に定義するものである。プロパティはキーと値のセットであり、スタイルの宣言は1つ以上のプロパティの宣言を含むことができる。プロパティの定義は以下のようである。

/* ここにセレクタを記述する */ {
  background: #f1f1f1;
  color: #333;
}

CSS

フォーマット

  • インデントは2スペース
  • キャメルケースではなくダッシュを使うことを推奨
  • もしBEMを使っているのであればアンダースコアとパスカルケースは使用可能(下記のOOCSS and BEMをみてください)
  • IDセレクタは使わない
  • 複数セレクタのスタイルルールを宣言する場合は1行に1つのセレクタを記述する
  • 中括弧の開始、つまり '{'の前にスペースを1つ入れる
  • プロパティにおいて、:の前でなく後ろにスペースを1つ入れる
  • 中括弧の終了、つまり }の前に改行を入れる
  • スタイルの宣言の間には改行を入れること

悪い例

.avatar{
    border-radius:50%;
    border:2px solid white; }
.no, .nope, .not_good {
    // ...
}
#lol-no {
  // ...
}

良い例

.avatar {
  border-radius: 50%;
  border: 2px solid white;
}

.one,
.selector,
.per-line {
  // ...
}

コメント

  • コメントを記述する際は, ブロックコメント(/* */)ではなくラインコメント(//)を使う
  • コメントはなるべく行末コメントを避け、コメント用に1行使う
  • それ自身がドキュメントとなるように詳細にコメントを書く
  • z-indexの使用
  • 互換性やブラウザ対応

OOCSS と BEM

OOCSSとBEMの組み合わせを推奨。理由は以下の通り:

  • CSSとHTMLの間に明快で厳格な関係を生む
  • 再利用しやすくなり、コンポーネントを組み立てやすくなる
  • ネストの利用を減らし、詳細度を下げる
  • 拡張性の高いスタイルシートを作り上げる

OOCSS, もしくは “オブジェクト指向CSS”とは、スタイルシートをオブジェクトの集合として考えやすくするためにCSSを書く方法の一つである。ここでいうオブジェクトの集合の、 “オブジェクト”とはウェブサイトとは独立した再利用しやすいスニペットをさす。

BEM, もしくは“Block-Element-Modifier”, とはHTMLとCSSの命名規則である。Yandexのコードの拡張性についての思想から発展し、OOCSSを実装するためのガイドラインとして存在している。

BEMの変形であるパスカルケースを用いた “blocks”は、コンポーネント(Reactなど)を用いた時に特にうまく機能すると考えている。アンダースコアやダッシュはモディファーや子要素に使われている。

function ListingCard() {
  return (
    <article class="ListingCard ListingCard--featured">

      <h1 class="ListingCard__title">Adorable 2BR in the sunny Mission</h1>

      <div class="ListingCard__content">
        <p>Vestibulum id ligula porta felis euismod semper.</p>
      </div>

    </article>
  );
}
/* ListingCard.css */
.ListingCard { }
.ListingCard--featured { }
.ListingCard__title { }
.ListingCard__content { }
  • .ListingCardは “block”であり、高階層のコンポーネントを表している
  • .ListingCard__title は“element”であり、.ListingCardをblockとして構成するための構成要素である子孫要素として表される
  • .ListingCard--featuredは “modifier”であり、.ListingCardの異なる状態やブロックのバリエーションの一つとして表される

IDセレクタ

CSSではIDによってセレクタとして要素を決定することができるが、それはアンチパターンとして考えられるべきでもある。IDセレクタはスタイルの宣言に不必要に高い詳細度をもたらし、再利用ができない。

この詳細度の扱いについては CSS Wizardry’s articleを読むと良い。

JavaScript フック

同じクラスをCSSとJavaScriptの両方でバインディング(JSのイベントをつけつつスタイリングを行うなど)をするのは避けること。この2つを混同することは少なくとも開発者が変更を行うCSSとJavaScriptの両方を理解しなければならないことになり時間の無駄であるし、最悪の場合は機能が壊れることを恐れて変更をすることを躊躇してしまう。

.js-という接頭辞を持つJavaScriptバインド専用のクラスを作ることを推奨する。

<button class="btn btn-primary js-request-to-book">Request to Book</button>

ボーダーBorder

ボーダーのないスタイルにはnoneの代わりに0を使うこと。

悪い例

.foo {
  border: none;
}

良い例

.foo {
  border: 0;
}

Sass

シンタックス

  • scssシンタックスを使い、元のsassシンタックスは使わない
  • 通常のCSS, @include記法の宣言などの順序(下記参照)

プロパティの宣言順序

  1. プロパティの宣言@includeやネストセレクタなどではない通常のプロパティを全て並べる
    .btn-green {
      background: green;
      font-weight: bold;
      // ...
    }
    
  2. @include記法の記述@includeのまとまりを作るとセレクタ全体を読むのが楽になる
    .btn-green {
      background: green;
      font-weight: bold;
      @include transition(background 0.5s ease);
      // ...
    }
    
  3. セレクタのネストセレクタのネストは,仮に必要なのであれば, 中括弧の最後に記述しその後ろには何も書かない。ネストされたスタイルの宣言の前には1行空ける。また、上記の複数セレクタと同様に複数セレクタを記述する際は上記のガイドラインにしたがって1行に1セレクタの記述とする。
    .btn {
      background: green;
      font-weight: bold;
      @include transition(background 0.5s ease);
    
      .icon {
        margin-right: 10px;
      }
    }
    

変数

変数の名前にはキャメルケースやスネークケースではなくダッシュつなぎを使用する。接頭辞として同じファイル内でのみ使われる変数に関しては例外として使用を認める。(例. $_my-variable)

ミックスイン

ミックスインはDRY(Don’t Repeat Yourselfの略)にし明快にするため、もしくは良い名前がつけられた関数のように複雑さを抽象化するために使われるべきだ。このためにミックスインを使うのは何ら問題ないが、もしファイル通信を圧縮していないのであれば(例. gzipなど), このことは余計なコードの重複をもたらすので注意しなければならない。

Extend記法

@extend 記法は直感的ではなく、危険な振る舞いを引き起こす可能性があるため、特にネストさせている場合などは使うべきではない。たとえ一番上の階層で仮置きとしてのセレクタを拡張するのだとしても、もしセレクタの順序を後々変更してしまった場合などに問題を引き起こす。(例えば異なるファイルにその記述があり、ファイルのロード順序がずれた場合など) gzipをかければ@extendを使うことによって得られる合理的な出力結果のメリットの大抵はカバーできるし、ミックスインを使えばDRYな素晴らしいコードを生み出すことができる。

セレクタのネスト

3階層以上ネストさせない

.page-container {
  .content {
    .profile {
      // STOP!
    }
  }
}

もしセレクタの階層がこれほど深くなった場合、あなたのCSSは以下のようになっていると考えられる:

  • HTMLとの結びつきが強くなりすぎている、もしくは
  • セレクタの詳細度が高すぎる、もしくは
  • 再利用できない

もう一度: 決してIDセレクタはネストさせてはいけない!

もしIDセレクタを最初におきたい場合(本来やるべきではないが)、決してネストさせてはいけまない。もしあなた自身がこのようなことをしていると気づいた場合は、もう一度マークアップを見直すかなぜそのような強い詳細度にしなければならなかったのかを確認する必要がある。もしHTMLとCSSを正しいフォーマットで書いていればこのようなことをする必要は絶対にない。

翻訳

このスタイルガイドでは以下の言語でも読むことができます:

 

以上いかがでしたか?

Githubの原文が記載されているレポジトリではscss-lintを使ってコーディング規約に沿っていない記述には警告を出すLinterの設定ファイルも混ざっています。コーディング規約を決めた後はその規約をどう運用していくかも重要になっていくためこの設定ファイルなども開発の参考にしてみてはいかがでしょうか?? 住まいカンパニーでは非常に複雑なシステムの上でCSSを記述しているため、特にこのようなコーディング規約や設計はとても重要になってきます。 JSやCSSをコーディング規約を一緒に作りながら10年後の未来を見据えて最適なフロントエンド開発を行ってくれるエンジニアを探しています!!

詳しくはこちら