tailwindcss-typography-sample

HTML セマンティクスとフロントエンド アーキテクチャについて

過去 1 年間に私が試してきた考え、経験、好きなアイデア、アイデアのコレクションです。 HTML セマンティクス、フロントエンド アーキテクチャのコンポーネントとアプローチ、クラス命名パターン、HTTP 圧縮について説明します。

セマンティクスについて

意味論は、記号とシンボルの間の関係と、それらが何を表すかを研究するものです。 言語学では、主に言語における記号 (単語、フレーズ、音など) の意味を研究します。 フロントエンド Web 開発のコンテキストでは、セマンティクスは主に、HTML 要素、属性、および属性値 (Microdata などの拡張機能を含む) の合意された意味に関係します。 これらの合意されたセマンティクスは通常仕様で形式化されており、プログラム (そしてその後人間) が Web サイト上の情報の側面をよりよく理解するのに役立ちます。 ただし、形式化した後でも、要素、属性、および属性値のセマンティクスは、開発者による適応と選択の対象となります。 これは、正式に合意されたセマンティクス (これは HTML 設計原則です) のその後の変更につながる可能性があります。

異なるタイプの HTML セマンティクスの区別

「セマンティック HTML」を記述する原則は、現代のプロフェッショナルなフロントエンド開発の基礎の 1 つです。 ほとんどのセマンティクスは、既存または予期されるコンテンツの性質の側面に関連しています (例: h1 要素、lang 属性、type 属性の email 値、Microdata)。

ただし、すべてのセマンティクスがコンテンツから派生する必要があるわけではありません。 クラス名を「非意味的」にすることはできません。 どのような名前が使用されているとしても、それらには意味があり、目的があります。 クラス名のセマンティクスは、HTML 要素のセマンティクスと異なる場合があります。 HTML 要素、特定の HTML 属性、Microdata などの合意された「グローバル」セマンティクスを、その目的と、通常、次のような属性の値に含まれる「ローカル」Web サイト/アプリケーション固有のセマンティクスの目的と混同することなく、活用できます。 クラス属性。

クラスに関する HTML5 仕様セクションでは、次のような想定される「ベスト プラクティス」が繰り返されているにもかかわらず…

…作成者は、コンテンツの望ましい表現を記述する値ではなく、コンテンツの性質を記述する [クラス属性] 値を使用することが推奨されます。

…これを行う固有の理由はありません。 実際、大規模な Web サイトやアプリケーションで作業する場合、それが妨げになることがよくあります。

次の非常に単純な例を考えてみましょう。

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

クラス名のニュースでは、内容からは明らかでないことは何もわかりません。 コンポーネントのアーキテクチャ構造に関する情報は得られず、「ニュース」以外のコンテンツでは使用できません。 クラス名のセマンティクスをコンテンツの性質に厳密に結び付けると、アーキテクチャの拡張性や他の開発者が簡単に使用できる機能がすでに低下しています。

内容に依存しないクラス名

別の方法は、設計内で繰り返される構造パターンと機能パターンからクラス名のセマンティクスを導き出すことです。 最も再利用可能なコンポーネントは、コンテンツから独立したクラス名を持つコンポーネントです。

クラス名に特定の内容を厳密に反映させるのではなく、レイヤー間の接続を明確かつ明示的にすることを恐れるべきではありません。 これを行うとクラスが「非セマンティック」になるのではなく、単にクラスのセマンティクスがコンテンツから派生しないことを意味します。 より堅牢で柔軟で再利用可能なコンポーネントの作成に役立つのであれば、追加の HTML 要素を含めることを恐れる必要はありません。 これを行うと HTML が「非セマンティック」になるわけではありません。それは、コンテンツをマークアップするために必要な最低限を超える要素を使用することを意味するだけです。

フロントエンドアーキテクチャ

コンポーネント/テンプレート/オブジェクト指向アーキテクチャの目的は、さまざまな種類のコンテンツを含めることができる、限られた数の再利用可能なコンポーネントを開発できるようにすることです。 重要なアプリケーションにおけるクラス名のセマンティクスで重要なことは、クラス名が実用主義に基づいて推進され、その主な目的、つまり開発者が使用できる有意義で柔軟で再利用可能なプレゼンテーション/動作フックを提供することに最も適していることです。

