はじめに
CSS設計の類はもっとカスタムデータ属性を活用するべきではの内容の補足・補強を含めて「こういう書き方もありなのではないか?」程度の内容です。
BEAM
BEMの考え方を踏襲し、block__element[data-c-modifier="modifier"]
という記法をとります。
属性を用いてmodifierを設定しますから、仮にBEAM (Block Element Attribute-Modifier)とでも呼びましょう。
基本構文
基本的にはBEMの考え方を継承しますが、CSSのクラス名によるModifierを使わず、代わりにデータ属性としてdata-c-modifier
を利用します。
基本構文は次のようになります。
<div class="block" data-c-modifier="modifier">
<div class="block__element">
...
</div>
</div>
.block {
display:block;
font-size: 12px;
&__element {
font-weight: normal;
}
}
.block[data-c-modifier="modifier"] {
font-size:24px;
.block {
&__element{
font-weight: bold;
}
}
}
@extend
を使って継承を行う必要はありません。
なぜなら.block[data-c-modifier="modifier"]
はすでに.block
の一部だからです。
状態の指定
表示やアニメーションの進行状況など、要素に対する状態の指定にはカスタムデータ属性を用います。
<div class="block" data-c-modifier="modifier" data-is-active="true"></div>
有効か無効かを判断する場合、値は必ずBooleanで与えるようにしてください。
上記のHTMLに対してスタイルはつぎのようにして与えます。
.block {
// 指定がない場合は常に表示するようにしましょう。
display: block;
&[data-is-active="true"] {
display: block;
}
&[data-is-active="false"] {
display: none;
}
}
状態の指定のサンプル
下はカスタムデータ属性を用いたトグルボタンの実装のサンプルになります。
jsでの操作は状態を変更するだけで済むようになり、jsから動きを分離することが可能になります。
<button>Toggle</button>
<div class="block" data-c-modifier="bordered" data-is-active="true">
fade
</div>
var BlockElement = document.querySelector(".block");
document.querySelector("button").addEventListener("click", function(e) {
BlockElement.dataset.isActive = !(BlockElement.dataset.isActive === "true");
});
状態を拡張する
カスタムデータ属性を用いることで、クラスを拡張することなく要素の状態を設定することができます。
下は要素のフェードイン・アウトのcssのサンプルです
[data-u-animation-fade="true"] {
display:block;
transition:visibility 0s linear 0.5s, opacity 0.5s ease;
&[data-is-active] {
display: block;
}
&[data-is-active="false"] {
opacity: 0;
visibility: hidden;
}
&[data-is-active="true"] {
transition-delay:0s;
opacity: 1;
visibility: visible;
}
}
このコードは、上で挙げたblockに[data-u-animation-fade="true"]
を追加するだけで動作します。
<button>Toggle</button>
<div class="block" data-is-active="true" data-u-animation-fade="true">
fade
</div>
このように、BEAMではユーティリティクラスなどの適用される範囲をより自由に操作することができます。
要素の小要素、孫要素
BEAMでは、HTMLの木構造とCSSの構造をできるかぎり一致させることを推奨します。
小要素へのスタイルは、block__element-element
のような形式で指定します。指定できる要素は孫要素までとします。
それ以上ネストが深くなる場合は、コンポーネントをさらに分割できないか検討してください。
要素指定のサンプル
下に、TwitterのSummaryカードのようなUIの実装をサンプルとしてあげます。
<div class="card">
<div class="card__header">
<div class="card__header-icon">
<img src="icon.png" />
</div>
<div class="card__header-author">
<strong>宮沢賢治</strong>
<small>@kenji_miyazawa</small>
</div>
</div>
<div class="card__body">
<div class="card__body-text">
あの岩手のすきとおった風、夏でも底に冷たさをもつ青いそら、うつくしい森で飾られた盛岡市、郊外のぎらぎらひかる草の波。
</div>
<time class="card__body-dateline">2016/03/06</time>
</div>
</div>
.card {
display: block;
border: 1px solid #e1e8ed;
padding: 16px;
border-radius: 4px;
max-width: 520px;
&__header {
height: 48px;
width: 100%;
display: flex;
&-icon {
width: 48px;
> img {
width: 100%;
height: auto;
vertical-align: bottom;
}
}
&-author {
> strong {
display: block;
font-weight: bold;
font-size: 14px;
letter-spacing: 0.01em;
}
> small {
display: block;
font-size: 12px;
font-weight: normal;
}
}
}
&__body {
&-text {
font-size: 16px;
font-weight: normal;
}
&-dateline {
font-weight: normal;
font-size: 12px;
}
}
}
基本的な構文は以上です。
ファイル・ディレクトリ構成案
BEAMを有効的に扱うために、次のようなディレクトリ構成を推奨します。
また、HTMLから場所を特定しやすいよう、ファイル名とセレクタの名前を極力一致させることが望ましいです。
modifierしたセレクタをファイルで分割する場合、ファイル名にblock--modifier
とつけると良いでしょう
├── foundations
│ ├── _base.scss
├── components
│ ├── _card.scss
│ ├── _button.scss
│ └── state
│ └── _data-c-is-active.scss
├── layouts
│ └── _site-header.scss
├── utilities
│ ├── _data-u-animation.scss
│ └── _data-u-grid.scss
└── pages
├── _page.scss
├── _page--frontpage.scss
└── _page--subpage.scss
Foundations
Reset.cssやNormalize.cssなどを用いたブラウザのデフォルトスタイルの初期化や、プロジェクトにおける基本的なスタイルを定義します。
ページの下地としての全体の背景や、基本的なタイポグラフィなどが該当します。
Component
再利用可能な最小単位のモジュールです。
一般的によく使われるパターンであり、例えばBootstrapのComponentカテゴリなどに見られるbuttonなどが該当します。
出来る限り、最低限の機能を持ったものとして定義されるべきであり、それ自体が固有の幅や色などの特色を持つことは避けるのが望ましいです。
Componentの内部でさらにComponentを持つ場合、そのファイルはLayoutに移動させるべきです。
Layouts
Componentを複数配置してモジュール化したい場合に利用します。
カスケーディングが許容されるComponentだと思ってください。
LayoutでComponentのレイアウトや見た目を操作する場合、極力modifierを指定しないようにすることが望ましいです。
Pages
ページごとのLayoutやComponentのレイアウトやマージンを定義します。
使い回しができない最大の単位です。
Utilities
アニメーションやグリッドシステム、Clearfixなど汎用的に扱うパーツを格納します。
Componentにdata属性で渡すだけで適用できることが推奨されます。
上で挙げた[data-u-animation]
などが該当します。
プレフィクス
jsとの名前の衝突の回避や、状態がどこにあるのかを明確にするためにカスタムデータ属性にはプレフィックスをつけることを推奨します。
例
- Component - [
data-c-*
] - Layout - [
data-l-*
] - Page - [
data-p-*
] - Utility - [
data-u-*
]