BEMについて今学んだか、最初からそれを習得しているかは関係ありませんが、既にこのような有用な方法論を高く評価しているかもしれません。 BEMが何かわからない場合は、このCSS方法論の基本的な理解を前提とする用語を使用するため、この記事を続ける前にBEM Webサイトでそれについて読むことをお勧めします。
この記事は、すでにBEMを使用しており、より効率的に使用したい人と、BEMについてさらに学びたい人を対象としています。
今、私は物事がどのように呼ばれるかについて疑いはありません。 長い間私を押しのけてきた唯一のものは、い構文です。 私の中のデザイナーは、ひどい二重アンダースコアとハイフンで私のシックなレイアウトを乱雑にしたくありませんでした。
私の開発者はそれを実用的に見ていた。 そして最終的に、ユーザーインターフェイスを構築するモジュール式の方法は、「しかしそれだけでは十分ではありません」と述べた私の脳の右側を上回りました。 このような場合、外部フォームよりも機能を優先します。 いずれにせよ、空の話で十分です。 私が苦労した10の問題と、それらを解決するためのヒントを紹介します。
1.「何をすべきか」「孫」「セレクター(だけでなく)」
明確にするために、2レベルにネストされた要素を参照する必要がある場合は、孫セレクターが使用されます。 これらの悪い男の子は私の人生の呪いであり、これが人々がすぐにBEMを嫌う理由の1つだと確信しています。 例を挙げます。
<div class="c-card"> <div class="c-card__header"> <!-- … --> <h2 class="c-card__header__title">Title text here</h2> </div> <div class="c-card__body"> <img class="c-card__body__img" src="http://some-img.png" alt="description"> <p class="c-card__body__text">Lorem ipsum dolor sit amet, consectetur</p> <p class="c-card__body__text">Adipiscing elit. <a href="/somelink.html" class="c-card__body__text__link">Pellentesque amet</a> </p> </div> </div>
お気づきかもしれませんが、名前はすぐに手に負えなくなる可能性があり、ネストが増えると、要素のクラス名がugくなります。 c-card
と呼ばれる短いブロックbody
とtext
、 link
を使用しましたが、元のブロック要素の名前がc-drop-down-menu
場合はどうなるか想像してみてください。
二重アンダースコアは、セレクタ名に一度しか表示されないはずです。 BEMは_--
ではなく____--
略です。 また、要素のマルチレベルの命名を避けます。 偉大な偉大な偉大な孫レベルを取得する場合は、おそらくコンポーネントの構造を確認する必要があります。
BEMの命名規則はDOMに厳密には結び付けられていないため、ネストされた要素がいくつレベルにあるかは関係ありません。 命名規則は、まず、最上位レベル(この場合はc-card
のブロックとの関係を確認するのに役立ちます。
同じカードコンポーネントをどのように考えるか:
<div class="c-card"> <div class="c-card__header"> <h2 class="c-card__title">Title text here</h2> </div> <div class="c-card__body"> <img class="c-card__img" src="http://some-img.png" alt="description"> <p class="c-card__text">Lorem ipsum dolor sit amet, consectetur</p> <p class="c-card__text">Adipiscing elit. <a href="/somelink.html" class="c-card__link">Pellentesque amet</a> </p> </div> </div>
これは、ネストされたすべての要素がカードブロックによってのみ影響を受けることを意味します。 また、テキストと画像をc-card__header
たり、セマンティック構造を壊さずに新しいc-card__footer
要素を導入することもできます。
2.「使用するアイテムはどれですか?」
この時までに、私の例でc-
が使用されていることに気付いているかもしれません。 このプレフィックスは「コンポーネント」を表し、BEMクラスのすべての名前の基礎となります。 このアイデアは、コードの可読性を向上させるハリー・ロバートの命名技法から借用されています。
私が採用したシステムと、この記事の例に表示される多くのプレフィックス:
種類 | プレフィックス | 例 | 説明 |
---|---|---|---|
成分 | c-
| c-card
、 c-checklist
| アプリケーションの基礎を形成し、個々のコンポーネントのすべての化粧品が含まれています。 |
レイアウトモジュール | l-
| l-grid
、 l-container
| これらのモジュールには外観はありませんc-
コンポーネントの配置とアプリケーションのレイアウトの構築にのみ使用されます。 |
ヘルパー | h-
| h-show
、 h-hide
| これらのクラスには1つの関数があり、特定!important
を高めるために !important
(主に位置決めまたは可視性に使用されます。) |
州(州) | is-
、 has-
| is-visible
、 has-loaded
| これらは、 c-
コンポーネントが持つ可能性のあるさまざまな状態を示しています。 詳細については、問題6を参照してください。 |
Javascriptフック | js-
| js-tab-switcher
| JavaScriptの動作がコンポーネントにバインドされていることを示しています。 スタイルはそれらに関連付けられていません。 スクリプトの操作を容易にするためにのみ使用されます。 |
そのような名前は、コードの可読性を信じられないほど向上させることがわかりました。 BEMに夢中になれないとしても、このテクニックは間違いなく覚えておく価値があります。
また、品質保証テストの場合はss-
、サーバー側(サーバー側)のさまざまなフックの場合はss-
など、他のプレフィックスを使用することもできます。 しかし、上記のリストはすでに良いスタートであり、このテクニックに慣れてから新しい名前を入力できます。
次の問題でこの命名スタイルを使用する良い例を見るでしょう。
3.「ラッパーは何と呼ぶべきですか?」
一部のコンポーネントには、子要素のレイアウトを設定する親ラッパー(またはコンテナ)が必要です。 このような場合、私は常にレイアウトをl-grid
などのレイアウトモジュールに抽象化し、各コンポーネントをl-grid__item
コンテンツとして追加しようとします。
このカードの例では、4つのc-card
リストを整理したい場合、次のマークアップを使用します。
<ul class="l-grid"> <li class="l-grid__item"> <div class="c-card"> <div class="c-card__header"> […] </div> <div class="c-card__body"> […] </div> </div> </li> <li class="l-grid__item"> <div class="c-card"> <div class="c-card__header"> […] </div> <div class="c-card__body"> […] </div> </div> </li> <li class="l-grid__item"> <div class="c-card"> <div class="c-card__header"> […] </div> <div class="c-card__body"> […] </div> </div> </li> <li class="l-grid__item"> <div class="c-card"> <div class="c-card__header"> […] </div> <div class="c-card__body"> […] </div> </div> </li> </ul>
これで、モジュールの場所とコンポーネント名がどのように適合するかについてのしっかりした考えが得られました。
後で頭痛の種がなくなるように、もう少し高度なマークアップを使用することを恐れないでください。 いくつかの<div>
タグを減らすと、誰も気分が良くなりません!
状況によっては、これは不可能です。 たとえば、グリッドが従わない場合、または親要素に意味のある名前が必要な場合は、どうすればよいですか? 私は通常、状況に応じて単語container
またはlist
選択します。 これをカードの例に適用すると、 <div class="l-cards-container">[…]</div>
または<ul class="l-cards-list">[…]</ul>
、使用されるケースに応じて。 重要なのは、命名規則に従うことです。
4.「クロスコンポーネント...コンポーネント?」
別の一般的な問題は、スタイルまたは配置が親コンテナの影響を受けるコンポーネントです。 この問題のさまざまな解決策は、Simuraiによって詳細に説明されています 。 私が最も効果的だと考えるアプローチについてのみお話しします。
要するに、前の例のcard__body
c-button
を追加したいと仮定しましょう。 このボタンはすでにそのコンポーネントであり、次のように設計されています。
<button class="c-button c-button--primary">Click me!</button>
通常のボタンコンポーネントに違いがない場合は問題ありません。次のように移動します。
<div class="c-card"> <div class="c-card__header"> <h2 class="c-card__title">Title text here</h3> </div> <div class="c-card__body"> <img class="c-card__img" src="http://some-img.png"> <p class="c-card__text">Lorem ipsum dolor sit amet, consectetur</p> <p class="c-card__text">Adipiscing elit. Pellentesque.</p> <!-- --> <button class="c-button c-button--primary">Click me!</button> </div> </div>
ただし、スタイルにわずかな違いがある場合はどうなりますか。たとえば、 c-card
コンポーネントの一部である場合にのみ、角を丸くするために少し減らしたい場合はどうでしょうか。
クロスコンポーネントクラスは、私にとって最も信頼できるソリューションのように思えました。
<div class="c-card"> <div class="c-card__header"> <h2 class="c-card__title">Title text here</h3> </div> <div class="c-card__body"> <img class="c-card__img" src="http://some-img.png"> <p class="c-card__text">Lorem ipsum dolor sit amet, consectetur</p> <p class="c-card__text">Adipiscing elit. Pellentesque.</p> <!-- ** --> <button class="c-button c-card__c-button">Click me!</button> </div> </div>
これは、BEM Webサイトで「ミックス」として知られているものです。 しかし、私はこのアプローチについて、Esteban Lussichからのすばらしいコメントをいくつか読んで考えを変えました。
上記の例では、 c-card__c-button
クラスは1つ以上の既存のc-button
プロパティを変更しようとしc-button
が、成功したアプリケーションの呼び出し元(または詳細さえ)に依存します。 c-card__c-button
クラスは、 c-button
ブロックの後に定義されている場合にのみ機能します。このようなクロスコンポーネントをさらに作成すると、すぐに手に負えなくなる可能性があります。 (もちろん、信頼できます!important
ですが、私はそうしません!)。
真のモジュラーUI要素の設計は、親コンテナーから完全に独立している必要があります-どこに配置しても同じように見えるはずです。 「mixes」メソッドのように、別のコンポーネントからクラスを追加してスタイルを設定することは、コンポーネント指向設計のオープン/クローズド原則に違反します。つまり、美学のために別のモジュールに依存するべきではありません。
何よりも、プロジェクトの成長に合わせて簡単に再利用できるため、これらの小さな外観上の違いには修飾子を使用してください。
<button class="c-button c-button--rounded c-button--small">Click me!</button>
これらの追加のクラスを再び使用しない場合でも、少なくとも親コンテナまたは変更を適用するための元の順序にバインドされません。
別の方法は、デザイナーに行き、ボタンが他のボタンと互換性があり、この問題を完全に回避する必要があることを彼に伝えることです。
5.「修飾子または新しいコンポーネント?」
最大の問題の1つは、コンポーネントの終了位置と新しいコンポーネントの開始位置を決定することです。 c-card
例では、非常に類似したスタイル属性を持つc-panel
と呼ばれる別のコンポーネントを作成できますが、違いは非常に顕著です。
ただし、これにより、 c-panel
とc-card
2つのコンポーネント、またはc-card
の単純な修飾子を使用する必要性が決まります。これは、独自のスタイルを適用します。
プロジェクトをモジュールで簡単にいっぱいにして、コンポーネントの形ですべてを行うことができます。 いくつかのコンポーネントCSSファイルの管理が複雑になりそうな場合は、修飾子から始めることをお勧めします。 CSSのブロックから新しい修飾子をスタイルするためにすべてをリセットする必要がある場合の良い指標は、私にとっては、新しいコンポーネントを作成するときです。
何よりも、他の開発者やデザイナーのチームで働いている場合、彼らの意見を見つけてください。 それらを数分間集めて話し合います。 この答えは言い訳のように見えますが、大規模なアプリケーションでは、どのモジュールが利用可能で、コンポーネントが何であるかを理解することが非常に重要です。
6.「状態の管理方法」
これは、特にアクティブな状態または開いた状態でコンポーネントをスタイルする場合、かなり一般的な問題です。 カードがアクティブな状態にあるとしましょう。 そのため、それらをクリックすると、美しいストロークで目立ちます。 このクラスの名前はどうですか?
次の2つのオプションがあります。オフライン状態フックを使用するか、コンポーネントレベルでBEMのような修飾子の命名を使用します。
<!-- --> <div class="c-card is-active"> […] </div> <!-- --> <div class="c-card c-card--is-active"> […] </div>
統一のためにBEM名を使用するというアイデアが好きであるという事実にもかかわらず、自律クラスの利点は、JavaScriptを使用して任意のコンポーネントにユニバーサルステートフックを適用できることです。 スクリプトを使用して修飾子に基づいて特定の状態クラスを適用する必要がある場合、これはより問題になります。 もちろんこれは可能ですが、追加のJavaScriptコードを記述する必要があります。
状態フックの標準セットに固執することは理にかなっています。 クリス・ピアスは良いリストをまとめました 。
7.「新しいクラスを要素に追加しないほうがよい場合」
特に各タグにクラスを割り当てなかった場合、ユーザーインターフェイスの複雑な部分を構築するために必要な膨大な数のクラスに圧倒される人々を理解できます。
通常、コンポーネントのコンテキストで、スタイルを変更する必要のあるすべてのものにクラスをアタッチします。 コンポーネントがこのコンテキストで異なって見えることを要求しない限り、 p
タグをクラスなしで残すことがよくあります。
これは、マークアップに多くのクラスが含まれることを意味します。 しかし、最終的には、コンポーネントは独立して動作し、副作用なしで動き回ることができます。
CSSのグローバルな性質により、クラスをすべてに割り当てると、レンダリングを完全に制御できます。 最初の不快感は、完全にモジュール化されたシステムの利点に値します。
8.「コンポーネントの挿入方法」
言ってみよう。 c-card
コンポーネントにチェックリストを表示したい。 これを行うべきではない方法の例:
<div class="c-card"> <div class="c-card__header"> <h2 class="c-card__title">Title text here</h3> </div> <div class="c-card__body"> <p>I would like to buy:</p> <!-- --> <ul class="c-card__checklist"> <li class="c-card__checklist__item"> <input id="option_1" type="checkbox" name="checkbox" class="c-card__checklist__input"> <label for="option_1" class="c-card__checklist__label">Apples</label> </li> <li class="c-card__checklist__item"> <input id="option_2" type="checkbox" name="checkbox" class="c-card__checklist__input"> <label for="option_2" class="c-card__checklist__label">Pears</label> </li> </ul> </div> <!-- .c-card__body --> </div> <!-- .c-card -->
ここにはいくつかの問題があります。 1つ目は2レベルセレクターで、これについては最初のセクションで学習しました。 第二に、 c-card__checklist__item
適用されるすべてのスタイルは、この特定のケースのみを参照するため、再び使用することはできません。
リストをマークアップモジュールに分割し、チェックリスト項目を独自のコンポーネントに分割することを好みます。これにより、自分で別の場所で使用できます。 これにより、 l-
プレフィックスをゲームに返すことができます。
<div class="c-card"> <div class="c-card__header"> <h2 class="c-card__title">Title text here</h3> </div> <div class="c-card__body"><div class="c-card__body"> <p>I would like to buy:</p> <!-- - --> <ul class="l-list"> <li class="l-list__item"> <!-- --> <div class="c-checkbox"> <input id="option_1" type="checkbox" name="checkbox" class="c-checkbox__input"> <label for="option_1" class="c-checkbox__label">Apples</label> </div> </li> <li class="l-list__item"> <div class="c-checkbox"> <input id="option_2" type="checkbox" name="checkbox" class="c-checkbox__input"> <label for="option_2" class="c-checkbox__label">Pears</label> </div> </li> </ul> <!-- .l-list --> </div> <!-- .c-card__body --> </div> <!-- .c-card -->
これにより、これらのスタイルを繰り返す必要がなくなり、アプリケーションの他の場所でl-list
とc-checkbox
を使用できるようになります。 これにはもう少しマークアップが必要ですが、見返りに、可読性、カプセル化、再利用性が得られます。 これらが主要なトピックであることにお気づきかもしれません!
9.「コンポーネントには100万のクラスがありますか?」
一部の人々は、要素ごとに多くのクラスが悪いと考えており、 --
を追加することもできます。 私にとっては、コードが読みやすくなり、何をすべきかを正確に知っているので、これは問題ではないようです。
ボタンのスタイル設定に使用できる4つのクラスの例:
<button class="c-button c-button--primary c-button--huge is-active">Click me!</button>
私はこの構文が少しugいことを理解していますが、それは明らかです。
ただし、これにより頭が痛くなった場合は、 セルゲイ・ザロウスキのテクニックをご覧ください 。 簡単に言えば、スタイルシートで.className [class^="className"], [class*=" className"]
して、追加の機能をシミュレートする必要があります。 この構文がおなじみの場合は、 Icomoonが同様の方法を使用してアイコンセレクターを制御しているためです 。
この手法では、コードは次のようになります。
<button class="c-button--primary-huge is-active">Click me!</button>
class^=
およびclass*=
を使用した場合、個々のクラスを使用した場合よりもパフォーマンスの低下がはるかに大きいかどうかはわかりませんが、理論的にはこれはクールな代替手段です。 私にとっては、いくつかのクラスで十分なオプションがありますが、この方法は、代替を好む人にとって間違いなく言及に値するように思えます。
10.「コンポーネントタイプの応答を変更できますか?」
この問題はArie Thulankによって私に与えられ、私は100%の解決策を見つけようとしました。
例としては、特定の瞬間にタブのセットに変わるドロップダウンメニューや、メニューバーに変わるオフスクリーンのサイドナビゲーションがあります。
実際、単一のコンポーネントは、メディアクエリによって指示される2つの異なる状態を持つことができます。
これら2つの例では、 c-navigation
コンポーネントを作成する傾向があります。特定の時点での変更がそれを行うからです。 しかし、大きな画面でカルーセルに変わる画像のリストについてはどうでしょうか? これは私にとって問題のあるケースであり、十分に文書化されてコメントされているので、理想的には、このタイプのインターフェイス用に、フレンドリ名( c-image-list-to-carousel
)を含む別のワンタイムコンポーネントを作成する価値があると思います。
ハリー・ロバーツは 、これを制御する1つの方法として、 レスポンシブなプレフィックスを挙げました 。 彼のアプローチは、すべてのコンポーネントをシフトするのではなく、レイアウトと書き込みスタイルの変更を目的としていますが、ここでこの手法を適用できない理由はありません。 したがって、次のようにクラスに名前を付けることができます。
<ul class="c-image-list@small-screen c-carousel@large-screen">
将来的には、これは適切な画面サイズのメディアクエリに反映されます。
ヒント:次のように、バックスラッシュで@
を無効にする必要があります。
.c-image-list\@small-screen { /* */ }
そのようなコンポーネントを作成する理由はありませんでしたが、この方法は開発者にとって非常にフレンドリーに見えます。 他の人があなたの意図を理解しやすいはずです。 しかし、私はそのような名前をsmall-screen
やlarge-screen
small-screen
として推奨しません-それらは読みやすさを改善するためにのみ使用されます。
おわりに
BEMは、コンポーネントベースの方法でのモジュラーアプリケーションの探求において、私にとって救いであることが証明されています。 私は今、ほぼ3年間使用していますが、上記の問題は私の障害となっています。 この記事がお役に立てば幸いです。まだBEMを使用していない場合は、これを開始することを強くお勧めします。
すべてのエラー(文法的、字句的など)についてコメントに書いて、それらを修正できてうれしいです。 ご清聴ありがとうございました