再利用可能で組み合わせ可能なコンポーネント

スケーラブルな HTML/CSS は、概して、再利用可能なコンポーネントの作成を可能にするために HTML 内のクラスに依存する必要があります。 柔軟で再利用可能なコンポーネントとは、DOM ツリーの特定の部分内に存在することに依存せず、特定の要素タイプの使用を必要としないコンポーネントです。 さまざまなコンテナに適応でき、簡単にテーマを設定できる必要があります。 必要に応じて、追加の HTML 要素 (コンテンツをマークアップするために必要な要素以外) を使用して、コンポーネントをより堅牢にすることができます。 良い例は、ニコール・サリバンメディア・オブジェクトと呼んでいるものです。

簡単に結合できるコンポーネントは、クラスを優先して型セレクターを回避することで恩恵を受けます。 次の例では、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 を簡単に組み合わせる方法は、クラスを使用して子 DOM 要素のスタイルを設定することです。 これはルールの特異性を減らすのに役立ちますが、主な利点は、構造スタイルを任意のタイプの子ノードに適用するオプションが提供されることです。

.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-*) にのみ使用し、プレゼンテーションをフックにぶら下げないことです。

<a href="/login" class="btn btn-primary js-login"></a>

こうすることで、コンポーネントの構造やテーマを変更すると、必要な 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 機能を使用して、「シングルクラス」パターンの使用に伴うメンテナンス作業の一部を軽減できます。 ただし、プリプロセッサの助けを借りたとしても、私の好みは「マルチクラス」パターンを使用し、HTML に修飾子クラスを追加することです。

これはよりスケーラブルなパターンであることがわかりました。 たとえば、ベース btn コンポーネントを使用して、さらに 5 種類のボタンと 3 つのサイズを追加します。 「マルチクラス」パターンを使用すると、組み合わせて使用できる 9 つのクラスが得られます。 「シングルクラス」パターンを使用すると、最終的に 24 クラスになります。

どうしても必要な場合には、コンポーネントを状況に応じて調整するのも簡単です。 別のコンポーネント内に表示されるボタンを微調整することもできます。

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

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

「マルチクラス」パターンは、コンポーネント内の任意のタイプの btn スタイル要素をターゲットにするために必要なコンポーネント内セレクターが 1 つだけであることを意味します。 「シングルクラス」パターンは、考えられるすべてのボタン タイプを考慮し、新しいボタン バリアントが作成されるたびにセレクターを調整する必要があることを意味します。

構造化されたクラス名

コンポーネント、およびその上に構築される「テーマ」を作成するとき、一部のクラスはコンポーネントの境界として使用され、一部のクラスはコンポーネント修飾子として使用され、その他のクラスは DOM ノードのコレクションをより大きな抽象的なプレゼンテーションコンポーネントに関連付けるために使用されます。

btn (コンポーネント)、btn-primary (修飾子)、btn-group (コンポーネント)、および btn-group-item (コンポーネント サブオブジェクト) の関係を推測するのは困難です。名前からはその目的が明確に示されていないためです。 クラス。 一貫したパターンはありません。

2011 年の初めに、HTML、CSS、JS を行ったり来たりしてサイトのアーキテクチャをつなぎ合わせようとするのではなく、DOM スニペット内のノード間の表現上の関係をより迅速に理解できるようにする名前付けパターンの実験を開始しました。 ファイル。 要点の表記は主に BEM システムの命名方法の影響を受けていますが、読みやすい形式に調整されています。

私が最初にこの記事を書いて以来、他のいくつかのチームやフレームワークがこのアプローチを採用しています。 MontageJS は表記法を別のスタイルに変更しました。私はこのスタイルを好み、現在 SUIT フレームワークで使用しています。

/* Utility */
.u-utilityName {}

/* Component */
.ComponentName {}

/* Component modifier */
.ComponentName--modifierName {}

/* Component descendant */
.ComponentName-descendant {}

/* Component descendant modifier */
.ComponentName-descendant--modifierName {}

