CSSは敷居が低い分、書く人がある程度一貫性を意識して書かないと、すぐに破綻してしまうところがあって、いろんな方がいろんな手法を編み出していて、ベストプラクティス!と呼べるものが複数あるような状態だと思います。
自分としては、つまるところ書く人(達)が書きやすく管理しやすい方法であれば何でもいいと思うし、プロジェクトによっても答えは変わる話だと思うので、「これがサイコー!」みたいなことはなかなか言いづらいなあとおもっていたんですが、
最近は、複数人で制作するケースなどもあるので、あらかじめ「自分にとってのちょうどいい」を明文化して おけば、一緒に仕事をするときに「こいつは、こういうのがちょうどいいやつなんだな」と思ってもらえるかなと思い、まとめてみました。
2018-07-31更新: CodePenのサンプルをいくつか追加しました。
🚩基本姿勢
- 完璧な管理は目指さない。
- 管理のために迷う時間は少ないほうがいい。
- ルールはなるべく、単純でかつ少ないほうがいい。
BEM & ITCSS
_ BEM
- CSSの命名規則は様々なもまた好みも出る部分なので、これが正解!ということはなかなか言えない
- BEMは大分周知されてきていて、巷のCSSフレームワークなどでも採用されているケースが多い印象
.block-name--modifier
.block-name__element
こんな感じで、クラスの関係性を明確にすることで、重複するスタイルを減らして効率よく書いていきます。
ただ、.block-name__element
はあんまり使ってない
この書き方は、親のどこかに.block-name
を持つ要素につけるクラスですが、こういうのを厳密にやろうとすると
.block-name__element__inner { /* ... */ }
みたいになっちゃいがちなので、以下のような時だけ限定的に使ってます。
- その親子構造がスタイルに依存している時
- 親要素の中だけに影響範囲を絞ったスタイルを書きたい時(名前空間的な使い方)
▼ ITCSS
ITCSSがどういうものか、については下記の記事がとても参考になります。
簡単にいうと、
- CSSをいくつかのレイヤー(階層)にわける
- レイヤーごとに記載する内容を決める
- レイヤーは優先度(詳細度)が低い順に並べる
というルールでCSSを管理して、それぞれに役割と優先順位を設けることで、CSSを管理しやすくするというものです。
以下のような感じで分類します。
レイヤー名 | 内容 |
---|---|
⚙️Settings | Sassで使う変数を書くレイヤー。 |
🔧Tools | Sassで使うMixinを書くレイヤー。 |
💎Generic | リセットCSS(例:normalize.css) などを書くレイヤー |
📐Base | 要素セレクタに対するスタイルを書くレイヤー。見出し関係のデフォルトスタイルなど。scaffold。 |
⭐️Objects | 作っているサイトやアプリのデザインに依存しない汎用的に利用するスタイルを書くレイヤー。 プレフィックスは o-
|
✨Components | 作っているサイトやアプリのレイアウトに依存するスタイルを書くレイヤー。 プレフィックスは c-
|
💉Utilities | いわゆるヘルパークラス。唯一 !important の利用が許されるレイヤー。プレフィックスは u-
|
⭐️Objectsレイヤーについての補足
形やスタイルを持たないもの、概念的なクラスなど詳細度の低いスタイルを記述するレイヤーです。
Webサイト作る場合だと、そのサイト独自のスタイルではなく、自分がスタイルしていく上で汎用的に使うスタイルなどをここに書きます。
✨Componentsレイヤーについての補足
Objectsよりも抽象度の高いクラスを記述します。
コンポーネントという名前の通り、ここにも汎用的に利用するスタイルを書きます。
ただ、抽象度は前述の Objects とこの Components の2段階だけなので、必然的に汎用的に使用しない詳細度の高いクラスも書くことになりますが、それはそれで許容します。
(いま書いているクラスが、他の場所で使われる使われないかということを悩まない。基本的に「他の場所でも使われるかもしれない」という前提でスタイルする)
Webサイトをゼロから作る時は、サイト用のほとんどのスタイルはこのレイヤーに書くことになります。
JavaScriptが参照するクラスは .js-
プレフィックスをつける
CSSでスタイルするクラスと、JavaScriptで参照するクラスが同じだと、あとで見た目を変えるのにクラス名が変わっちゃう!みたいな時に、JavaScriptにまで影響を及ぼしてしまうので、.js-
プレフィックスをつけて明確に分けておくと安心。
状態変化はプレフィックス .is-
か、なるべくWAI-ARIA属性を使う
.c-nav--active
みたいにBEMのModifierをステートとして使うという手もありますが、上記の.js-
プレフィックスをつける理由と一緒で、JSの中のスタイル用のクラス名を直接書いてしまうと、後々の見た目でJSが意図した通り動かなくなったりする危険性があるので、CSSとJSの世界をつなぐ役割として.is-
プレフィックスクラスか、WAI-ARIA属性を使います。
.c-nav.is-active { /* アクティブ時のスタイル */ }
.c-nav[aria-selected='true'] { /* アクティブ時のスタイル */ }
マージンは上と左に統一する
マージンを上に統一する理由
縦方向の margin
は、兄弟要素同士では相殺されますが、例えば、margin
を持った片方の要素が別の親要素にはいっていて、かつその親要素が padding
や border
などをもってたり overflow: hidden
が設定されてたりすると相殺されません。
この、HTMLの構造によって、見た目上はとなりあって見えててもマージンが相殺したりしなかったりするのがちょっと面倒なので、極力マージンの相殺に頼らないスタイルを心がけています。
デザインにおける余白の意味
これは飽くまでデザイナー的な視点ですが、装飾的な余白を除き、デザインの機能としてのマージンや余白を考える時、それは「前の要素と関係度(距離感)」を表すケースが多いです。
例えば、通常、見出しは段落よりも大きな上マージンを持ちますが、これは単に見出しの文字が大きいからではなく、その見出しがそれまでの話を一旦区切って、新たな話題が始まるということを表現するために、段落よりも大きな余白をあけてる、という風に考えます。
なので、基本的には情報が流れる方向(上から下、左から右)とは逆側にマージンを持つほうが自分はしっくりきます。
その他、マージンについての考察は、以下の記事も参考になります。
マージンを左に統一する利点
横方向のマージンを左に統一するというのも、上記のデザイナー視点の余白の意味に起因してますが、横方向についてはコーディング面の利点も少しあります。
自分は段組みのようなレイアウトを組む時に、
- 余白は子要素が持つ
- 親要素にネガティブマージンを設定して、子要素のもつ余白を吸収
という方法をよく使います。
<div class="c-flex">
<div class="c-flex__item">
<div class="c-box"></div>
</div>
<div class="c-flex__item">
<div class="c-box"></div>
</div>
</div>
.c-flex {
display: flex;
flex-wrap: wrap;
margin: -1em 0 0 -1em;
>* {
flex: 1 1 auto;
width: 50%;
padding: 1em 0 0 1em;
}
}
.c-box {
padding: 1em;
background-color: gray;
height: 8em;
}
See the Pen ネガティブマージン(左の場合) by nibushibu (@nibushibu) on CodePen.
この時、もし右にネガティブマージンを持ってしまうと、そのネガティブマージン分、.flexの親要素が横に拡張されてしまい、.flexがウインドウ幅100%だったりすると、ページの右側に不要なスペースができてしまいます。
See the Pen ネガティブマージン(右の場合) by nibushibu (@nibushibu) on CodePen.
左にネガティブマージンをもてば、こうした不要なスペースができることを防げる、という利点があります。
ネガティブマージンを使えば、折り返しをもったレイアウトのマージンにも対応できる。
横一列のレイアウトだけを考慮するなら、以下のように :first-child
を使ってもいいと思います。
.c−flex {
display: flex;
flex-wrap: wrap;
margin-left: -1em;
>* {
flex: 0 1 auto;
margin-left: 1em;
}
>*:first-child {
margin-left: 0;
}
}
ただ、flex-wrap: wrap
にするなど、中のアイテムの折り返しがある前提で隙間の予約を作ろうとすると、:first-child
だけでは対応できません。
ネガティブマージンを使った方法なら、折り返しにも対応できるので、いまのところの最適解として、こういう方法を常用しています。
See the Pen ネガティブマージンを使った折り返しありのタイルレイアウト by nibushibu (@nibushibu) on CodePen.
※Gridレイアウトがちゃんと使えるようになれば…こういうネガティブマージンを使うことも必要なくなるかも。
内側に余白(padding
)を持つ囲みデザインの作り方
デザインするとこういう要素はよく出てきます。
この囲みの中の余白は、囲み自身が持つこともできるし、その中の要素に持たせることもできますが、
自分は、基本的に外の箱が自分自身の内部の余白(padding
)を持つべきだと思っています。
余白を持つ囲みの中に、上マージンをもった要素が入ってきたらどうするか
ただ、囲み自身に中の余白をpadding
としてもたせると、そのなかに要素が上マージンを持っている見出しなどを入れると、その見出しの上マージンと、囲みの余白、という二重の余白が上にできてしまいます。
この二重の余白を回避するために、余白を持った箱の中に上マージンを持つものがある場合には、下記のように最初の子要素だけ、上マージンをなくす指定を親要素に追加しています。
<section class="box fit">
<h2>これには上マージンがあるけどキャンセルされる</h2>
<p>この段落の上マージンはキャンセルされない</p>
</section>
.box {
border: 1px solid gray;
padding: 1em;
}
.fit>*:first-child {
margin-top: 0;/* 最初の子要素の上マージンを削除 */
}
See the Pen 中身のマージンをキャンセルするBOX by nibushibu (@nibushibu) on CodePen.
ちなみに、この方法を使うためには、縦マージンを持つスタイルの詳細度を10(クラス1つぶん)に保っておく必要があります。
参考: [CSSが効かない?詳細度を再確認しよう。](Qiita
https://qiita.com/flag_ryo/items/cf3512f9b4978c41d0e1)
スタイルの命名ルール
- 詳細度をなるべく低く。
- CSS内の記述場所でスタイルの優先度を管理できるようにする。
- 基本はクラスしか使わない(IDとか要素セレクタ使わない)
- クラス名は基本的に省略しない英単語
- 小文字だけ(大文字使わない)
- 単語はハイフンでつなぐ
- 単語が重複するときは
.foo
.foo-2
.foo-3
みたいに番号つける - 数字が付かないものは便宜上
.foo-1
と考える - 一つ目の段階から「これの2って作ることあるかなぁ」を考えるの面倒。
- 映画のタイトルとかも、大抵、続編ができてはじめてタイトル「〜2」って付くし!
- 影響範囲を限定したいスタイルには、頭に名前空間(プレフィックス)つける
CSSで実装できることはCSSでやる(JavaScript使う前に)
プルダウンメニューとかモーダルとか、JavaScript使いたくなるけど、CSSでできる要件であればなるべくCSSで実装する。
なんでもかんでもというわけじゃないけど、mouseover
とかmouseout
とかをいちいち設定するより:hover
ですんじゃうことは結構あるし、そのほうがシンプルに書ける。
モーダルなんかは、モノによっては:target
を使って、アンカーリンクでモーダルを表示、みたいなこともよくやる。
その他
p
タグはなるべくスタイルを設定せずプレーンな状態にしておく。
br
が改行を表すように、p
タグは段落を表す。テキストの文脈に依存して使われることが多いので、p
タグについては、フォントサイズなどのスタイルはなるべく親の要素を継承するようにする。