css 投稿日:2017/09/11

まさか!?IDを使ったCSS設計手法「SDI-CSS」


計画性の無いCSSがプロジェクトを破綻させてきた。なんとかしてCSSに秩序を持たせたいが、記述の自由度の高さから下手なルールは逆に苦痛を与える。BEMなどの厳密な命名規則を強いるCSS設計が静かに破綻している中、もっと手軽に、直感的に書ける設計は無いのだろうか。本記事ではID属性を使ったスコープデザインのCSS設計「SDI-CSS」を紹介する。




BEMは好きですか?

上記の質問はBEM占いと言って、好きと答えた人は仕事に真面目でルールを守る。好きじゃないと答えた人は直感的でズル賢い。どうです?当たってるでしょう?(BEM占いはもちろん私が考えました)

BEMは実によくできたCSS設計手法で、BEMに従って記述する限りほとんど副作用のないCSSが出来上がります。これは素晴らしいことです。

しかしBEMのあまりにも詳細で省略を許さない命名規則を実践していると、まるで軍隊か刑務所で過ごしているような窮屈さを感じます。はい、私のBEM占いの答えはもちろん後者です。

どうして完璧なはずのBEMが嫌いなのか、自分の胸に手を当てて考えてみました。

BEMの嫌いなところ

BEMのダメなところ、と言っているわけではありません。あくまで個人的な好き嫌いの話しです。

不自然な階層表現。

そう、BEMの命名規則はCSSセレクタを並列に書きます。

例えば3階層にネストする要素を持つ.blockモジュールの定義。

<div class="block">
    <div class="block__elem1">
        <div class="block__elem2">
            <h2 class="block__title">
            <div class="block__elem3"></div>
        </div>
    </div>
</div>
.block {}
.block__elem1 {}
.block__elem2 {}
.block__elem3 {}
.block__title {}

命名規則によるスコープ表現で、CSSがネストしていませんね。

ですが、やはり直感的にこう書きたい気がします。

<div class="block">
    <div class="elem1">
        <div class="elem2">
            <h2 class="title">
            <div class="elem3"></div>
        </div>
    </div>
</div>
/* SCSS */
.block {
    .elem1 {}
    .elem2 {}
    .elem3 {}
    .title {}
}

せっかくCSSセレクタは階層構造を宣言できるのだから、それを利用するのが自然だと思いませんか?

…分かってます。あなたの頭に浮かんだ数々の反論についてはひとまず忘れて下さい。

とにかく長い

<div class="menu">
    ...
    <span class="menu__item menu__item_visible menu__item_type_radio">...</span>
</div>
.menu__item_type_radio {
    color: blue;
}

menu__item menu__item_visible menu__item_type_radio

この部分を認識するには、まず目薬をさす必要がありそうです。

過保護すぎる

<ul class="news-list">
    <li class="news-list__item"></li>
    <li class="news-list__item"></li>
    <li class="news-list__item"></li>
    <li class="news-list__item"></li>
    <li class="news-list__item"></li>
</ul>

ulliしか内包できないのだから、大目に見て欲しいです。

<ul class="news-list">
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
</ul>

厳密過ぎる極小モジュール

BEMではどんなに小さなパーツでもモジュールとして固有名詞が与えられます。

<div class="side-story">
    <h2 class="side-story__title">
        Side Story<br>
        <small class="side-story__title--subtitle"> - Sub title.</small>
    </h2>
</div>

タイトルの中の小さな補足ですら.side-story__title--subtitleのような長い命名が必要になります。一番小さいパーツなのに一番詳細に命名しなくてはならないなんて狂っています。

<div class="side-story">
    <h2 class="side-story__title">
        Side Story<br>
        <small> - Sub title.</small>
    </h2>
</div>

上記で理解できないほど人間の脳は信用出来ないものでしょうか。

<p>Lorem ipsum dolor sit amet,
<strong class="strong-word strong-word--color_caution">consectetur</strong>
adipisicing elit. </p>

少し極端ですが、このようなモジュールも考えられます。

<strong class="caution"></strong>だけじゃダメですか?

ブロックの拡張に弱い

次のようなモジュールがあったとします。

