🙆‍♀️

CSS設計初心者がBEMを使ってみて、理解しておいた方が良いと思ったところ、こうしていれば良かったかもしれないところ

2024/09/26に公開

CSS設計をやったことがない私がBEMを実際プロジェクトで使ってみて、こういう場合どうすれば良いの?と迷ったところが何点かあり、その経験をもとにBEMにおいて理解しておいた方が良いと思ったところをBEMの公式を確認しながらまとめます。
また、実際使ってみてこうした方が良かったかもしれない点についても述べたいと思います。

BEMを採用するに当たってBEMの記事をいろいろ読みましたが、おれおれBEMのような内容の記事も多いことから考えると、BEMそのもののメリットは大きいのでBEMのルールをベースにしつつ、厳密にやろうとすると不便に感じるところがあるので別のルールも追加するみたいな感じかと思います。

私自身、関わったプロジェクトではBEMのルールから逸脱した部分もあります。プロジェクトの前提の問題で逸脱せざるえなかったというのもありますし、単にドキュメントをちゃんと読んでなかったからというのもあります。。。
それでも複数人で開発する上で、BEM等の設計手法を採用していない場合と比べて明らかに他者が書いたコードが読みやすく、メンテナンス性も高いと感じましたので、次のプロジェクトでも利用したいと思いました。

それでは本編に進みたいと思います。

理解しておいた方が良さそうなこと

主にはblockに関連した内容になります。BEMに関しては、Blockをちゃんと設計して運用することが肝かなと思います。

block名のユニークネスは重要である

BEMの公式には以下のように、BEM entityの名前はユニークと言っています。

https://en.bem.info/methodology/naming-convention/#css-selector-naming-convention

The name of a BEM entity is unique.

blockに関しては以下のようなentityであり、そのようなentityに対してユニークな名前をつけてね、という事です。
カプセル化というのはちょっと難しい言い方ですが、blockは、JavascriptやCSS含めて一つの単位としてまとめ、それによってページのどこでも独立してそのまま使えるようにしましょう、ということです。

A logically and functionally independent page component, the equivalent of a component in Web Components. A block encapsulates behavior (JavaScript), templates, styles (CSS), and other implementation technologies.
(日本語訳)
論理的かつ機能的に独立したページコンポーネントであり、Webコンポーネントにおけるコンポーネントに相当します。ブロックは、振る舞い(JavaScript)、テンプレート、スタイル(CSS)、その他の実装技術をカプセル化します。

block名をユニークにすることで、可読性やメンテナンス性も高まりますし、以下に示すようにスタイルの衝突が避けられますので、HTML,CSSを書く上でのある意味一番のストレスから解放されます。

たとえば、class="block__element"に対するSCSSは、以下のような構造になりますが、blockがユニークに管理できていれば、block名が名前空間の役割をしてくれるので、スタイルの競合を気にしなくてすみます。

.block{
    &__element{
    //......
    }
}

実現するためには、たとえば以下の記事のように、block名.scssというファイル名で管理することで、blockとscssファイルを1対1対応に縛るのは良いプラクティスだと思います。
qiita - 一番詳しいCSS設計規則BEMのマニュアル - ファイル

wrapperをどう扱うか

ある要素をページ内に配置する際に、hoge-wrapperとかhoge-containerのようなクラス名をつけたdiv等で対象要素を囲んでレイアウトしていくというやり方はよく利用されてきたように思います。
私はBEMのドキュメントをまともに読まずスタートしてしまったので、wrapperを使ってしまったのですが、(hogeというクラス名のついたblockに対してhoge-wrapperブロックを作成)
公式にはちゃんとwrapperに対する言及があります。

https://en.bem.info/methodology/css/#how-do-i-make-an-html-wrapper

Traditionally, HTML wrappers are used for:
Positioning HTML elements relative to other elements.
Positioning elements inside a section.
The BEM methodology achieves the same results by using mixes, or by creating an additional block element. You don't need to create additional abstract wrappers. They don't add any specific features.
(日本語訳)
伝統的に、HTMLのラッパー要素は以下の目的で使用されてきました:
・他の要素に対するHTML要素の位置調整。
・セクション内での要素の配置。
BEMの方法論では、これらと同じ結果をミックスや追加のブロックエレメントを使用して達成します。抽象的な追加のラッパーを作成する必要はありません。それらは特定の機能を追加するわけではありません。

少し補足すると、この文章から、BEMにおいては特定の機能を提供しない抽象的な要素を作成することは推奨しない、という基本的なコンセプトが読み取れます。

リンク先にある Positioning a block relative to other blocksという項目の方にだけ触れますが、

<body class="page">
    <!-- header and navigation-->
    <header class="header page__header">...</header>

    <!-- footer -->
    <footer class="footer page__footer">...</footer>
</body>
.page__header {
    padding: 20px;
}

.page__footer {
    padding: 50px;
}