/* Component state (scoped to component) */
.ComponentName.is-stateOfComponent {}

これは、現時点で私が便利だと感じている命名パターンにすぎません。 それはどんな形でも構いません。 ただし、(単一の) ハイフン、アンダースコア、またはキャメル ケースのみに依存するクラス名の曖昧さを取り除くことができるという利点があります。

RAW ファイルのサイズと HTTP 圧縮に関する注意事項

モジュール式/スケーラブル CSS に関するあらゆる議論に関連して、ファイル サイズと「肥大化」に関する懸念があります。 ニコール・サリバン氏の講演では、Facebook のような企業がこの種のアプローチを採用した際に経験したファイル サイズの節約 (およびメンテナンスの改善) についてよく言及されています。 さらに、プリプロセッサ出力に対する HTTP 圧縮の影響と、HTML クラスの広範な使用についての逸話を共有したいと思いました。

Twitter Bootstrap が最初に登場したとき、私はコンパイルされた CSS を書き直して、手動で作成する方法をより適切に反映し、ファイル サイズを比較しました。 両方のファイルを縮小した後、手作りの CSS はプリプロセッサの出力よりも約 10% 小さくなりました。 ただし、両方のファイルも gzip 圧縮すると、プリプロセッサの出力は手作りの CSS よりも約 5% 小さくなります。

これは、縮小されたファイル サイズだけではすべてがわかるわけではないため、HTTP 圧縮後のファイル サイズを比較することがいかに重要であるかを強調しています。 これは、プリプロセッサを使用する経験豊富な CSS 開発者は、HTTP 圧縮後のファイル サイズを小さくするのに適しているため、コンパイルされた CSS 内のある程度の繰り返しについて過度に心配する必要がないことを示唆しています。 プリプロセッサを介したより保守しやすい「CSS」コードの利点は、未加工および縮小された出力 CSS の美しさやサイズに関する懸念を打ち破るはずです。

別の実験では、ライブ サイトから取得した 60 KB の HTML ファイル (すでに多くの再利用可能なコンポーネントで構成されている) からすべてのクラス属性を削除しました。 これにより、ファイル サイズが 25KB に減少しました。 元のファイルとストリップされたファイルを gzip 圧縮すると、そのサイズはそれぞれ 7.6 KB と 6 KB となり、その差は 1.6 KB でした。 クラスを自由に使用した場合の実際のファイル サイズの影響については、強調する価値はほとんどありません。

どうやって心配するのをやめる方法を学んだのか…

多くの熟練した開発者の長年にわたる経験により、大規模な Web サイトやアプリケーションの開発方法が変化してきました。 それにもかかわらず、「セマンティック HTML」とはコンテンツ派生のクラス名を使用することを意味する (それでも最後の手段としてのみ) というイデオロギーに慣れている人にとっては、通常、そのことを痛感する前に大規模なアプリケーションで作業する必要があります。 そのアプローチの非現実的な性質。 古いアイデアを無視し、代替案を検討し、さらには以前に却下した可能性のある方法を再検討する準備ができていなければなりません。

自分や他の人が保守するだけでなく積極的に反復する必要がある、重要な Web サイトやアプリケーションを書き始めると、最善の努力にもかかわらず、コードの保守がますます難しくなり始めることにすぐに気づきます。 これらの問題に取り組むための独自のアプローチを提案した何人かの人々の研究を時間をかけて検討する価値は十分にあります。Nicole のブログとオブジェクト指向 CSS プロジェクト、Jonathan Snook のスケーラブル モジュラー アーキテクチャ CSS、Yandex が開発した Block Element Modifier メソッドなどです。

CSS の作成と編集に費やす時間を削減する方法で HTML と CSS を作成することを選択すると、要素のスタイルを変更する場合は、要素の HTML クラスを変更するためにより多くの時間を費やす必要があることを受け入れる必要があります。 これは、フロントエンド開発者とバックエンド開発者の両方にとって、かなり実用的であることがわかりました。誰もが事前に構築された「レゴ ブロック」を再配置できます。 CSS 錬金術を実行できる人は誰もいないことが判明しました。