はじめに
今回、社内でフロントのまるまるリニューアル(RailsのViewは基本的に全て変更)があるので、CSS設計やディレクトリ構成などをどうするか色々まとめてやってます。
コンポーネント化を意識したり、どういうルールを適用したりするかをまとめていたら結構な量になったので共有しようと思った次第です。
BEMやSMACSS、FLOCSSなどを導入したけどそれだけじゃどうも綺麗にならない、だったりどこのファイルに何を書いていけばいいかいまいち分からない…といった方には参考になるかと思います。独自で作ったものであることと、リニューアルの初期段階でこれから問題がいくつか発生する(すでに発生していくつか改善しています)可能性があることを踏まえてご覧いただければと思います。不明点や気になる点はコメントで聞いていただければ幸いです。
以前、基本的なものは僕が普段使っているCSSのルールをコーディング規約にしてみたにまとめたものの、プロジェクトで使う時にはもっと細かくルール化されているほうがやりやすいなと判断し、新しく記事にしました。
なお、Rails、Sass、Compassなどの利用を前提としているので単にフロントだけで使いたいといった場合は状況に合わせて変更していただければと思います。
※ わりかし長いです。
この記事のバージョンについて
7/26 Ver 1.0.0
Gemfile
今回の話に関連するGemfileについて記述。
ruby '2.1.4'
gem 'rails', '4.2.0'
gem 'sass-rails', '5.0.1'
gem 'compass-rails', '~> 2.0.4'
gem 'bootstrap-sass'
gem 'slim-rails'
gem 'html2slim'
gem 'modernizr-rails'
gem 'font-awesome-rails'
目的
- 社内でのフロントエンドのコーディングルールの統一化
- FLOCSSやBEMなど様々なものが使われているが何を使うかはじめに決めておいて統一しておきたい
- レベルが違う人が書いても同じようなCSSが書けるようになることを目標として、あとはCSS力を上げるだけになるようにする
まずはプロジェクト単位で導入。やってみて良さそうだったら別のプロジェクトでも導入していくというプロセスをとっていきたいと考えている。
前提
- Railsでの利用
- Slim、SassとCompassを使用
- SMACSSとBEMのmodifierを採用。SMACSSのmoduleではOOCSSの考えをもとに書く
注意
私が他に書いたQiitaとは一見すると矛盾しているところがあるかもしれないが、プロジェクトに最適なものにした一例ということで見ていただきたい。大事なのはルールを決めて一貫性のあるCSSにすること。
基本的なコーディング規約
参考
僕が普段使っているCSSのルールをコーディング規約にしてみた
私が書いたQiitaをベースにしている。(一部今回のプロジェクトでは違うところがある) 今回のプロジェクトに合わせて必要なものだけ変更しているので基本下参照。Qiitaにはより詳細が書いてあるので一読をおすすめする。
インデントは2タブにする
色はHEXにする
.fugafuga
color: rgb(255,255,255) // NG
.hogehoge
color: #000 // OK
.fafafa
color: rgba(#000, 0.8) // OK(アルファを指定したい時)
rgbを使わない。
色定義は可能なら三桁で記述する
.hogehoge
color: #aabbcc // NG
.fugafuga
color: #abc // OK
コメントアウトは //を使う
/* */は使わないで、 //で統一
何か大きいブロックでコメントを入れたいときは
//
// Write here!
//
とする。
IDセレクタは使わない
idはJavaScriptもしくはページ内アンカーのために使い、スタイルを当てるためには使用しないこととする。
important!は使わない
jQueryプラグインの制約などよっぽど特別な事情が無い限りNG。
制約の強いCSSは書かない
p.hogehoge
みたいなのはNG。ただしmoduleに合わせて子セレクタを使うのはOK。
整数部分が0の少数値は整数部分を省略する
.hogehoge
margin: 0.8em // NG
.fugafuga
margin: .8em // OK
ゼロには単位を付けない
.hogehoge
margin: 0px // NG
.fugafuga
margin: 0 // OK
プロパティはabc順にする
// NG
.hogehoge {
color: red
background-color: blue
}
// OK
.fugafuga
background-color: blue
color: red
Mixinはプロパティの最後に記述し、Mixinが複数ある場合はabc順にする
基本的に以下のようにする。
// NG
.hogehoge {
@include mixin_name
background-color: blue
color: red
}
// OK
.fugafuga
background-color: blue
color: red
@include mixin_name
別々のセレクタとプロパティは改行して書く
// NG
.hogehoge, .fugafuga
color: red
.hogehoge,
.fugafuga
color: red
理由:可読性のため。
HTML/CSSの命名はわかりやすいものにする
上の記事が分かりやすいので読んでおくこと。
CSSは以下の形式で書く
.hogehoge
color: red
キーとバリューの間は空ける。(Sassだと確か空けないと自然とエラーになったはず)
別々のCSSルールは改行してスペースも空ける
// NG
.hogehoge
color: blue
&:first-child
color: blue
.fugafuga
color: red
// OK
.hogehoge
color: blue
&:first-child
color: blue
.fugafuga
color: red
理由:可読性の問題。SCSSの場合はスペースは空けないことにしていたがSass記法の場合はやや見にくいと感じたのでスペースをあけることにする。なおセレクタの中にセレクタを書いていく場合にはスペースを空けないこととする。(&:first-childのところ)
変数
命名
スネークケースで記述する。
× $base-color
× $baseColor
◯ $base_color
どこに書くか
Sassで使う変数はページ固有のものの場合はそのページ内の先頭に書く。
数カ所のレイアウトでしか使わない場合(paddingの大きさやネガティブマージンの調整など)は使う場所の近くに変数を書いてもいい。
メディアクエリ
FIXME: レスポンシブ対応が必要になったら記述
ダミーイメージ
placehold.itが使いやすくておすすめ。
/* widthが先で heightが後 */
<img src="http://placehold.it/200x200">
Slim
Slimとは
- erbと滋賀って無駄な<>とか書かなくてよくなる
- 高速かつ軽量
- Ruby製テンプレートエンジン。
作業効率短縮のために導入。
注意点
divタグのときはdivをつけない
Slimだと
div.hogehoge
| text
は
.hogehoge
| text
でいける。divはよく使うという理由から書かなくてもいいことになっている。
参考ドキュメント
Slimがわからないという人は以下の記事を読んでからはじめることを勧める。
erbをslimに変える
# ファイル直接指定でもいけるはずです。
$ bundle exec erb2slim erbがあるディレクトリ slimを置きたいディレクトリ
# ex) app/views以下をerbからslimに変換してくれる
$ bundle exec erb2slim app/views app/views
# -dを付けるとslimに変換した後にerbを削除する
$ bundle exec erb2slim app/views app/views -d
Sass
メリット
- Sassのほうがインデント記法なので無駄なタイピングが減り作業効率アップへとつながる
- 綺麗に書かないとエラーが出るので、自然と見やすいコードになる
主に上記の2点から導入。SCSSとそこまで差があるわけではないので、学習コストもほぼないと思う。
注意
MixinはSassだと以下のようになる。
.hogehoge
=mixin_here // SCSSでいうと@mixin
+border-radius() // SCSSでいうと@include
参考
普段はSCSSしか書いてないという人ははじめは公式のLearn Sassを見ながら行うことを勧める。
Compass
導入理由
- かゆいところに手が届くmixin
- CSS3が簡単に使える
- わざわざmixinファイルに自前で書く必要がない
- リセットCSSのファイルを自前で作る必要がない
参考:
使うべきCompassのmixin
CSS3モジュール
box-shadowなど、本来プレフィックスが必要なものは基本的にCompassのものを使う。
Helperモジュール
公式に良さそうなものがあったら積極的に使っていく。
Typographyモジュール
積極的に使う。
// hover, focus字に下線をつけてくれる
+hover-link
// normal, hover, active, visited, focusの順に色をまとめて指定できます。最低引数は1つ以上
+link-colors("black", "red", "blue", "yellow", "purple")
// 普通の文章のカラーリングと同じにしてくれる ※ 内部的には color: inherit, text-decoration: inheritなどを行っています。
+unstyled-link
Inline listやInline-block Listは出力されるCSSのコードを考えて基本使わないようにする。
Utilitiesモジュール
clearfixは以下のmixinを使うこと。
.hogehoge
+pie-clearfix;
その他
他にも使えそうなものがあったら公式を見て積極的に活用。レビューで継続的に使えそうか判断してここに追記していくか決める。
リセットCSS
採用の理由
-
normalize.cssとどちらでも問題ないが、resetで基本的に0にして更に必要なものだけプロジェクトに合わせて初期状態を決めておく方針にする
-
normalizeだとCSSの知識量によってうまく実装できない可能性が出てくる
どのリセットCSSを使うか
CompassのリセットCSSを読み込ませる。既にあるものを使った方がいいのではという考え。
追加で記述すること
body
font-family: Arial, Roboto, "Droid Sans", "ヒラギノ角ゴ ProN W3", "Hiragino Kaku Gothic ProN", "メイリオ", Meiryo, sans-serif
a
color: inherit
text-decoration: none
デフォルトだとfont-familyの指定がないため。
デフォルトを遊ゴシックにしたい場合もあるので、その時は追加で記述する。
Mixin
はじめから入れておくべきMixin
プロジェクトでよくあるMixinははじめからいれておく。
Compassでデフォルトで読み込まれるもの以外でよくプロジェクトで使うものは追加しておく。
BEMのmodifier
=m($name)
@at-root #{&}--#{$name}
@content
modifier指定するときは使う。
使い方
.hogehoge
+m(foo) // .hogehoge--foo
arrow
=icon-arrow($bd-col: #999999, $bd-type: solid, $bd-width: 2px, $before-or-after: before, $arrow-deg: 45deg, $arrow-size: 8px)
&:#{$before-or-after}
width: $arrow-size
height: $arrow-size
border-top: $bd-type $bd-width $bd-col
border-right: $bd-type $bd-width $bd-col
+rotate($arrow-deg)
@content
triangle
=triangle($direction: top, $color: #ccc, $size: 2px)
@if not index(top right bottom left, $direction)
@error "Direction must be either `top`, `right`, `bottom` or `left`."
width: 0
height: 0
content: ''
z-index: 2
border-#{opposite-position($direction)}: $size * 1.5 solid $color
$perpendicular-borders: $size solid transparent
@if $direction == top or $direction == bottom
border-left: $perpendicular-borders
border-right: $perpendicular-borders
@else if $direction == right or $direction == left
border-bottom: $perpendicular-borders
border-top: $perpendicular-borders
三角形は使うことが結構あるのでMixinに。
table-cellで上下中央にする
=vertical-center
display: table-cell
vertical-align: middle
heightとline-heightで上下中央にする
=vertical-center-for-one-line($height)
height: $height
line-height: $height
上下中央にしたいものが1行のときはこっちが便利。
positionのmixin(absolute, relative, fixed, staticそれぞれ)
=position($position, $top: null, $right: null, $bottom: null, $left: null)
position: $position
top: $top
right: $right
bottom: $bottom
left: $left
=absolute($args...)
+position(absolute, $args...)
=relative($args...)
+position(relative, $args...)
=fixed($args...)
+position(fixed, $args...)
=static
+position(static)
使い方
.foo
+(absolute, $top: 1rem, $left: 50%)
理由: position系はよく使うため。
calc
=calc($property, $expression, $fallback: 100%)
#{$property}: $fallback
#{$property}: -moz-calc(#{$expression})
#{$property}: -webkit-calc(#{$expression})
#{$property}: calc(#{$expression})
オールドブラウザのフォールバックも対応。
background-colorのlink_colors
=link-background-colors($normal: #fff, $hover: false, $active: false, $visited: false, $focus: false, $is_active: false)
background-color: $normal
@if $visited
&:visited
background-color: $visited
@if $focus
&:focus
background-color: $focus
@if $hover
&:hover
background-color: $hover
@if $active
&:active
background-color: $active
@if $is_active
&.is_active
background-color: $is_activ
テキストのMixinはCompassにあったがbackground-colorは無かったので自作。
アニメーション系
TODO: 必要になったら随時入れる。
CSSファイル構成(SMACSS)
SMACSSに基づいたファイル構成にする。
└── assets/stylesheets/
└── base
└── _base.sass // color, html, bodyなどbaseとなるCSSをココに書く
└── _mixin.sass // Compass以外のmixinを追加
└── _reset.sass // +global-resetを追加した上で、Compass以外に追加して使用したいものがあれば上書きする
└── _utility.sass // Compass以外にutilityとして使いたいものがあれば追加(position: relative; など) 基本的に.u- をprefixにつける
└── font
└── webフォントが必要なら追加
└── layout
└── _grid.sass // gridを独自に実装する場合は使う
└── _layout.sass // header, footerなど .l-を prefixにつける
└── module // component(module)がはいるところ
└── _各component名.sass // 各 component
└── bootstrap
└── _ bootstrap-custom.sass
└── _ bootstrap_and_overrides.sass
└── app.sass
採用理由:ディレクトリごとに何があるかはっきりとわかるため。
app.sassには何を書くか
全てパーシャルファイルとしてimportする。
@charset "utf-8"
@import font-awesome
@import "compass"
//
// base
//
// リセット
@import 'base/reset'
// mixin
@import 'base/mixin'
// ベース
@import 'base/base'
// スーパー汎用クラス
@import 'base/utility'
// hihglight
@import 'base/highlight'
//
// layout
//
// グリッド
@import 'layout/grid'
// レイアウト。 .l-container,.l-headerなど
@import 'layout/layout'
//
// module ※ moduleを追加するごとに下に記述していく
//
@import 'module/g-ad'
@import 'module/top_section'
@import 'module/details'
@import 'module/header_content'
@import 'module/side_content'
@import 'module/nav'
@import 'module/subnav'
@import 'module/search'
@import 'module/copyright'
@import 'module/category'
@import 'module/breadcrumbs'
// Bootstrap
@import "bootstrap/bootstrap-sprockets"
@import "bootstrap/bootstrap-custom"
@import "bootstrap/bootstrap_and_overrides"
Bootstrapについて
今回のプロジェクトではBootstrapを使う。
カスタマイズについて
Bootstrapを使う場合は、全部はいらないよね?bootstrap-sassをカスタマイズして使う方法を参考にしている。
今回のものに合わせて具体的に言うと、
app/assets/stylesheets/bootstrap以下に
_bootstrap-custom.sassを作成して
// Core variables and mixins
@import "bootstrap/variables"
@import "bootstrap/mixins"
// Reset and dependencies
// @import "bootstrap/normalize"
// @import "bootstrap/print"
// @import "bootstrap/glyphicons"
// Core CSS
// @import "bootstrap/scaffolding"
// @import "bootstrap/type"
// @import "bootstrap/code"
// @import "bootstrap/grid"
// @import "bootstrap/tables"
// @import "bootstrap/forms"
// @import "bootstrap/buttons"
// Components
// @import "bootstrap/component-animations"
// @import "bootstrap/dropdowns"
// @import "bootstrap/button-groups"
// @import "bootstrap/input-groups"
// @import "bootstrap/navs"
// @import "bootstrap/navbar"
// @import "bootstrap/breadcrumbs"
// @import "bootstrap/pagination"
// @import "bootstrap/pager"
// @import "bootstrap/labels"
// @import "bootstrap/badges"
// @import "bootstrap/jumbotron"
// @import "bootstrap/thumbnails"
// @import "bootstrap/alerts"
// @import "bootstrap/progress-bars"
// @import "bootstrap/media"
// @import "bootstrap/list-group"
// @import "bootstrap/panels"
// @import "bootstrap/responsive-embed"
// @import "bootstrap/wells"
// p@import "bootstrap/close"
// Components w/ JavaScript
// @import "bootstrap/modals"
// @import "bootstrap/tooltip"
// @import "bootstrap/popovers"
// @import "bootstrap/carousel"
// Utility classes
// @import "bootstrap/utilities"
// @import "bootstrap/responsive-utilities"
必要な物だけコメントアウトを外して使用する。
なおGridはBootstrapのものを採用している。
使わないもの
- scaffolding
CompassのリセットCSSで対応する。問題があったらコードを参照して、base/resetに追記していく。
_bootstrap_and_overrides.sassについて
bootstrapのCSSを上書きしたい場合は_bootstrap_and_overrides.sassに記述していく。
BEM
modifierのみを採用。
elementを採用しない理由は
- CSS名が冗長になること
- componentを意識してファイルを作成すればBEMのelementは不要なのではないかということ
よって、modifierのみを採用して btn--red, btn--blueのように使用する。
なお、実際のプロジェクトでは以下のようなmixinを入れて使う。
// BEM - Modifier
=m($name)
@at-root #{&}--#{$name}
@content
.btn
+m(red)
.btn--red {}
のようにして基準のmoduleの後ろに記述していく。
module(OOCSSの考えも採用)
moduleとはつまりcomponentのこと。
SMACSSのmoduleのことでもある。
OOCSSの考えも取り入れている。
参考:モジュール組み
採用理由
- 現状BEM(主にelement)を採用しているためにやたら冗長になっている
- componentを組み合わせて実装していきたいのでファイルごとに分けてしまいたい
ルール
class名はmoduleのファイル名と同じにする。
例えばファイル名がmodule/_module_hey.sass
だとすれば
.module_hey
// write here
のように書く。
moduleを1語で表せない場合はスネークケースにする
× .module-hello
× .moduleHello
◯ .module_hello
理由
- ハイフンはBEMのmodifierで使用しているため
- あとはルールを統一するために決めているため(好みでもあるが)
moduleは出来る限り2語までにする
× .module_hello_hey
◯ .module_hello
理由
- 単語が長いcomponentはそもそもcomponentとして適切ではない可能性が高いから
module以下は汎用的な出来る限りシンプルなクラス名をつける
.module
.title
font-size: 16px
.text
font-size: 12px
のようにする。
もちろん、 ※グローバルに.title, .textなどを絶対に使用しない前提※。
理由
- 冗長なクラス名を避けるため
- HTML/CSSで見慣れた命名にしたいため
OOCSSについての補足
OOCSSとは、
- 構造(structure)と見た目(skin)の分離
- コンテナ(container)と内容(content)の分離
のことである。
構造(structure)と見た目(skin)の分離とは
.btn // 構造
color: #333 // 色はskinだがdefault値は例外で指定しておく
height: 150px
padding: 5px 10px
text-align: center
width: 200px
.btn--red // 見た目(skin)
background-color: red
color: #fff
のように構造(structure)と見た目(skin)を分離する。
基本的にskinの部分がBEMのmodifierになると考えておけば迷わない。
コンテナ(container)と内容(content)の分離とは
要するにCSSのスタイル指定にHTMLのタグを使うなということ。
具体的に言うと、
.block
h2
ではなく、
.block
.title
のようにすればOK。
この原則に従うメリット
- クラスのついてない
<h2>
などのHTML要素はデフォルトスタイルを確約できる(普通のh2 {}
指定ということ。) - .blockの付与されたHTML要素は、
<h2>
でなくても同じ見た目を提供できる -
.block h2
のスタイルにデフォルトのh2
スタイルを適用したい場合にカスケードによるスタイル定義を行う必要がない
要するにHTMLとCSSの依存関係を避ける事ができるということ。
結構当たり前の話ですが、基本的にタグには指定しないようにする。
参考: [CSS] Object Oriented CSSを学んで綺麗なコードを書く
prefix
今回のコーディング規約で、prefixをつけるのは以下。まとめておく。
- .l-
- .u-
- .is-
.l-
layoutファイル内のCSSは.l-をつける。
具体的には.l-header, .l-footer, .l-containerなど。
以下のようにしてmoduleのように書いていく。
.l-header
.inner
.logo
.title
.u-
utilityに関するものは、.u-をつけてbase/_utility.sass内に書く。
.is-
JavaScriptで状態の変化を表す場合のみ使用。
.is-active のように使用。他の場合は基本的にBEMのmodifierを使用。
(もっとちゃんとしたルールを作る必要がありそうか)
画像
ファイル構成
形式
- 写真などの色数の多いもの : JPEG (.jpg)
- アルファチャンネルを持つもの : PNG24 (.png)
- アニメーションGIF : gif (.gif)
- それ以外 : PNG8 (.png)
理由:画像容量を少しでも軽くするため
スライスしておくときはデザイナに上のようにしてほしい旨を伝える。
共通ディレクトリの構成
└── images
└── common
└── modulename
└── icons
上記のようにモジュール単位で使われるものはmodule名でディレクトリを作成、モジュールをまたいで使用される画像は、iconsのように適宜ディレクトリを作成する。
例:
/common/icons/ アイコン
/common/btns/ ボタン
/common/arrows/ 矢印
/common/misc/ その他諸々画像
※ miscは「種々雑多な」という意味です。
画像の命名方法
特別な理由がない限り、基本的にそれを意味するものがわかるような命名をすればよい。
これも 2語以上になる場合はスネークケース(アンダースコア区切り) で書く。
/common/arrows/ の中なら
×: arrow_left.png
◯: left.png
/common/misc/の中なら
×: img_logo.png
◯: logo.png
理由
- それぞれディレクトリ構成から自明であるから
- ファイル名がシンプルになるから
余白について
余白(margin, padding)は予め4の倍数と決めてデザインしてもらうこととする。
_base.sassには以下の様な変数が定義されている。
$default_space: 4px
.hogehoge
padding: $default_space * 2
上のようにして使っていく。
この倍数の所は、デザイナーと相談して決めたところでプロジェクトによって変更があってもいいと思う。統一性のあるデザインにするのが狙いとフロント側がpxで迷わないようにするため。
フォントサイズ
フォントサイズは基本的に、
- 大見出し(32px)
- 中見出し(16px)
- 小見出し(14px)
- 普通より大きいテキスト(13px)
- デフォルトテキスト(12px)
- 一番小さいテキスト(10px)
として、上の5つを基本的に使う。
色
色は予め決めておいて、_base.sassに記述しておきそれを使うようにする。
テキストカラーや背景色、border colorなど。
border
色や太さ、radiusの大きさなど統一性を持たせる。
これも予め_base.sassにデフォルトの太さやradiusを書いておいてもいいかもしれない。
Modernizrについて
Modernizrとは、様々なブラウザに対応するためにHTMLにclassをつけてくれるライブラリ。
主にJavaScriptのAndroid対応のときのために使う。
Font Awesomeについて
アイコンにはFont Awesomeを使う。
画像でなくなるので、高解像度の対応も楽、ちょっとしたサイズ変更も容易になるため。
RailsだとHelperが用意されているので、基本的にはHelperを使って書く。
Rails
デフォルトで作成されるSassファイル
基本的に使わないで(削除してOK)逐一moduleの中に書いていく。
プロジェクト
実際にプロジェクトに入ったらどうするのか?
- 既存のmoduleだけで足らない場合はmodule/以下にファイルを作成する。_filename.sass のようにパーシャルにする
- all.sassに@import 'module/filename' を書く
基本的に新しく作るファイルはmodule関連が多くなると思う。
i18n
TODO: まだ段階的なもの。他に良いやり方がないか検討
共通となる文言は
locales/views/common/ja(en).yml
以下に書く。
それ以外は、ページごとに固有のものは、
locales/views/コントローラー名/アクション名/ja(en).yml
ファイルに書く。
common/ja.yml ファイルの書き方
ja:
common:
title: タイトル # タイトルタグが入る
navigation:
notifiaction:
notificationなどmodule名でも使われているものに関してはmodule名を使う。
終わりに
CSSは勝手に書いてもなんとかなっちゃうところがあるので、敷居が低く初心者でも簡単にコーディングができるが、そのような考えだと寿命の長いCSSは書けない。実際に導入してみて、こういうところはどうすればいいのか、というところがあったら質問するなりしてすぐ解決。必要ならルール追加をしてアップデートしていく。