pageブロックに対してのheader,footerの位置を設定する際に、header,footerには直接paddingを設定したくない、ということなんですが、なぜそうしたくないかというと、再利用性を損なうからです。(header,footerのポジションなのでサイト内で再利用パターンがあるのか想像しづらいという点でサンプルとして適切なのかは疑問ですが)

では、どう書くかというと、
pageブロックのエレメントとしてのheaderを意味する、page__headerを追加します。
この書き方が意味するのは、この<header>はblockであり、elementでもあるentityという事なので、BEMの基本コンセプトから考えると私は違和感を感じますが、公式では、このmixesという方法を推奨しています。

blockのmarginをどう扱うか

blockは再利用が前提なので、blockにはmarginをつけるべきではない、というのはすぐに想像がつきそうなものですが、私はよく考えずblockにmarginをつけてしまって後で困るというのを経験しました。

では、どう対応したら良いかですが、公式にはちゃんと対応策が書かれています。
上記のwrapperのところと同じですが、mixを使います。
https://en.bem.info/methodology/css/#external-geometry-and-positioning

In CSS with BEM, styles that are responsible for the external geometry and positioning are set via the parent block.
(日本語訳)
BEMでのCSSでは、外部のジオメトリや配置に関するスタイルは親ブロックを介して設定されます。

公式のサンプルコードをそのまま載せますが、buttonブロックに対するmarginは、親blockであるheaderブロックのelementとして定義を追加して対応します。

<!-- `header` block -->
<header class="header">
      <button class="button header__button">...</button>
</header>
.button {
    font-family: Arial, sans-serif;
    text-align: center;
    border: 1px solid black;    /* Frame */
}
.header__button {
    margin: 30px;               /* Padding */
    position: relative;
}

ディレクトリ構造をどうするか

ここは簡単に触れますが(今後加筆するかもしれません)、公式にはこうあります。
File structure organization

こうしていれば良かったかもしれないところ

キャメルケースを採用すれば良かったかも

block-name__element-name--modifier-nameのように複数単語をケバブケースでつないでいる例が多いような気がしますし、私もそれを採用しましたが、横棒が多く個人的には見にくいと感じました。
以下のようにキャメルケースを使えば
blockName__elementName--modifierName となり、視認性が高まると思います。

なお、ネーミングに関しては、Alternative naming schemes という項目があるように、ネーミングについてはいろんなパターンを許容しているようですが、以下のように、blockに関してはちゃんとしよう、とあります。

You can create your own custom naming solution for BEM entities. The most important thing is that your new naming system makes it possible to programmatically separate blocks from elements and modifiers.
(日本語訳)
BEMエンティティに対して、独自のカスタム命名規則を作成することができます。最も重要なのは、新しい命名システムが、ブロックをエレメントやモディファイアからプログラム的に区別できるようにすることです。

SCSSでを使わずに書けば良かったかもしれない

多くの記事では、&を使ってSCSSを記述していますが、実際プロジェクトで書いていると、この記事にもあるように不便さを感じてきます。特にSCSSの修正においてそれを強く感じました。
BEM(MindBEMding)によるCSS設計

ちなみにBEMの公式にはSCSSの書き方についてのドキュメントはありません(たぶん)

・ SCSSの記述量が増えてくると視認性が悪くなる

たとえば以下のように、書いていくわけですが、記述量が少ない時はまだ良いのですが、記述量が増えてきて1画面に収まらなくなってくると、&がどの要素に由来しているのかパっと見で分からなくなってきます。けっこうストレスです。

.block{
  &__element{
    &--modifier{
      ......
    }
    &:hover{
      ......
    }
  }
  &__element2{
    &:last-child{
      ......
    }
  }
}

以下のように書いていれば、冗長なようですが、一目でで分かります。

.block__element--modifier
.block__element:hover
.block__element2:last-child

・ 検索性が悪くなる

上記と関連しますが、ブロックがユニークである(前提)一方、エレメント名やモディファイア名は複数のblock内で同じ名前を使う可能性が高いので、&__elementという検索キーワードだと複数箇所ヒットしてしまって、SCSSの修正を行う際に面倒臭さを感じました。&を使わずに書けばblock__element--modifierの形で一意に検索できます。

以上が私が感じた、こうしていれば良かったかもしれないところ、ですが、上記のような記述のルールはチーム内で決定し開発がスタートしてしまった後では変更が難しいかと思いますので、可能な限り先にどのような記述パターンがあって、それぞれどのようなメリット・デメリットがありそうかを把握しておいてからスタートできると、良いかなと思いました。

まとめ

BEMを使ってみて実際に迷ったポイントに関しては、ほとんどBEMの公式に対応策が書いてありましたので、それを抽出しつつまとめました。参考になれば嬉しいです。
間違い等あれば教えていただけるとありがたいです。
BEM良かったのでまた使ってみたいと思っていますが、FLOCSSも使ってみたいなと思ったりしてます。

<公式以外の参考記事>

Discussion