About HTML semantics and front-end architecture

Credit

This article is translated with permission of Nicolas Gallagher.
You can find original article at About HTML semantics and front-end architecture

本記事はNicolas Gallagher氏の了承を得て翻訳された記事です。
原文はAbout HTML semantics and front-end architectureにて掲載されています。

HTMLのセマンティックとフロントエンド構造について

私自身が気に入っている考え、体験、アイデアのコレクションでありかつ、1年間実験しつづけているアイデアのコレクション。
記事はHTMLのセマンティックについて、コンポーネントについて、フロントエンドの構造についてのアプローチ、クラスの命名パターン、そしてHTTPの圧縮について。

We shall not cease from exploration And the end of all our exploring Will be to arrive where we started And know the place for the first time. via T.S. Eliot - “Little Gidding”

セマンティックについて

セマンティックとはサインとシンボルの関係性とそれらが何を意味するかについての研究。言語学では主として言語におけるサイン(単語、フレーズ、音など)の意味を探る研究であり、フロントエンドウェブ開発においてはHTMLの要素、属性、属性値(マイクロデータなどの拡張を含む)についての合意された意味と大きな関係を持っている。それらの合意されたセマンティックはたいていの場合は仕様により形式づけられ、ウェブサイトの情報をプログラム(そして人間にとって)側でより理解しやすくするために利用されている。
しかし、形式化された後でも要素、属性、属性値のセマンティックはデベロッパに依存する形で適応され、また吸収されている。このことは合意されたセマンティックの変容につながることがある(HTMLのデザイン原則でもある)

HTMLセマンティックのタイプの違いを見分ける

“セマンティックHTML”を書くという原則は現在のプロフェッショナルなフロントエンド開発の基礎の1部になっている。ほとんどのセマンティックはすでに存在する、あるいは予期出来るコンテンツの特性に関連している(例えば、h1やlang属性、type属性におけるemail値など)

しかし、全てのセマンティックがコンテンツ派生である必要はない。クラス名は“セマンティックではない”という状態にはならない。どんな名前が使われようと意味があり目的がある。クラス名のセマンティックはHTMLエレメントのセマンティックとは異なることがあるわけだ。HTML要素、いくつかの属性、マイクロデータのような同意された“グローバル”なセマンティックを、クラス属性などに含まれる事が多いウェブサイトやアプリケーション単体に属する“ローカル”なセマンティックと混在させずに活かすことができる。

HTML5仕様のクラスについてのセクションで繰り返される仮定された“ベストプラクティス”:

…著者はクラス属性をコンテンツに求める見た目を説明する値ではなく、 コンテンツ自体の特性を説明する値を利用すること。

を実践することに固有の理由がない。それどころか大規模ウェブサイト、アプリケーションの開発においては障害になることが多い。

以下のシンプルな例を見てほしい:

<div class="news">
  <h2>News</h2>
  [news content]
</div>

クラス名newsはコンテンツから明白な部分以外のことを伝達しない。コンポーネントの構造上の仕組みについての情報もないし、“news”以外のコンテンツには利用出来ない。コンテンツの特性に対して厳しくクラス名にセマンティック性を求めたために構造の規模を縮小させただけではなく、ほかのデベロッパにとって使いづらいものにしてしまっている。

コンテンツから独立したクラス名

代替手段としてデザイン内の機能的な、構造的なパターンからクラス名のセマンティックを派生させる方法がある。最も再利用しやすいコンポーネントはコンテンツから独立したクラス名を持っている。

クラス名が特定のコンテンツを厳格に反映した値ではなく、レイヤ同士の関係を明確にすることを恐れるベきではない。
そうすることでクラス名が“セマンティックではない”状態にはならない。単にセマンティックがコンテンツから派生していないだけだ。コンポーネントを再利用性が高く、柔軟で、強固にできるのであればHTML要素を追加することを恐れるべきではない。またそうすることでHTMLが“セマンティックでない”ということにはならない。単にコンテンツをマークアップするのに最低限以上の要素が必要になったというだけにすぎない。

フロントエンド構造

コンポーネント、テンプレート、オブジェクト指向の構造の目的は、限定数の再利用可能で、様々なコンテンツタイプを内包できるコンポーネントを開発することだ。クラス名のセマンティックにおいて重要なのは実用主義に則って、主目的つまり、デベロッパが利用するにあたって意味があり、柔軟で、再利用可能な見た目/動作のフックとなることだ。

再利用、結合できるコンポーネント

