Irohabook
CSS CSSのコード規約

BEMから脱出したHTMLとCSSのコーディング規約・命名規則と例(2019年最新版)

広告

BEM は優れた命名規則で、当サイトも長い間使ってきた。しかし階層が深くなるとアンダースコアの区切りに違和感が出てくる。

あなたが見ているこのページを前にスクロールしてほしい。ヘッダーは左と右の 2 つに分かれている。左のパーツはロゴとカテゴリー一覧がある。

今回はこのページの構造を例に BEM と違う命名規則を考える。ページのおおまかな構造は下のようになる。

<div class="header">
    <div class="header__left">
    </div>
    <div class="header__right">        
    </div>
</div>

BEM を使うとアンダースコアがたくさん必要になるかもしれない

愚かなコードは次のようになる。

<div class="header">
    <div class="header__left">
        <div class="header__left__logo"></div>
        <div class="header__left__categories"></div>
    </div>
    <div class="header__right">
    </div>
</div>

BEM を額面どおりに受けとるとアンダースコアだらけになるが、一部の開発者は

<div class="header">
    <div class="header__left">
        <div class="header-left__logo"></div>
        <div class="header-left__categories"></div>
    </div>
    <div class="header__right">
    </div>
</div>

とするかもしれない。結局はハイフンでつないでいる。開発者は「親・子・孫」の関係を「親・子」と「子・孫」の 2 つの関係に分けて、最初の「親・子」は BEM が定めたアンダースコアのルールを適用しない。

親子の関係をアンダースコア、単語の区切りをハイフンにすると、コピー&ペーストで苦しむ。

header-left__logo

という単語をダブルクリックしてコピーするとき、header と left__logo で分かれる。もちろんこれは「親・子・孫」の関係を壊すものだ。そもそもアンダースコアを 2 本も使ってコードを冗長にして、デザイナーはストレスがたまらないのか?

広告

別の書き方

ここから当サイトが採用した命名規則になる。

<div class="header">
    <div class="header-left">
        <div class="headerLeft-logo"></div>
        <div class="headerLeft-categories"></div>
    </div>
    <div class="header-right">
    </div>
</div>

親子関係はいつもハイフンで表す。

header-left
headerLeft-logo

headerLeft という単語はキャメルケースで、この単語そのものが親子関係(header-left)を暗示している。しかし headerLeft-logo はあくまでも headerLeft と logo の親子を意味する。

結論はこうである。自分に親がいるときは、常に「親・自分」とハイフン区切りで命名する。自分の親はいつもキャメルケースにする。キャメルケースに親の親などの先祖を暗示させる。

<div class="header">
    <div class="header-left">
        <div class="headerLeft-logo"></div>
        <div class="headerLeft-categories">
            <div class="headerLeftCategories-item">
                <div class="headerLeftCategoriesItem-date">
                </div>
            </div>
            <div class="headerLeftCategories-item">
            </div>
        </div>
    </div>
    <div class="header-right">
    </div>
</div>

BEM と関係ないコーディング規約・命名規則

当サイトはシンプルな構造とデザインを徹底している。HTML のコードはなるべく増やさず、ネストは浅くする。inline-block や flex はもちろん、box-sizing なども駆使して最小のコードで UI を組み立てる。

シンプルな HTML と css にはいくつかの規則が必要になる。

広告

大原則:コードをなるべく書かない

集合論と記号論理学は難しいが、その原因は入れ子にある。あらゆる概念で最も厄介なものが入れ子である。そして html と css は入れ子を相手にする。

箱の中に箱があり、その中にまた箱があり…という構造は単純で扱いやすいが、巨大化すると高層ビルのように融通がきかないものになる。

入れ子を相手にするときは、階層を少なくしよう。余計なタグを書かないようにしよう。余計なクラスを定義しないように。

これを達成するために、あなたは最初に書いたコードを一度すべて破壊する必要がある。これは過度な最適化でなく、プロジェクトが健全に進化するためのプロセスである。

あなたが見ているこのサイトのコードを見てほしい。ネストが浅く、タグがほとんどなく、行数がないことに気づくはずだ。このコードは現時点で 17 回ほど創造と破壊をくりかえしている。

まずはコードを書こう。そして徹底的に破壊し、徹底的に削るのだ。誰が書いたコードだろうと容赦なく無駄を省くのだ。もしコードを消されて怒る人がいたら倍返しだ。

数学を履修した人の意見を聞こう

筆者は数学科出身だから、数学を理解している人はデザインの複雑さをうまく処理する力があると確信している。数学を知っている人がいたら、プロジェクトは巨大化と生産性のバランスを維持しながら進化するはず。

色やフォントサイズなどのみを指定する個別のクラスを使わない

