osawa no log

(いまさら)BEMと仲良くするための個人的なガイドライン

created at: 2021-07-27

BEMに頼らなきゃいけないときに心がけていること

はじめに

そもそも2021年にまだBEMを使用していることについて

  • Railsのアセットパイプラインや他のMVCフレームワークなど、CSS Modules/CSS in JS的な運用が難しい案件はまだまだある

    • 実際にはwebpackerなりpostcssなりでしっかり設定すればできるはずだが、そこの決裁権がないことがほとんど
    • 上述の理由であまり知見がない、という恥ずかしい事情もある
  • 単に古い案件の保守がある
  • React/Vueの案件ではコンポーネントファイル内でスコープができるため、BEMを用いる必要がないので採用してない

なぜ今回まとめようと思ったか

  • 引き継ぎで自分の設計したCSS周りの仕様をイチから説明する必要がでてきた
  • 「なぜここがこうなのか」にはそれぞれ明確な理由があるが、それって案外自分だけで把握してることかもしれず、設計をみただけで理解されやすいものではないと思った

そもそもCSSにおける行儀を意識しないと話ができない

  • カスケードしてしまうことによる副作用がつらい
  • 外部ライブラリの行儀が悪いことによる副作用がつらい
  • 詳細度のコントロールが必要になる仕様がつらい
  • position:absolute/fixedやflexbox、gridなど、必要だけどDOMの構造と密結合なレイアウトがつらい

これらのつらさをできる限り低減するための方策として、CSS設計なり命名規則(今回のメインであるBEMなど)を活用する必要があることをまず説明しておく必要がある
が、今回そこまで入れると壮大になるので割愛

コンポーネント志向という考え方を共有しないと理解されづらい

  • BEMはまずBlockというひとかたまりのコンポーネントがあり、その中で子要素としてのElementがある、という概念なので、そこをまず理解してもらう必要がある
  • Atomic Designを先に知ってもらうのがよさそうだが、あれを忠実に体現するのもまた地獄なので難しい

総則

MindBEMdingを採用

  • 汚いといわれがちだが、結局クラス名のユニーク性を担保しやすいのでメリットのほうが大きい
  • 少なくともCSS(SASS)側は&の適切な使用によって汚くはならない
  • HTML側はHamlなりSlimなりを使うことですこし見通しがよくなるが、汚いものは汚い

    • 慣れればよい、といろいろなところで言われるが実際そのとおりと言うしかない
    • 過剰なModifier付与を避ければなんとか許容できるようにはなった

Modifier入りのクラスはModifierなしのクラスと併記する

  • これによってSASS内で@extendだの、継承のためだけの@includeだのを使わなくてよくなる

    • 特に@extendはメディアクエリ絡みの制約があって使いづらいため避けたかった
  • ModifierはあくまでBlockのバリエーションであるという意味性からしても、原型としてのBlockを併記することは宣言的でありわかりやすい
  • haml/slim記法でいうところの

    .block.block--modifierA.block--modifierB.block--modifierC

    みたいな大変なことになる場合もあるが、これはむしろコンポーネントを分けるサインと捉えていて、こんな冗長になるなら切り分けましょう、という考え方

Elementの入れ子は行わない

  • BEMのメリットである構造の明確化が損なわれ、過剰に複雑化してしまう
  • .block__elementA__elementBinA__elementCinBinA

    は素直に.block__elementCにすればいいじゃない

よって構造体は以下の6種類に限定される:

  • .block
  • .block__element
  • .block__element--modifier
  • .block--modifier
  • .block--modifier__element (非推奨)
  • .block--modifier__element--modifier (非推奨)

下ふたつを非推奨としたのは「Modifierに依存したコンテクストが生まれる」のが原因で、その問題点はそもそものCSSの仕様とぶつかって大変だとかそういう話になってくるのでここでは行儀がよくないと言うにとどめておくが、個人的には下ふたつは両方とも.block__element--modifierに統一するのがシンプルで良いと考えている


細則

SASSを使う、そしてSASS記法を使う

  • BEMと直接関係はないが、セミコロンだの波括弧だのを省きたい
  • インデントによる構造管理は記述の自由度を制限できるので、汚い書き方をされづらい

SASS記法におけるインデントレベルとBEMの構造に一定の関連をもたせる

  • 上述の「Elementの入れ子は行わない」ことで規定された6種類の構造体は以下のようなインデント構成で記述される:

    .block
      &__element
        &--modifier
      &--modifier
        &__element // 非推奨
          &--modifier // 非推奨

    特に.block__elementは必ず1インデント、.block__element--modifierは必ず2インデントになることが重要で、これによってsassファイルを目視した際に構造を把握しやすい

なるべくすべての要素にBEM形式のクラスを付与するが、厳密化しすぎない

  • 要素にスタイルをあてるのは行儀が悪いので、面倒でもコンポーネント内部の要素すべてにBEM形式のクラス名を付けたい
  • だがいくらなんでも冗長性が高くなりすぎるもの、DOM構造がカチカチに固定されるものは例外的に許容したい

    • その場合は親子セレクタを用いることでなるべく影響範囲を狭める

      table.block
        > thead, > tbody, > tr
          th, td

&をElementやModifier名の一部として用いない

  • たとえば.block__element.block__element-nameがあったとして

    .block
      &__element
        &-name

    のようにすると、上述のインデントレベルとの関連性が崩れてしまうため、この場合は素直に以下のように分ける

    .block
      &__element
      &__element-name
  • &の用法としては以下にまとめられる:

    .block
      &, > img // 自身および自身の子孫セレクタに影響を及ぼす場合
      &:hover, &:focus // 疑似クラス
      &::before, &::after // 疑似要素
      &.is-active // JavaScript等によって動的に付与されるクラスをコンテクストとする場合

同一のメディアクエリは1コンポーネントにつき1回の記述で済ませる

  • そもそも@mediaを直で書きたくないので以下のようにmixinを作っているが、直書きでも同様

    $breakpoint_md: 768px
    @mixin min-screen($breakpoint: $breakpoint_md)
      @media screen and (min-width: $breakpoint)
        @content
  • 上記mixinを用いる場合、どの階層でも用いることができるが、記述がバラケて追えなくなるので一箇所にまとめる

    • インデントをなるべく統一する意味でも複数の階層に記述されるべきではない

.block__element.block--modifier@mediaの記述順を統一する

  • これらはともに1インデントで記述されるため、パッと見で見間違えるリスクが残る
  • .block__element.block--modifierはどの順でも問題ないが、記述順を統一しておくことでリスクを低減する
  • 余裕があればコメントで区切る
  • いっそ.block--modifierをインデントなしで書いてしまうのもアリだとは感じているが、.blockに内包されるものであることを宣言的に示せるので上記のようにしている
  • @media(およびメディアクエリ用mixin)は最下部にまとめる

まとめると下記のようになる:

.block
  ...
  &--modifier
    ...
  &__element
    ...

  // mixinを用いる場合
  @include min-screen
    ...
    &--modifier
      ...
    &__element
      ...

// @mediaを直で書く場合
@media (min-width: 768px)
  .block
    ...
    &--modifier
      ...
    &__element
      ...

Profile picture

Written by osawa
tw:@osawa