柔軟なHTML/CSSは大概の場合、再利用できるコンポーネントの構築をするためにHTMLに対するクラスに頼ることになる。柔軟で再利用できるコンポーネントは特定の要素タイプや存在するDOMツリーに依存しない。容易に見た目の方向性や異なるコンテナに適応することが求められる。必要であれば追加のHTML要素(コンテンツをマークアップする以上に必要な)を用いてコンポーネントをより強固にする必要がある。このよい例がNicole Sullivanが言うところのmediaオブジェクトだ。

簡単に結合できるコンポーネントはタイプセレクタを使わずにクラスだけを利用することでつくることができる。以下はbtnコンポーネントとuilistコンポーネントの結合を難しくしている例。問題は.btnの詳細度は.uilist aよりも少なく(そのため共通のプロパティを上書きする)、uilistコンポーネントがアンカーを子ノードに持たなければならない点にある。

.btn { /* styles */ }
.uilist { /* styles */ }
.uilist a { /* styles */ }
<nav class="uilist">
  <a href="#">Home</a>
  <a href="#">About</a>
  <a class="btn" href="#">Login</a>
</nav>

uilistとほかのコンポーネントとより簡単に結合するためのアプローチとしては、クラスを利用してサブオブジェクトをスタイルする方法がある。ルールの詳細度を減らせるだけではなく、子ノードがなんであれ構造的なスタイルを追加することができることが主な利点となる。

.btn { /* styles */ }
.uilist { /* styles */ }
.uilist-item { /* styles */ }
<nav class="uilist">
  <a class="uilist-item" href="#">Home</a>
  <a class="uilist-item" href="#">About</a>
  <span class="uilist-item">
    <a class="btn" href="#">Login</a>
  </span>
</nav>

JavaScript用クラス

JavaScript用クラスの利用は見た目や構造上の変更から適用したJavaScriptの動作を守ることができる。私自身が便利だと感じているアプローチはJavaScriptフックとして特定のクラス、js-*を利用する方法で、このクラスに対して見た目のスタイルを行わない。

{% highlight html %} {% endhighlight %}

こうすることでJavaScriptで定義した動作や機能を見た目は構造の変化の影響を受けづらくすることができる。

コンポーネント修飾子

大体の場合コンポーネントは元にしているコンポーネントから若干見た目がことなる種類を持つことが多い(例えば異なる背景色やボーダー色)。このコンポーネント種類をつくるために主に2つのパターンがある。ここでは“単一クラスパターン”と“マルチクラスパターン”と呼ぶことにする。

単一クラスパターン

.btn, .btn-primary { /* button template styles */ }
.btn-primary { /* styles specific to save button */ }
<button class="btn">Default</button>
<button class="btn-primary">Login</button>

マルチクラスパターン

.btn { /* button template styles */ }
.btn-primary { /* styles specific to primary button */ }
<button class="btn">Default</button>
<button class="btn btn-primary">Login</button>

プリプロセッサを利用しているなら、単一クラスパターンのメンテナンスをSassの@extend機能を使って削減できる。しかしプリプロセッサの手助けがあったとしても私は“マルチクラスパターン”を利用する方がよいと考えている。

このアプローチの方がより柔軟なパターンだからだ。ベースオブジェクトであるbtnを例にして、5つのタイプのボタンと3つのサイズを拡張したとしよう。“マルチクラスパターン”を利用すると9つの混ぜ合わせることができるクラスを作るだけにたいして、“単一クラスパターン”では24クラス必要になる。

もし絶対に必要なのであればコンポーネントレベルの変更をオブジェクトで行うことも“マルチクラスパターン”のほうが簡単にできる。また別のコンポーネント内で利用するbtnの調整を行うことも容易に可能だ。

/* "multi-class" adjustment */
.thing .btn { /* adjustments */ }

/* "single-class" adjustment */
.thing .btn,
.thing .btn-primary,
.thing .btn-danger,
.thing .btn-etc { /* adjustments */ }

“マルチクラスパターン”ではコンポーネント内のベースオブジェクトbtnの派生系をたった1つのセレクタで指定することが可能になる。“単一クラスパターン”では新しいボタンの種類が作られるたびにすべてのボタンのタイプに対して調整を行う必要がある。

構造的なクラス名

コンポーネント、そしてそれをベースに“見た目”を作る際、いくつかのクラスは修飾子として使われ、いくつかはコンポーネントの境界として使われ、またいくつかはコンポーネントのサブオブジェクトなどに使われる。

クラスの名前にクラス自体の目的が明確に現れていないため、btn(コンポーネント)、btn-primary(修飾子)、btn-group(コンポーネント)、btn-group-item(サブオブジェクト)の関係性を薄めることは難しい。
これに対して一致したパターンが存在しない。

