こんにちは、エンジニアの尾形です。
前回のブログではHTML/CSSコーディングを行なうにあたっての事前準備についてお伝えしました。
今回はCSS規約やSassによるコーディングの実例について、ごく一部ではありますが、ご紹介します。
FLOCSSをベースとした構成について
前回のブログで少し触れましたが、CSSコーディングを行なうにあたり、FLOCSSをベースとしたCSS規約を作成しました。
数あるCSS方法論の中でFLOCSSを採用した理由は以下の通りです。
- コンポーネントの概念がエンジニアにとって馴染みやすい。
- 候補のひとつであったBEMの概念が含まれている。命名しやすい。Sassとの相性が良い。
- 再利用性や拡張性が高く、機能追加や仕様変更に柔軟に対応できそう。
- 情報量が比較的多くわかりやすい。
FLOCSS本家サイト
hiloki/flocss: CSS organization methodology.
FLOCSSのカスタマイズ事例も公開されていましたので、こちらをベースとしました。
styleguide/css-coding-rule.md at master · manabuyasuda/styleguide
ファイル・ディレクトリ構成は以下の通りとしました。なお、Sassを使用しています。
(root) |-- foundation/ | |-- base/ | |-- function/ | |-- keyframes/ | |-- mixin/ | |-- variable/ | |-- vendor/ | `-- vendor-extension/ |-- layout/ `-- object/ |-- component/ |-- project/ |-- scope/ `-- utility/
FLOCSSは次の3つのレイヤーと、Objectレイヤーの子レイヤーで構成されます。
1. Foundation - プロジェクトにおける基本的なスタイル 2. Layout - ページを構成するコンテナーブロックのスタイル 3. Object - プロジェクトにおけるビジュアルパターン 3.1 Component - 小さな単位のモジュール 3.2 Project - プロジェクト固有のパターン 3.3 Utility - 便利クラス、汎用クラスなど
今回のプロジェクトでは、前述した構成にあるようにFoundationレイヤーに子レイヤーを追加しました。レイヤーというよりはグループに近いです。グループ化して構成をすっきりさせるのが狙いです。
1.1 Base - 要素セレクタと属性セレクタのデフォルトスタイル 1.2 Function - プロジェクト全体で使用される@function 1.3 Keyframes - プロジェクト全体で使用される@keyframes 1.4 Mixin - プロジェクト全体で使用される@mixin 1.5 Valiable - プロジェクト全体で使用される変数 1.6 Vendor - 外部のライブラリやフレームワーク 1.7 Vendor-Extension - Vendorレイヤーの上書き
また、Objectレイヤーにも子レイヤーを追加しました。ここではComponentやProjectのコンポーネント単位ではなく、それより大きな括りでのスタイルを定義します。ページ固有のスタイルもここで定義しました。
3.4 Scope - ページ内のスコープでのスタイル
カスケーディングについても少し触れたいと思います。カスケーディングはCSSの大きな特徴のひとつです。カスケーディングとは、ある要素のあるプロパティに対する宣言が複数あるときに、ひとつの宣言だけが有効になるようにする仕組みのことです。カスケーディングの管理が煩雑だとCSSが破綻していきます。FLOCSSにおいては、後ろのレイヤーになるほど抽象的なスタイルから具体的なスタイルになり、カスケーディングにおいて強いルールにする必要があります。
上記を踏まえ、レイヤーの読み込み順序は以下のようになっています。
@import "foundation/variable/"; @import "foundation/vendor/"; @import "foundation/vendor-extension/"; @import "foundation/base/"; @import "foundation/function/"; @import "foundation/mixin/"; @import "foundation/keyframes/"; @import "layout/"; @import "object/component/"; @import "object/project/"; @import "object/scope/"; @import "object/utility/";
命名規則について
命名規則はFLOCSSの規約に従って、BEMをベースにしたMindBEMding を採用しました。BEMは、Block、Element、Modifierという3つの概念で構成されます。BEMを使用すると、命名ルールが統一され、HTMLを見ただけでBlockの範囲をとらえることが容易になります。また、再利用性、拡張性も向上し、開発のしやすさ、メンテナンスのしやすさにもつながります。
Objectレイヤーのモジュールに対しては、役割の明確化と名前の重複を避けるために以下の接頭辞を付与しました。
- Component – `.c-*`
- Project – `.p-*`
- Scope – `.s-*`
- Utility – `u-*`
FLOCSSではマルチクラスパターンを基本としていますが、今回の開発ではシングルクラスを基本としました。マルチクラスとシングルクラスの違いを簡潔に言うと、部品の組み合わせをHTML側で行なうかCSS側で行なうかの違いです。シングルクラスを採用した理由は、開発メンバーはオブジェクト指向でのプログラム開発に慣れており、Sassの@extendを使用したシングルクラスの方が馴染みやすくコーディングしやすかったためです。
シングルクラスの例を挙げると以下のようになります。
.button { display: inline-block; cursor: pointer; } .button--primary { @extend .button; background-color: #CCAA00; } .button--secondary { @extend .button; background-color: #FFCC00; }
BEMを使用すると、子要素が多い場合にElementが複数になることがあり、マークアップの仕方にバラつきが出ることがあります。
今回はバラつきを避けるために以下のようなルールを追加してマークアップしました。
<ul class="nav"> <li class="nav__item"> <ul class="nav__item-items"> <!-- 3単語で区切る場合 --> <li class="nav__item-items-item"> <!-- ここから子孫として新Blockにします。 --> <a href="#" class="link"></a> </li> <li class="nav__item-items-item"> <a href="#" class="link"></a> </li> </ul> </li> <li class="nav__item"> <!-- 2単語で区切る場合 --> <ul class="nav__item-items"> <!-- ここから子孫として新Blockにします。 --> <li class="item"> <!-- 新Blockに対してBEMでElementを追加します。 --> <a href="#" class="item__link"></a> </li> <li class="item"> <a href="#" class="item__link"></a> </li> </ul> </li> </ul>
- .nav__itemの子要素は.nav__item-items、.nav__item-items-itemのようにBlock__Elementの後に-を区切りとしてElementを追加していきます。
- Element名自体に-が含まれる場合も、Element区切りの-との区別は設けません。
- ElementはBlockのスコープから飛び出してはいけません。
- Element名は3単語までとします。それ以上の入れ子となる場合は、ブロックをリセットして子孫とします。
- 3単語で区切る際に3単語目がBlockになってしまう場合は2単語でBlockをリセットします。
Sassは以下のようになります。
// 3単語で区切る場合 .nav { &__item { } &__item-items { } &__item-items-item { // ここで親Blockをスペース区切りで指定して、子孫として新Blockにします。 // 子Block名は親Block内でユニークであれば任意の値で問題ありません。 & .link { } } } // 2単語で区切る場合 .nav { &__item { } &__item-items { // ここで親Blockをスペース区切りで指定して、子孫として新Blockにします。 // 子Block名は親Block内でユニークであれば任意の値で問題ありません。 & .item { // 新Blockに対してBEMでElementを追加します。 &__link { } } } }
上記の場合、CSSは以下のようになります。
// 3単語で区切る場合 .nav {} .nav__item {} .nav__item-items {} .nav__item-items-item .link {} // 2単語で区切る場合 .nav {} .nav__item {} .nav__item-items {} .nav__item-items .item {} .nav__item-items .item__link {}
プロパティの宣言順
CSSのルールセット内のプロパティの宣言は、機能ごとにまとめて記述することでCSSの可読性が上がると思います。例えば、表示方法に関するプロパティ(display, visibility など)、位置に関するプロパティ(position, z-index など)、フォントに関するプロパティ(font-size, line-height など)などのことです。
ただ、これらの宣言順序を各開発メンバーが意識しながらコーディングするのでは効率が悪いので、CSSのプロパティを自動で並べ替えしてくれる CSScomb というツールを使用して、開発メンバー間で同じような記述となるようにしました。
開発メンバーが使用していたエディター(IDE)は、Atom、PhpStorm、NetBeans、VisualStudioCodeとバラバラでしたが、どのエディターにもCSScombのプラグインは提供されていました。蛇足ですが、私は Eclipse -> NetBeans -> VisualStudioCode とエディターを乗り換えて今に至りますが、キーバインディングはEclipseのものをずっと使用しています。操作感を特に変えずにエディターを変更できるのは良いですよね。
いま現在は、VisualStudioCodeを利用しているメンバーが多くなってきています。その理由は、TypeScript言語での開発をメインで行っており、その点においてほかのエディターと比べて秀でていると感じたためです。(感想には個人差があります)
エンジニアとデザイナーの思考の違い
本筋から外れますが、今回SPAの開発を行なうにあたり、デザイナー(非エンジニア)とエンジニアが協働してデザイン・UI/UXを作り上げ、ともにHTML/CSSコーディングを行ないました。その際に感じた点を少しお伝えしたいと思います。
我々エンジニアは「ボタン」や「入力ボックス」などの最小単位の部品(オブジェクト指向で言うオブジェクト)から考える傾向にあります。それとは逆にデザイナーは全体のレイアウトを考えてから内部の部品を考える方が多いのではないでしょうか。
そのため、エンジニアがコーディングを行なう際に「あの画面とこの画面で微妙にデザインが違う!同じにして!」というのはよくあると思います。もちろん明確な理由があってデザインを変えているケースはあると思いますが、エンジニアにとってはできるだけ共通の部品を使用してコード量を減らしたいですよね。
この思考に違いについては、デザイナーにもFLOCSSでHTML/CSSコーディングを行なってもらいオブジェクト指向の考え方を理解してもらうことで、隔たりを埋められたと思っています。念のためお断りしておくと、一方的にエンジニアの思考を押し付けたわけではありません。我々エンジニアも類似サービスの調査等を通してUI/UX設計について理解を深める努力をしました。デザインに関して疑問が生じた際は、そのようにした理由・経緯をデザイナーから説明してもらいエンジニアが納得したうえで進めるようにしました。お互いの思考を理解しあって歩み寄ることも必要だと思います。
まとめと予告
今回はCSS規約やSassによるコーディングの実例について、ごく一部ではありますが、書かせていただきました。
次回はSassコーディングを行なう上で必要となる各種自動化の話について紹介させていただきたいと思います。
引用元・出典
hiloki/flocss: CSS organization methodology.
styleguide/css-styleguide.md at master · manabuyasuda/styleguide
styleguide/css-coding-rule.md at master · manabuyasuda/styleguide
フロントエンドエンジニアへの道
1- HTML/CSSコーディングはじめるぞ〜HTML/CSSコーディングを行なうにあたっての事前準備
2- チーム作業をするための最適なCSS規約、Sassについて考えた
3- Sassコーディングを行なう上で必要となる各種自動化の話
4- Angular1によるSPA開発 – 基本編
5- Angular1によるSPA開発 – 設計編
6- Angular1によるSPA開発 – Tips編