文字を緑色にするクラス green を考えてみる。

.green {
    color: green;
}

一行しかないこのクラスがあちこちに散らばった html は最低のコードである。フォントを緑色にしたい要素に color: green; を指定すればすむ。

あなたがこのページを見たという経験を一生忘れさせないためにもう一度言う。

色やサイズだけを指定するクラスは、史上最低にして史上最悪のデザインだ。驚くほど生産性が低く、関連するコードに悪影響を与える。人類が生んできた css のコードで最も愚かなものが、色だけを指定したクラスである。

広告

OOCSS を採用しない

OOCSS は html と css を根本的に理解していないため、決して採用してはいけない。プログラマーとしてでなく、純粋なユーザーになっていろいろなページデザインを見つめ、入れ子の複雑さがシダ植物のように繁栄しているさまを眺めよう。

冷静になったら OOCSS の思想をあらためて考えよう。次のポイントが見えてくるはずだ。

第一に、入れ子はオブジェクト指向と合わない。第二に、構造はクラスでなく html のタグが決定する。第三に、複数のクラスを同一のタグに指定することは、入れ子の複雑さをべき乗に拡大させる。以上から第四に、拡張性、汎用性、可読性のすべてにおいて OOCSS は採用に値しない。

コピー・ペーストを悪と考えない

OOCSS を推奨する人は「コピー・ペーストの多い設計は愚かだ」と考えている。プログラマーは楽をするために苦労すると言われるが、コピー・ペーストのようなくだらない工程を減らす試みは、あるレベルを超えるとそれを帳消しにするハードワークを将来に生む。

例えばログイン、アップロード、利用規約の同意のボタンを考えよう。あなたは三つのボタンを同じようにデザインしたい。つまり同じ幅を指定したい。

.btn {
    width: 120px;
}

この btn というクラスを三つのボタンにぶちこみたい。そうすれば btn の width を変えるだけで、すべてのボタンの幅が変わる。考え方はいいが、この発想を過剰に進めると、ログインとアップロードのボタンがそれぞれのページに最適化されて独自に進化する可能性は失われる。

ログインのボタンはログインページのデザイン変更に合わせて進化するべきだが、この人は自分の愚かな省エネ精神を優遇して、ログインとアップロードという本来異なる機能のボタンを共通にしている。

機能ごとにデザインするという原則はコーディングでなくビジネスが下す結論だ。多くのデザイナーはビジネスでなくデザインをデザインするため、愚かな省エネ精神を発揮する。まともな結論はこうだ。

.login button {
    width: 120px;
}

.upload button {
    width: 120px;
}

.agreement button {
    width: 120px;
}

ひょっとしたらアップロードのボタンは 160px が最適かもしれない。進化とは可能性を諦めないことだ。可能性を維持するにはつまらない労力を尊重しないといけない。

入れ子を普遍的に支配する万能なフレームワークや概念などこの世にない。あると考えることは無駄であり、それを研究する時間はコピー・ペーストよりもはるかに高くつく。もう一度しつこく主張しよう。

OOCSS のような愚かな省エネ精神より、不愉快なコピー・ペーストを尊重するほうが生産性は良くなる。「フレームワークのようなコーディング規約を採用しないと大規模なデザインは破綻する」という考えは、愚かな妄想である。

クラス名に日本語を使わない

日本語をローマ字にしたクラス名は使わない。

.kiji {
    font-size: 15px;
}

.kategorii {
    font-size: 15px;
}

.kaisha-annai {
    font-size: 15px;
}

日本語をローマ字にすると見映えが悪くなり、単語も冗長になる。

広告

子要素を持たない要素に class をふらない(最重要)

document というクラス名の div 要素に p タグの子要素があるとき、この p タグにクラスはふらない。

.document p

わざわざ .document-p というクラスを作る必要はない。「タグに直接デザインを指定してはいけない」という主張は採用できない。記事の時刻を time タグで指定しているとき、他に time タグがなければ

.post time {
    color: #666;
    font-size: 12px;
}

とする。わざわざ .post-updated-time といったクラスを作る必要はない。タグをそのまま活用しない限り、HTML のコードは無駄なクラスであふれて汚くなる。

すべてに class を指定するデザイナーはコードを汚し、周りのプログラマーに迷惑をかける。

float を使わない

display: flex が登場してから float をほぼ完璧に追放できるようになった。今や使う必要はほとんどない。使って生産性が向上したことは一ミリ秒もない。

before などを駆使する

before や after を駆使すると余計なタグを書かずにすむ。この記事の下に同じカテゴリーの記事一覧が表示されているが、リストの項目に番号がついている。これは before を使っている。

画像と文字のセット、または特殊なデザインが文字の隣にあるケースは before や after で処理するようにしよう。