<h2 class="subtitle"></h2>

仕様変更で、見出しと並列に日時を表記する仕様に変わったとします。

<h2 class="subtitle"></h2>
<p class="date"></p>

しかし2つはセットのモジュールなので外包しなければなりません。

<div>
    <h2 class="subtitle"></h2>
    <p class="date"></p>
</div>

はて、ネストしたのでBEMの命名規則に従ってclass名を変更します。

<div class="subtitle">
    <h2 class="subtitle__title"></h2>
    <p class="subtitle__date"></p>
</div>

ひどいリファクタリングです。果たしてこれは変更に強いと言えるのでしょうか?

<div class="subtitle-block">
    <h2 class="subtitle"></h2>
    <p class="date"></p>
</div>

BEMさえなければこれで済む話です。こちらの方が変更に強く見えます。

ではどうあるべきか

散々BEMの悪口を言った気がしますが、BEMが悪いわけでも恨みがあるわけでもなく、たまたま近くにいたので十字架に磔(はりつけ)ただけです。

この記事の本質は、「ではどうあるべきなのか」。

まず人間の本能に従って欲求を並べてみます。

  • できるだけシンプルな命名にしたい!
  • できるだけ省略したい!
  • それでいてコンフリクトを気にしたくない!

BEMはコンフリクトを最重要視しているため冗長となりましたが、プログラミングの世界では「スコープ」という概念によって堅牢性と命名のシンプルさを両立してきました。

そこで考えられたのが、「SDI-CSS」です!

SDI-CSS

「SDI」は「Scope design by ID」の略で、ページをブロックで分割しIDで付与して、スコープを実現しようというCSS設計手法です。

狭いスコープにスタイルを記述することで、.text.image.left.rightなどの安易なclass名を用いることが出来ます。

BEMやOOCSSのようなモジュール指向とは真逆のアプローチで、サイト上のページやコンテンツはそれぞれ固有のスタイルが必要になると考えます。その上で2度以上同じスタイルが必要になったら初めてモジュールに切り出します。

設計手順

SDI-CSSでは、コンテンツのブロック化とbody要素へのclassの付与を行うところから始めます。

ブロック化とID

SDI-CSSではページを複数のブロックに分けることから始まります。ちょうどワイヤーカンプのようなイメージです。

ブロックには全てid属性を指定します。bodyの直下に存在するブロックにはid属性を与えます。ヘッダーやフッター、コンテンツボディなどです。

また、コンテンツボディの直下にあるブロックにもIDを与えます。コンテンツを章のブロックに分けます。それらには「block1」「b1」など安易なID名を与えます。

ブロックで区切り、それぞれをIDで命名
ブロックで区切り、それぞれをIDで命名

これでIDの命名は完了です。
SDI-CSSではブロック以外にID名を与えることは強制しません。もちろん、javascriptからすぐに使いたいボタンなどにID名を与えることはあなたの自由です!

なぜclassではなくidなのか

SDI-CSSではclassは完全に汎用的で重複する可能性があるものとします。

BEMでは逆に重複しないように厳密な命名を行うことを強制していましたね。

IDを使ってブロックが一意であることを保証することで、そのブロックの中では安易なclass名が使えるようになります。

#b1 {
    .title {}
}
#b2 {
    .title {}
}

上記のように.titleのような安易なclass名を再利用できます。

bodyクラス

body要素のclassにはページのスラッグ名を与えます。子ページ、孫ページの場合は親を含む直系親族のスラッグを全て付与します。

<!-- 孫ページのbody-class -->
<body class="parent child grand-child">

そうすることでページ固有のスタイルを狭いスコープで利用することができ、堅牢性を保つことが出来ます。

body.parent.child.grand-child {
    // ここに記述するスタイルは極狭い範囲にしか影響を与えません。
}

bodyにはidではなくclassを用いています。
理由は、スラッグは重複する可能性があるからです。サイト構造によっては以下のような重複が有りえます。

home
    company
        about
    works
        about

ガイドライン

例えば以下のようなページがあったとします。