昨年から、私はサイト構造をHTML、CSS、JavaScriptのファイルを行き来して理解するのではなく、DOMスニペットからノードと見た目上の関連性をより素早く理解するための命名規則の実験をしている。パターンはBEMシステムのアプローチをベースに私自身がスキャンしやすいように変更を加えている。

t-template-name
t-template-name--modifier-name
t-template-name__sub-object
t-template-name__sub-object--modifier-name

component-name
component-name--modifier-name
component-name__sub-object
component-name__sub-object--modifier-name

is-state-type

js-action-name

いくつかの構造を抽象的な“テンプレート”として扱い、いくつかをより明確なコンポーネント(通常は“テンプレート”上に作られる)として扱った。しかしこの差異がいつも必要だとは限らない。

これは現時点で私自身が便利だと感じている命名規則でしかない。どんな規則であろうとかまわない。しかし、(1つの)ハイフン、アンダースコア、キャメルケースなどに頼るよりはクラス名の曖昧さを解消する点は利点としてあげられる。

ファイルサイズとHTTP圧縮について

モジュラー、柔軟な、オブジェクト指向のCSSにはどんな場合でもファイルサイズとその肥大化についての議論がついて回る。Nicole Sullivanの話では、このアプローチを採用したことでFacebookのような会社で実際にファイルサイズを減少(メンテナンス性の向上も)ができたことについてよく触れられる。これに加えてHTTPによる圧縮がプリプロセッサのアウトプットとHTMLクラスの広域利用にどう影響を与えるかについて共有したい。

Twitter Bootstrapがはじめリリースされた際、コンパイルされたCSSを私ならどう書くかを反映した形に書き直しファイルサイズを比較した。両ファイルを圧縮した後、私が手作業で作成したCSSはプリプロセッサのアウトプットファイルより10%ほどサイズが小さかった。しかし両方のファイルをgzipするとプリプロセッサのアウトプットファイルの方が手作業で作成したCSSより5%ほど小さかった。

このことは単に圧縮されたファイルだけではなんの意味もなく、HTTPによる圧縮した後でファイルサイズを比較することがどれだけ大事であるかを示している。また十分な経験を積んだCSSデベロッパがプリプロセッサを利用している場合、HTTPによる圧縮によりファイルサイズの小さなファイルを作ることができるため、必要以上にコンパイル後のCSSの繰り返し部分に対して心配する必要がないことを意味している。プリプロセッサを利用することでよりメンテナンスしやすくなった“CSS”コードは、圧縮されただけのアウトプットCSSのサイズや見栄えをしのぐ利点がある。

ほかの実験では60KBのHTMLをライブサイト(すでにたくさんの再利用可能なコンポーネントが存在する)からすべてのクラス属性を削除した。これによりHTMLのファイルサイズを25KBに減らすことができた。しかしgzipされたオリジナルとクラスを削除したファイルのファイルサイズを比べると順番に7.6KBと6KBとなり、1.6KBの違いしかない。実質的なファイルサイズは進歩的なクラスの利用に関わらず心配するに値しないサイズとなるわけだ。

心配しすぎない方法

何年にもおよぶ多くの熟練したデベロッパたちの経験はどう大規模ウェブサイト、アプリケーションを開発するかに対して変革を生み出している。それにも関わらず、“セマンティックHTML”はコンテンツ派生のクラス名を意味するという観念から離れられない個人に対して、大規模アプリケーションを構築する際にそのアプローチがいかに実践的ではないかに気づく機会を与えない。昔のアイデアを捨て、別の方法を探し、以前あきらめた方法にすら機会を与える用意が必要だ。

1度でも、自分、あるいは他者がメンテナンスするだけではなく、積極的に何度も変更を加えるようなウェブサイトやアプリケーションを開発し始めると、どれだけ苦労しようとコードはメンテナンスしづらくなっていくことに気がつくだろう(Nicole Sullivanが何年も言っているように)。この問題に対しての解決を提案している人たちのアプローチを探索してみることは決して時間の無駄にはならない。例えばNicole Sullivanのブログ、Object Oriented CSSプロジェクト、Jonathan SnookのScalable Modular Architecture CSSまたYandexが開発したBlock Element Modifilerなどがある。

HTML/CSSにおいてCSSを書いたり、編集したりする時間を減らす方法を見つけるためには、スタイルを変更するのにHTML要素のクラス名を変更する時間を割くことになる。この方法はフロントエンドにもバックエンドにも、または事前に作られた“レゴブロック”を再アレンジする人にとっても実用的だ。どうやらCSS錬金術はいまだ誰も実践できていないというわけだ。