※注意:このCSSアーキテクチャは策定中です。
まとめきれていないので、アーキテクチャの領域を超えてガイドライン的な内容も含まれています。
さいきんDIY動画にハマっていて、2x4工法などを見ていて、その優れた統一規格の名前にあやかって、FramingCSS なんて名前を考えてみました(笑)
RSCSSの命名規則をベースとし、
SMACSS, FLOCSS, BEM, Atomic Design, Bootstrap, Foundation
などを参考にしています。
カテゴリ
- Base
reset.css や normalize.css などを用いたブラウザのデフォルトスタイル初期化のほか、サイトの基礎となるタイポグラフィを定義します。 - Utility
余白やフォントサイズ、色など、単体(またはいくつか)のプロパティ調整に使用するクラスを定義します。
調整用のクラスであり、Utility自身は構造を持ちません。
原則として全てのプロパティに !important を設定します。 - Block
サイトを構築するパーツ群を定義します。
パーツはその役割や粒度によって次のように分類されます。- Helper
clearfix など単独で使用することのない、他のパーツを補助するためのクラスを定義します。
汎用的なアニメーション・エフェクトやスクロールオフセットなど、パーツの補助機能追加に使用できます。
Utility と似ていますが、こちらには !important を使用しません。
原則として独自の Element を持つことはできませんが、擬似要素を持つことは可能です。 - Layout
ページを構成するベースレイアウトや、グリッドシステム、マルチカラムレイアウト用パーツのほか、ヘッダーやフッター、サイドバーなど、ページ単位で唯一の存在となるブロックを定義します。
子孫に全種の Block を持つことができます。
※ただし、ヘッダーやフッターなどの独立性の高い機能ユニットは一度作成したら運用で変更を加えることがあまりないため、構成に汎用的パーツの Layout や Compnent や Module を使うことは最小限に留め、独自の sub-Block パーツを定義してほかの Block パーツに依存しないようにした方が良いでしょう。 - Compnent
ボタンやサーチフォームのようなUI、アイコン、見出しのようなデザインタイポグラフィなど最低限の機能を持つパーツ群を定義します。
汎用性を持たせるため、アイコンのような例外を除き、固有の幅を持つことは出来る限り避けます。
子孫に Component を持つことができます。
ただし、自身は中にネストした Component に依存しない独立した構造でなくてはなりません。 - Module
ひとまとまりの機能を持ったパーツを定義します。
例えば Card や Panel などのコンテンツモジュール、Tabs や pagination などのナビゲーションモジュール、Hero Image や Slide Gallery などのビジュアルモジュール、Login Panel や集客成果のための CTA モジュールなど、明確な機能を持ったユニットモジュールです。
子孫に全種の Block を持つことができます。
ただし、自身は中にネストした Block に依存しない独立した構造でなくてはなりません。
(例えば、ネストした Component を削除したり別パーツに置き換えた途端にレイアウトが破綻するなど起きないようにします)
- Helper
- unique Block
単一ページまたは特定ディレクトリ配下など、限定された範囲でのみ使用する固有のブロックパーツを定義します。
上記Blockのサブカテゴリ(Helper,Layout,Compnent,Module)すべての性質を独自に定義できます。
※ただし、レイアウトやデザインの都合で独自ブロックを定義する必要性が高い場合を除き、
まずは、汎用ブロックに Modifier を追加してバリエーションを増やしたり、Utility で調整することを検討してください。
Block パーツの構成要素
- Block
パーツの起点コンテナとなるブロックです。
自身単体もしくは内部パーツの組み合わせにより、目的に応じた機能・意味を持ちます。 - Element
Block に属する構成パーツです。
Element は属する Block 内でのみ使用可能で、必ずBlockの子・子孫要素として定義します。
Block から独立して使用することはできません。 - Modifier
BlockまたはElementパーツのフラグです。
既存の Block ・ Element のバリエーションとしてスタイルを変化させたい場合、外観や状態・動作を定義します。
Block や Element のクラスとセットで定義し、単体で使用することはできません。
また、デザインの見た目が似ていても大きく構造や機能の違うものは Modifier ではなく、別の新たな Block として定義してください。
命名規則
Utility の命名
prefix としてクラス名の先頭にアンダースコアを付与します。
クラス名はプロパティに応じた Emmet CSS ショートハンドで命名します。
※プロパティ値の書き方は「連番と値」の項を参照してください。
例
/* m0 */
._m0 { margin: 0 !important; }
/* mr:a */
._mr-a { margin-right: auto !important; }
/* d:ib */
._d-ib { display: inline-block !important;}
マージンやパディングを上下・左右のセットで定義する場合は X軸・Y軸 で命名します。
/* X axis */
._mx-a { margin-right: auto !important; margin-left: auto !important;}
/* Y axis */
._py10 { padding-right: 10px !important; padding-left: 10px !important;}
Block の命名
Block の命名とセレクタ
Block は .block-name { ... }
のように、ハイフンで連結した最低2つの単語で命名します。
例
.like-btn { ... } /* いいねボタン */
.search-form { ... } /* 検索フォーム */
.article-card { ... } /* ニュース記事カード */
.m-hero-visual { ... } /* 名前空間(prefix)付きBlock */
Element の命名とセレクタ
Element は単語ひとつで命名します。
ハイフンなどの連結記号は使用しません。
.search-form > .field { ... }
.search-form > .action { ... }
2つ以上の単語が必要な場合は、ハイフンやアンダースコアを付けずに連結します。
.profile-box > .firstname { ... }
.profile-box > .lastname { ... }
.profile-box > .avatar { ... }
定義のする際は、子セレクタ >
を使用することを推奨します。
影響範囲が限定されるため、.ttl
のような単純な名前でもバッティングを避けることができます。
/* 推奨 */
.article-card > .ttl { ... }
.figure-box > .ttl { ... }
/* なるべく避ける */
.article-card .ttl { ... }
.figure-box .ttl { ... }
子セレクタが無い場合、 Block がネストされた際にスタイル競合し、意図しない表示となる可能性があります。
<div class="article-card">
<h3 class="ttl">カードのタイトル</h3>
<figure class="figure-box">
<p class="ttl">図のタイトル</p><!-- スタイルが競合する可能性アリ -->
<img src="" alt="">
</figure>
</div>
Element のセレクタ階層が深くなる場合、属する Block 名を継承した sub-Block として定義ます。
※詳しくは「詳細度」の項を参照してください。
.article-card > .inner > .group > .ttl { ... } /* 避ける */
.article-card-ttl { ... } /* 推奨 */
Modifier の命名とセレクタ
Modifier クラスの prefix として名前の先頭に -
ハイフン記号を付けます。
必ず Block や Element のクラスとセットにし、単体で定義することは禁止です。
/* Block */
.like-btn.-wide { ... }
.like-btn.-short { ... }
.like-btn.-disabled { ... }
/* Element */
.like-btn > .btn.-wide { ... }
.like-btn > .btn.-short { ... }
.like-btn > .btn.-disabled { ... }
/* NG */
.-wide { ... }
.-short { ... }
.-disabled { ... }
prefix とスコープ
Global scope
サイト全体共通で読み込むグローバルCSSです。
ここで定義する Block には以下のprefixを付与します。
Helper | Layout | Component | Module |
---|---|---|---|
.h-* | .l-* | .c-* | .m-* |
/* Helper */
.h-hover-effect {
opacity: 1;
transition: opacity 0.2s ease;
}
.h-hover-effect:hover {
opacity: 0.8;
}
/* Layout */
.l-grid-container {
display: grid;
}
.l-grid-container.-cols2 {
grid-template-columns: 1fr 1fr;
}
.l-grid-container > .cell.-colcpan2 {
grid-column-end: span 2;
}
/* Component */
.c-txt-normal {
font-size: 16px;
line-height: 1.5;
}
.c-btn-normal {
display: block;
width: 100%;
margin: 0;
padding: 2px 8px;
border: 0;
color: #fff;
background: #33a;
line-height: 1.25;
cursor: pointer;
text-decoration: none;
outline: none;
appearance: none;
}
/* Module */
.m-tabs-menu {
display: flex;
margin: 0;
padding: 0;
list-style: none;
}
.m-tabs-menu > li {
flex: 0 1 33.3333%;
}
Category scope (unique Block)
特定ディレクトリ範囲でのみ使用する共通スタイルには、カテゴリに応じた任意の prefix を付与します。
prefix は自由に設定できますが、他と重複することのない固有のものにしてください。
文字数にも制限はありませんが、影響範囲となるディレクトリ名や、1〜3字程度の少ない文字数にしておくと prefix であることが明示的に判別しやすくなります。
/* directory name prefix */
.inquiry-block-name { ... }
/* abbreviation name prefix */
.inq-block-name { ... }
Single scope (unique Block)
単独ページ専用のCSSファイルまたはHTML内の <style>
タグに定義する Block には prefix を付けず命名します。
.block-name { ... }
suffix
レスポンシブのブレークポイントの識別に suffix を使用します。
device | suffix example | remarks |
---|---|---|
All | .class-name |
ブレークポイントの指定なし |
Smart phone | .class-name-sp |
スマホ |
Tablet | .class-name-tb |
タブレット |
Desktop | .class-name-pc |
デスクトップPC |
TV | .class-name-tv |
フルワイド表示を想定しています |
._d-n { displey: none !important }
@media (max-width: 576px) {
._d-n-sp { displey: none !important }
}
@media (min-width: 577px) and (max-width: 991px) {
._d-n-tb { displey: none !important }
}
@media (min-width: 992px) and (max-width: 1399px) {
._d-n-pc { displey: none !important }
}
@media (min-width: 1400) {
._d-n-tv { displey: none !important }
}
※これはサンプルです。ブレークポイントはプロジェクトに応じたサイズを設定してください。
単語の省略
class名で使用する単語の省略は任意です。
省略する場合は命名にブレが発生しないよう、あらかじめプロジェクトごとに対応表を作っておきます。
事前に取り決めのない単語については基本的に省略せずに、誰が見ても明示的で分かりやすい命名を心がけましょう。
例
省略形 | 単語 |
---|---|
ttl | title |
txt | text |
sect | section |
連番と値
パーツの連番を付与する場合はハイフンに番号、値を示す数を付与する場合はクラス名の後ろに値となる数字を繋げます。
単位がpxの場合は数字のみ、パーセント値には p
を付与します。
それ以外の単位はそのまま値の後ろに付与します。
ネガティブ(マイナス)値の場合は数字の後ろに n
を付与します。
/* 連番 */
.block-name > .item-01 { ... }
.block-name > .item-02 { ... }
/* 数値 */
._fz16 { font-size: 16px !important;}
._w100p { width: 100% !important;}
._m10n { margin: -10px !important;}
._ml50pn { margin-left: -50% !important;}
少数値を持つクラス名を定義する場面はかなり限定的かと思いますが、ルールではなくアイティアとしてだけ記しておきます。
/* 数の頭に0を付けることで少数以下を表現 (1の位を表現できないことに注意) */
._lts05 { letter-spacing: 0.5px !important;}
/* 数字をハイフンで繋ぐことで少数点を表す (連番ルールとバッティングしてしまう) */
._lts0-5 { letter-spacing: 0.5px !important;}
._lts1-5 { letter-spacing: 1.5px !important;}
/* バックスラッシュを使い . をエスケープ
HTML: <p class="_lts0.5"> ... </p>
(HTML側の記述は分かりやすいですが、通常は使用できない文字なので推奨はしません) */
._lts0\.5 { letter-spacing: 0.5px !important;}
/* (暫定)慣例として単位を付けないことが多い line-height は例外的に単位なしとみなす */
._lh2 { line-height: 2 !important;}
値をキーワードで記述
大小のキーワードを用いるときは以下を使用します。
サイズ | infix |
---|---|
-3xs | |
-2xs | |
-xs | |
小さい | -sm |
基準値・中間値 | -md |
大きい | -lg |
-xl | |
-2xl | |
-3xl |
相対サイズ(relative-size)キーワードを用いる際は上のキーワードの頭にr
を付けます。
プロパティ指定時は混乱を避けるため単位は合わせてください。
※ こちらはアイディアの一つです。相対サイズには ._fz-80p
のようなパーセント指定を使用する方が明示的です。
/* NG */
._fz-rsm { font-size: 0.8em !important;}
._fz-rlg { font-size: 120% !important;}
/* OK */
._fz-rsm { font-size: 0.8em !important;}
._fz-rlg { font-size: 1.2em !important;}
/* OK */
._fz-rsm { font-size: 80% !important;}
._fz-rlg { font-size: 120% !important;}
詳細度
Utility を除き、 !important
の使用は原則禁止です。
id セレクタも原則禁止とします。やむを得ず id をセレクタに利用したい場合は属性セレクタ [id=*]
の使用を検討してください。
セレクタ階層
RSCSSの性質上、子セレクタを多用することになりますが、3〜4階層程度に留めてください。
階層が深くなる場合は、そのBlockに属する sub-Block の定義を検討してください。
.block-name { ... }
.block-name > ul { ... }
.block-name > ul > li { ... }
.block-name > ul > li > a { ... }
/* ↓ 階層が深くなりそうであれば Element を sub-Block として定義する */
.block-name { ... }
.block-name > ul { ... }
.block-name-item { ... } /* */
.block-name-item > a { ... }
タグセレクタの利用について
Element にはなるべくクラス名を付けてください。
タグセレクタは禁止ではありませんが、使用可能なタグが限定されるため汎用性が犠牲になります。
後から別のタグに置き換える必要が発生したり、新たな Element が追加となった際に、スタイルが適用されなかったり、同じタグを利用することが難しくなることを想定し定義してください。
例えば、<p>
など汎用性・使用頻度の高いタグを定義してしまうと、後から別のスタイルで文を追加することになった際に思いがけない負担となることがあります。
.m-login-box { ... }
.m-login-box > p { ... }
.m-login-box > a { ... }
<div class="m-login-box">
<p>ログインしてください</p>
<a href="#login">LOGIN</a>
</div>
/* ↓ 複数のページで使用されているが、あるページだけHTMLの変更が必要になった */
<div class="m-login-box">
<p>ログインしてください</p>
<button type="button">LOGIN</button> /* タグを変えたらスタイルが適用されない */
/* ここに新たなデザインの p要素を追加したい */
</div>
その場所でしか使用されていない完全固有パーツであればCSSを書き換えることは容易ですが、さまざまな場所で使用されている汎用 Block のスタイルを書き換えるには、影響範囲を調査し class を共有しているパーツに影響を与えないよう変更を加えなくてはなりません。
これはサイト規模やパーツの利用状況によってはかなり対応が難しくなりますので、汎用性が高いパーツには予めクラス名を付けておくことが望ましいです。
.m-login-box { ... }
.m-login-box > .lead { ... }
.m-login-box > .login { ... }
.m-login-box > .note { ... } /* ← 追加が容易です */
<div class="m-login-box">
<p class="lead">ログインしてください</p>
<button type="button" class="btn">LOGIN</button>
<p class="note">ログインには会員登録が必要です</p>
</div>
一方、<table>
や <ul>
など、子のタグ構成が明確で原則変動することのない構造を持つ Blockパーツ にはタグセレクタを使用することができます。
※パフォーマンスへの悪影響が言及されることが多いですが、実際のところ、jsなどと比べればごく僅かな違いでしかないようです。
タグセレクタを使用することで、詳細度を低く保ちながらHTML側の記述も少なく済みます。
<ul class="m-local-links">
<li><a href="#link-01">foo</a></li>
<li><a href="#link-02">bar</a></li>
<li><a href="#link-03">baz</a></li>
</ul>
.m-local-links { ... }
.m-local-links > li { ... }
.m-local-links > li > a { ... }
/* class名を付ける場合、このような書き方はNG */
.m-local-links > li > a.link { ... }
/* OK */
.m-local-links > li > .link { ... }
特に <table>
のような複雑な構造を持つ要素において、詳細度を低く保ちながらネストした要素にまでスタイルが適用されないよう影響範囲を限定するのに効果的です。
また、全称セレクター *
は詳細度に影響を与えないため、深い構造を持つ Block で非常に有用です。
※ただし、全称セレクタの多用は禁物です。パフォーマンスへの影響が微弱とはいえ積み重なれば影響も大きくなります。乱用は避けましょう。
.m-table-normal {
table-layout: fixed;
min-width: 100%;
border-collapse: collapse;
border-spacing: 0;
}
.m-table-normal > * > * > th,
.m-table-normal > * > * > td {
padding: 4px 8px;
border: 1px solid #ccc;
}
.m-table-normal > thead > * > th {
background: #ccc;
}
.m-table-normal.-zebra > * > *:nth-child(even) {
background: #eee;
}
ベンダー/サードパーティ製CSSの利用について
プラグインなどに付属するプロジェクト外のCSSを利用する場合、バージョンの互換性を考慮してプラグイン側の命名を優先します。
ただし、命名規則が混在しないようファイルを分けるなどの工夫が必要です。
カスタマイズが必要な場合、プラグイン付属CSS側に追加するか、プラグイン側の命名を踏襲したカスタムCSSを作成してください。
(ただし、ライセンスで改変禁止されている場合はライセンスに従ってください)
レスポンシブの記述について
CSSはモバイル ファーストで定義します。
ブレークポイント別のスタイル切替えについては、下記3パターンのいずれかで記述します。
どのパターンをメインに採用するかはプロジェクトの特性に応じて選択するのが良いと思います。
パターンを組み合わせることもできますが、管理が煩雑になるので注意してください。
1. オーバーライド
パーツの基本となるモバイル(スマホ)のスタイルを定義してからメディアクエリで必要なプロパティをオーバーライドします。
ブレークポイントが少ないプロジェクトでは管理がしやすいと思います。
.block-name {
width: 100%;
color: #333;
}
@media (min-width: 768px) {
.block-name {
width: 50%;
}
}
2. 固有定義
共通プロパティ定義後にメディアクエリでそれぞれ固有のプロパティを定義します。
無駄なオーバーライドが発生しないのでパフォーマンス的に有利ではありますが、実際のところ誤差程度でしかないようです。
オーバーライド式で記述すると、先に定義されたプロパティが不要な場合は打ち消さなければならないので、ブレークポイントが多いプロジェクトなどでは、それぞれで固有プロパティを定義するのが良いと思います。
.block-name {
color: #333;
}
@media (max-width: 767px) {
.block-name {
width: 100%;
}
}
@media (min-width: 768px) {
.block-name {
width: 50%;
}
}
3. クラス分離
suffix を使いクラスを分離して定義します。
重複が多くなり共通プロパティの変更の際も個別に修正しなければなりませんが、それぞれの削除や分離が容易です。
@media (max-width: 767px) {
.block-name-sp {
width: 100%;
color: #333;
}
}
@media (min-width: 768px) {
.block-name-pc {
width: 50%;
color: #333;
}
}
Utility ではこのパターンを使用します。
._d-n { displey: none !important }
@media (max-width: 767px) {
._d-n-sp { displey: none !important }
}
@media (min-width: 768px) {
._d-n-pc { displey: none !important }
}
参考
おわりに
読んでいただき、ありがとうございます。
まだ内容を詰めきれていませんが、気長に修正していきたいと思います。
以上、ありがとうございました。