<html>
<head></head>
<body class="home">
    <div id="header">
        <h1>my web site.</h1>
    </div>

    <div id="content">
        <div id="b1">
            <h2>contents title 1</h2>
            <p>Lorem ipsum dolor sit amet consectetur, adipisicing elit.</p>
        </div>
        <div id="b2">
            <h2>contents title 2</h2>
            <p>Lorem ipsum dolor sit amet consectetur, adipisicing elit.</p>
        </div>
        <div id="b3">
            <h2>contents title 3</h2>
            <p>Lorem ipsum dolor sit amet consectetur, adipisicing elit.</p>
        </div>
    </div>

    <div id="footer"></div>
</body>
</html>

scssで以下のようにコンテンツ構造を定義します。

body.home {
    #content {
        #b1 {

        }
        #b2 {

        }
        #b3 {

        }
    }
}

あとは#b1 #b2 #b3の中に自由にスタイルを記述するだけです!
h2とかpなんていうタグ名もセレクタに用いていいのです!

コンポーネントとのコンフリクトが起こる可能性はありますが、idスコープのおかげでその影響は極小さい範囲にとどまります!

モジュール

同じ表現が2度以上現れたらモジュール、またはコンポーネントに切り出します。

モジュールには命名規則が必要になります。
SDI-CSSでは、モジュールclassは大文字で始めることを推奨しています。
# CSSでは大文字と小文字が区別されます。

以下では粒度に応じて、

  • 要素を複数内包するブロック要素をモジュール
  • インライン要素をコンポーネント

として定義するアイデアを提示します。

//
//  Module
//  必ずclass名を2語以上で構成して下さい。
//  必ず単語の頭文字を大文字にして下さい。
//
.NewsList {
    > li {
        &:after {

        }
        > .news-title {

        }
        > .news-date {

        }
    }
}
//
//  Components
//  必ずclass名を1語で構成して下さい。
//  必ず頭文字を大文字にして下さい。
//
.Btn {
    display:inline-block;
    background-color:$color-primary;
    color:#fff;
    font-size:1rem;
    &.Btn-caution {
        background-color:$color-caution;
    }
    &.Btn-big {
        font-size:1.5rem;
    }
}

ヘルパークラス

どうしても局所的に用いたいスタイルというのは出てくるものです。
例えばこのブロックにだけmargin-top:20pxを付与したい、でもわざわざclass名を割り振りたくない、という場面が少なからずあります。

SDI-CSSではヘルパークラスが使用できます。ですが好ましいアプローチではないことも分かっているので、少し使いにくい命名規則を義務付けるといいでしょう。例えば以下のように定義します。

//
//  helper
//  必ず末尾に"_"を付与して下さい
//
.mt20_ {
    margin-top:20px;
}
.bigtext_ {
    font-size:200%;
}
.cf_ {
    @extend %clearfix;
}
.left_ {
    text-align:left;
}

いきなりですがまとめ

はい、ただのネタでした~!

誰もが当たり前のようにやっていることをあえて文書化するというアホな試みです。あえてホームページビルダーだけで旧石器時代みたいなサイトを作るジョークと同レベルのやつです。

私は実際に上記の思想(本稿でいうSDI-CSS笑)で実装していますが、レガシーな手法であることは自覚した上で使っています。生産性は高いですがメンテナブルでは無いと思います。勢いで書けるのでメンテしないサイトにはいいですよ。

BEMはマジでイイなと思っていて、SASS3.0で追加された@at-rootも使いながらアレンジして取り入れたいと思っています。記法はともかく思想は見習いたいです。

CSSの設計の正解を追い求めると、「正解が無いのが正解」という事実に行き着き、結局は宗教論や方言の違いということに落ち着きます。ただし色々な思想を知っておくことで、案件に応じて臨機応変に使い分けることが出来ます。

私は現状でCSS設計理論を追い求めることはすでに諦めていて、何らかの技術革新待ちです。Googleとか FacebookあたりがDOMを何とかしようとしてHTMLを抽象化した言語的なものが生まれると予想。それにブラウザの実装が追いついたら新しい世界が拓けると妄想しています。

終わりますが、SDI-CSSみたいな思想の設計が文書化されていたらぜひ教えてください。

comments powered by Disqus