省略形を使わない

btn などの略語を使わない。「このくらいの略語はわかるだろう」という判断は、多くの人にコードが流れていくにつれて余計な時間ロスを生む。

btn でなく button と書くこと。.glb でなく .global と書くこと。

div で a タグを囲うことに躊躇する

a タグは厄介なタグで、つい div や p で囲ってしまう。だいたいは意味がない。

<div class="tag">
    <a href="/tag/hello">Hello</a>
</div>

.tag {
    width: 120px;
}

.tag a {
    display: block;
    font-size: 15px;
    width: 100%;
}

上のようなコードをよく見かけるがナンセンスである。a タグを囲う div は不要だ。

<a href="/tag/hello" class="tag">Hello</a>

.tag {
    display: inline-block;
    font-size: 15px;
    width: 120px;
}

inline-block を使うと階層が一つ減ることがある。

複数形を使う

複数形を使うとコードが簡潔になる。次の HTML はどちらがいいだろうか。

<div class="post">
    <div class="post-item"></div>
    <div class="post-item"></div>
    <div class="post-item"></div>
</div>

<div class="posts">
    <div class="post"></div>
    <div class="post"></div>
    <div class="post"></div>
</div>

前者は愚かなコードだ。複数形を用いた後者のコードにしよう。小要素に item などの単語を使って親子関係を明示すると、孫やその子の要素の class 名は冗長になる。

<div class="post">
    <div class="post-item">
        <div class="postItem-header"></div>
    </div>
    <div class="post-item"></div>
    <div class="post-item"></div>
</div>

post-item の子は postItem-header になる。しかし複数形を用いたコードでは

<div class="posts">
    <div class="post">
        <div class="post-header"></div>
    </div>
    <div class="post"></div>
    <div class="post"></div>
</div>

post-header である。複数形を使うと、孫世代からの class 名から一つの単語を除去できる。

広告

兄弟関係をアンダースコアで明示する

次のコードはデザイナーをいらだたせる。

<div class="posts-wrapper">
    <div class="posts-wrapper-header">
        <p>最新記事</p>
    </div>
    <div class="posts">
        <div class="post"></div>
        <div class="post"></div>
    </div>
    <div class="posts-wrapper-footer">
        <p>次のページ</p>
    </div>
</div>

このコードは posts があくまでもメインであり、最新記事という見出しなどは補足にすぎない。これらサブのブロックを扱うとき、多くの人はメインとサブを包みこむラッパー(posts-wrapper)を定義する。

膨大なコードを分析した結果、ほとんどのラッパーはまともに機能しないことがわかっている。ラッパーは画面全体か、それに近い層にあるときにうまく機能する。小さいブロックのラッパーがうまく機能することは、経験上ほとんどない。つまり posts-wrapper は不要である可能性が高い。

次のコードはどうだろうか?

<div class="posts-header">
    <p>最新記事</p>
</div>
<div class="posts">
    <div class="post"></div>
    <div class="post"></div>
</div>
<div class="posts-footer">
    <p>次のページ</p>
</div>

まともなデザイナーにとって耐えがたいコードだ。なぜ posts-header は posts の外側にあるのに、ハイフンで header を持っているのか?

私たちはハイフンを親子関係とみなすことによって、世界中のページを統一的なコードで理解している。デザインの勉強をしていない人もどういうわけかハイフンで親子を明示する。それほどハイフンとは強い記号なのに、上のコードはその常識を覆している。

そこで筆者たちは次の原則を開発した。

<div class="posts_header">
    <p>最新記事</p>
</div>
<div class="posts">
    <div class="post"></div>
    <div class="post"></div>
</div>
<div class="posts_footer">
    <p>次のページ</p>
</div>

アンダースコアを使うことで posts_header が posts と兄弟関係にあるとわかる。ハイフンがないから、親子関係と違うことは明白だ。実際、その違和感のおかげで BEM はデザイナーに採用されるようになった。

補足 container と wrapper

デザインを中央に揃えたいときなど、その部分をラッパーで包むことは多い。ラッパーのクラス名として container と wrapper がよく使われる。container は、それに直接含まれる要素(子要素)が複数あるときに使う。子要素が一つの場合は wrapper を用いる。

子要素複数 ... container
子要素一つ ... wrapper

親要素が body のときを除いて、子要素を一つしか持たない下のような構造は避けるべきである。

親 home
子 home-content
孫 home-main
ひ孫 home-header, home-content

これは次のように書き換える。

親 home
子 home-wrapper
孫 home-container
ひ孫 home-header, home-content

広告

コンピューター コンピューター
プログラミング プログラミング
数学 数学
英語 英語
国語 国語
理科 理科
社会 社会