マークアップ歴の長い方からすれば大した話でもないのですが、フルスタックエンジニアの方々がフロントエンド開発に参入される昨今。CSS指定に関して、備わっていると良い感覚を共有できればと思ったので、メモを書きます。
MindBEMding が解決した課題
Vue.js のスコープ付き CSSについて見てみましょう。本家のドキュメントを引用します。
<style scoped>
.example {
color: red;
}
</style>
このスタイルは次の様にコンパイルされます。
<style>
.example[data-v-f3f3eg9] {
color: red;
}
</style>
この仕組みにより、global汚染と戦うコンポーネントの CSS は、private な名前空間を得ることができました。
MindBEMding では命名規則という人海戦術で「一意な名前空間」を確保し、指定の分散を防ぎました。この名前空間の確保のために最も浸透していたアプローチは「ファイル名と同じ Block 名称にする」というものです。scss では、次の様な記法がとられました。
.MoleculesExample {
&__elem { color: red; }
}
.MoleculesMyExample {
&__elem { color: blue; }
}
Molecules に関することは Molecules ディレクトリに全ての指定ファイルが含まれるため、競合することがないのです。ここではBlock名称に「Molecules」という接頭辞を与えていますが、実際には**略称接頭辞を用いて「責務分類」**し、競合することのない様に工夫がなされました。
今時で言うなら「Atoms なら .a-
」「Molecules なら .m-
」「Organisms なら .o-
」という風に考えれば、理解できるかと思います。
Component 指向に息づくリファクタ優位性
さて、次の様な Component があったとします。ある時期 &__list
および &__listItem
の要素が、他のコンポーネントでも「再利用可能」であることが判明しました。これを例に、いかにリファクタがし易いものであったかを解説します。
<div class="Block">
<h2 class="Block__title">title</h2>
<ul class="Block__list">
<li class="Block__listItem">hello</li>
<li class="Block__listItem">world</li>
</ul>
</div>
この時のスタイル指定は、次の様なものです。
.Block {
&__title {
font-size: 2.4rem;
}
&__list {
width: 240px;
background: #fff;
}
&__listItem {
font-size: 1.6rem;
}
}
分割・共通化された Component は、次の様になりました。
<div class="Block">
<h2 class="Block__title">title</h2>
<CommonList />
</div>
<ul class="CommonList">
<li class="CommonList__item">hello</li>
<li class="CommonList__item">world</li>
</ul>
この html と対になる様に、scss も二つのファイルに分割されます。特筆すべきは**「&__list
および &__listItem
は、そのまま指定を別ファイルに移してしまえば良い」**ということです。
.Block {
&__title {
font-size: 2.4rem;
}
}
.CommonList {
width: 240px;
background: #fff;
&__item {
font-size: 1.6rem;
}
}
&__list
が Element から Block に昇格、&__listItem
が&__item
に改名されている点に注意してください。この様なコンポーネントリファクタリングは近代フロントエンドでは頻繁に発生しますが、MindBEMding が提唱された時期に、この土壌は整っていたと言えます。
レイアウトの責務は親が持つ
上記のリファクタ時に直面する問題は恐らく、この共有 Component が利用される親 Component において「幅が違う」などのパターンです。
幅などのレイアウト指定が入ってしまうと、汎用性に欠けた Component になってしまいます。これは**「親Componentがレイアウト責務を担う」**ということを意識すれば、解決できることです。新たに、&__listContainer
という Element を設け、そこにレイアウトの責務を持たせます。
<div class="Block">
<h2 class="Block__title">title</h2>
<div class="Block__listContainer"> <!-- New -->
<CommonList />
</div>
</div>
<ul class="CommonList">
<li class="CommonList__item">hello</li>
<li class="CommonList__item">world</li>
</ul>
次の様に、レイアウトの責務が適切な場所に移動されました。.CommonList
の様な汎用性の高いコンポーネントは、ブロックレベル要素として親要素の幅いっぱいに広がります。レスポンシブレイアウトに柔軟に対応できる指定とも言えます。
.Block {
&__title {
font-size: 2.4rem;
}
&__listContainer {
width: 240px; /* ADD */
}
}
.CommonList {
// width: 240px; /* REMOVE */
background: #fff;
&__item {
font-size: 1.6rem;
}
}
MindBEMding の話がメインでしたが「親がレイアウトの責務をもつべき」という考えは、近代 Component 設計でも重要なチェック項目です。