単純に書けるが故にどういうルールで書くかを考えとかないとカオスになるんですよね。
クラスの命名の仕方とか、どういう粒度にするかとか、上書きどうするかとかとかとかとか。
実はその「ルール」をどうするかっちゅう設計思想というのがCSSにもありまして。
そのお話です。
!)最後にちゃぶ台返しします
BEM、OOCSS、SMACSS、FLOCSS
主要なCSS設計思想とその比較
開発において、保守性、再利用性、可読性の高いCSSを構築することは非常に重要。主要なCSS設計思想である BEM、OOCSS、SMACSS、FLOCSS について、それぞれの概要、メリット・デメリット、相互の関係性、それぞれの設計思想がどのようにCSSの構造化と管理に貢献するかを振り返ってみましょうか。
1. BEM (Block, Element, Modifier)
BEMは、CSSのクラス名をBlock, Element, Modifierという3つの要素で構成する命名規則 です。 この命名規則に従うことで、クラス名を見ただけで(?)HTMLの構造やスタイルの役割を理解しやすくなり、スタイルの衝突や意図しない上書きを防ぐことができます。
Block: ページを構成する独立したコンポーネントを表します。 例えば、ヘッダー、フッター、メニューなどがBlockに該当します。
Element: Blockを構成する要素を表します。 例えば、ヘッダー内のロゴ、メニュー内のリンクなどがElementに該当します。
Modifier: BlockやElementの状態やバリエーションを表します。 例えば、ボタンのホバー状態や、メニューのアクティブ状態などがModifierに該当します。
例:ブログ記事のカードを実装する場合
HTML:
<article class="blog-card">
<h2 class="blog-card__title">記事タイトル</h2>
<div class="blog-card__content">
<p>記事の概要</p>
<a href="#" class="blog-card__read-more">続きを読む</a>
</div>
</article>
CSS:
.blog-card {
border: 1px solid #ccc;
padding: 20px;
}
.blog-card__title {
font-size: 24px;
margin-bottom: 10px;
}
.blog-card__content {
line-height: 1.6;
}
.blog-card__read-more {
color: blue;
text-decoration: none;
}
.blog-card__read-more:hover {
text-decoration: underline;
}
解説:
blog-card が Block を表し, 記事カード全体のコンテナとなります。
blog-card__title, blog-card__content, blog-card__read-more は Element を表し, blog-card Block 内の要素を指定します。
:hover は Modifier の一種で、blog-card__read-more Element のホバー状態を表します。
このように、BEMの命名規則に従うことで、クラス名を見ただけでHTMLの構造とスタイルの役割を理解することができます。
<BEMのメリット>
可読性の向上: クラス名からHTML構造とスタイルの役割が明確になります。
再利用性の向上: BlockやElementを再利用することで、CSSコードの重複を減らすことができます。
保守性の向上: スタイルの適用範囲が明確になるため、意図しないスタイルの崩れを防ぎやすくなります。
<BEMのデメリット>
クラス名が長くなる傾向: 特にネストが深くなるとクラス名が冗長になり、可読性が低下する可能性があります。
擬似クラスとの組み合わせ: 擬似クラスを使う場合、BEMの命名規則との整合性を保つのが難しい場合があります。
Blockの分割粒度の決定: 適切なBlockの粒度を決定するのが難しい場合があり、設計者によって解釈が異なる可能性があります。
2. OOCSS (Object Oriented CSS)
OOCSSは、「オブジェクト指向」の考え方をCSSに適用した設計思想 です。 ページを構成する要素を 再利用可能なモジュール として定義し、それらを組み合わせてページを構築することで、CSSの保守性と再利用性を向上させることを目指します。
OOCSSの核心は、「レゴのように考える」 という考え方です。 ページを構成する要素をレゴブロックのように捉え、それらを組み合わせることで、複雑なページを構築するという発想です。
例:ボタンを実装する場合
HTML:
<button class="button">ボタン</button>
<button class="button button--primary">プライマリボタン</button>
<button class="button button--large">大きいボタン</button>
CSS:
.button {
display: inline-block;
padding: 10px 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #fff;
color: #333;
text-decoration: none;
cursor: pointer;
}
.button--primary {
background-color: #007bff;
color: #fff;
}
.button--large {
font-size: 18px;
padding: 15px 30px;
}
解説:
.button が モジュール を表し, ボタンの基本的なスタイルを定義します。
.button--primary と .button--large は Modifier で, ボタンのバリエーションを定義します。
このように、OOCSSを用いることで、ボタンというモジュールを定義し、それをベースに様々なバリエーションを作成することができます。
<OOCSSのメリット>
再利用性の向上: モジュールを再利用することで、CSSコードの重複を減らし、開発効率を向上させることができます。
保守性の向上: モジュール単位でスタイルを管理するため、変更の影響範囲を限定しやすくなります。
柔軟性の向上: モジュールを組み合わせることで、様々なレイアウトやデザインに対応しやすくなります。
<OOCSSのデメリット>
モジュール分割の難しさ: 適切なモジュール分割を判断するのが難しく、設計者によって解釈が異なる可能性があります。
抽象化の難しさ: 再利用性を高めるためには、モジュールを抽象化する必要がありますが、抽象化のレベルを適切に設定するのが難しい場合があります。
3. SMACSS (Scalable and Modular Architecture for CSS)
SMACSSは、CSSを5つのカテゴリ(Base, Layout, Module, State, Theme)に分類する設計手法です。これにより、大規模なWebサイトでもCSSを構造化し、管理しやすくすることを目指します。
Base: ウェブサイト全体に適用される基本的なスタイルを定義します。 (例: リセットCSS、タイポグラフィ)
Layout: ページ全体のレイアウトを定義します。 (例: ヘッダー、フッター、コンテンツエリア)
Module: 再利用可能なコンポーネントのスタイルを定義します。 (例: ボタン、フォーム、カード)
State: 要素の状態に応じたスタイルを定義します。 (例: ホバー状態、アクティブ状態)
Theme: ウェブサイトのテーマ(配色、フォントなど)を定義します。
例:ウェブサイトのヘッダーを実装する場合
HTML:
<header class="header">
<div class="container">
<h1 class="header__logo">ロゴ</h1>
<nav class="header__nav">
<ul class="header__nav-list">
<li class="header__nav-item"><a href="#">メニュー1</a></li>
<li class="header__nav-item"><a href="#" class="is-active">メニュー2</a></li>
<li class="header__nav-item"><a href="#">メニュー3</a></li>
</ul>
</nav>
</div>
</header>
CSS:
body {
font-family: sans-serif;
}
.header {
background-color: #f0f0f0;
padding: 20px 0;
}
.container {
width: 960px;
margin: 0 auto;
}
.header__nav-list {
list-style: none;
margin: 0;
padding: 0;
}
.header__nav-item {
display: inline-block;
margin-right: 20px;
}
.is-active {
font-weight: bold;
}
解説:
body のスタイル定義は Base カテゴリに属します。
.header と .container のスタイル定義は Layout カテゴリに属します。
.header__nav-list, .header__nav-item は Module カテゴリに属します。
.is-active は State カテゴリに属します。
<SMACSSのメリット>
構造化の促進: CSSをカテゴリ別に分類することで、大規模なスタイルシートでも管理しやすくなります。
保守性の向上: カテゴリ別にスタイルを管理することで、変更の影響範囲を限定しやすくなります。
チーム開発の効率化: 共通の設計ルールに従うことで、チームメンバー間でのコミュニケーションが円滑になります。
<SMACSSのデメリット>
学習コスト: 5つのカテゴリを理解し、適切に適用するためには、ある程度の学習コストが必要です。
柔軟性の低下: 厳密なルールに従うため、状況によっては柔軟性に欠ける可能性があります。
4. FLOCSS (Functional Level of CSS)
FLOCSSは、CSSを機能レベルで分類する設計手法 です。 OOCSSやSMACSSの考え方をベースに、より明確なファイル構成と命名規則を定義することで、CSSの保守性と再利用性を向上させることを目指します。
FLOCSSでは、CSSを以下の3つのレイヤーに分割します。
Foundation: プロジェクト全体で共通の基本的なスタイルを定義します。 (例: リセットCSS、変数、mixin)
Layout: ページ全体のレイアウトを定義します。 (例: ヘッダー、フッター、メインコンテンツ)
Object: 再利用可能なコンポーネントのスタイルを定義します。 Objectはさらに、Component, Project, Utilityに分類されます。
Component: 再利用可能な最小単位のコンポーネント
Project: いくつかのComponentの集合で、ページ固有のブロック
Utility: ComponentやProjectでは扱わない、marginなどのスタイル調整
例:ウェブサイトのフォームを実装する場合
HTML:
<form class="p-form">
<div class="c-form-group">
<label for="name" class="c-form-label">名前</label>
<input type="text" id="name" class="c-form-input">
</div>
<button type="submit" class="c-button u-mb16">送信</button>
</form>
CSS:
body {
font-family: sans-serif;
}
.l-container {
width: 960px;
margin: 0 auto;
}
.c-form-group {
margin-bottom: 20px;
}
.c-form-label {
display: block;
margin-bottom: 5px;
}
.c-form-input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.c-button {
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 5px;
background-color: #007bff;
color: #fff;
text-decoration: none;
cursor: pointer;
}
.u-mb16 {
margin-bottom: 16px;
}
解説:
body のスタイル定義は Foundation レイヤーに属します。
.l-container のスタイル定義は Layout レイヤーに属します。
.c-form-group, .c-form-label, .c-form-input, .c-button のスタイル定義は Component に属します。
.u-mb16 は Utility に属します。
<FLOCSSのメリット>
明確なファイル構成: レイヤーごとにファイルを分割することで、CSSの構造が明確になります。
命名規則による役割の明確化: 各レイヤーに特定のプレフィックスを付けることで、クラス名を見ただけでその役割を理解できます。
保守性と再利用性の向上: モジュール化と命名規則により、CSSの保守性と再利用性が向上します。
<FLOCSSのデメリット>
学習コスト: FLOCSSの概念と命名規則を理解するまでに、ある程度の学習コストが必要です。
柔軟性の低下: 厳密なルールに従うため、状況によっては柔軟性に欠ける可能性があります。
各設計思想の相互関係
BEM、OOCSS、SMACSS、FLOCSSは、それぞれ異なるアプローチでCSSの構造化と管理を目指していますが、相互に関連し合い、補完し合う関係にあります。
OOCSSは、CSS設計の基本的な考え方 を提供し、BEM、SMACSS、FLOCSSは、その考え方を具体的な手法に落とし込んだものと言えます。
BEMは、命名規則に焦点を当てた手法 であり、OOCSS、SMACSS、FLOCSSのいずれにも適用できます。
SMACSSは、CSSの分類に焦点を当てた手法 であり、OOCSSとFLOCSSの考え方を中に含んでます。
FLOCSSは、OOCSSとSMACSSをベースに、より明確なファイル構成と命名規則を定義した手法です。
…ここまでのまとめ
CSS設計思想は、大規模なWebサイトにおいてもCSSを効率的に管理・運用するために不可欠なものです。 どの設計思想を採用するかは、プロジェクトの規模や要件、チームのスキルレベルなどを考慮して決定する必要があります。
重要なのは、CSS設計思想の背後にある原則を理解し、プロジェクトに最適な手法を選択することです。 適切なCSS設計思想を採用し、実践することで、保守性、再利用性、可読性に優れたCSSを構築することができます。
新たな設計思想の検討
React(Next.js)を用いた開発において、最適なCSS設計思想は、プロジェクトの規模やチームの構成、開発者のスキルセットによって異なります。既存の設計思想(BEM, OOCSS, SMACSS, FLOCSS)はそれぞれ優れた点を持つ一方で、Reactのコンポーネント指向の開発スタイルと完全に合致しない部分も存在します。
既存の設計思想のルールに固執せず、Reactの特性を活かした、新たな設計思想の方向性を検討してみましょう。
目指すべき方向性
コンポーネント中心: Reactのコンポーネント構造を最大限に尊重し、CSSもコンポーネント単位で管理することを基本とします。
再利用性と拡張性の両立: コンポーネントの再利用性を高めつつ、プロジェクト固有のスタイルや将来的な拡張にも柔軟に対応できる仕組みを目指します。
シンプルで理解しやすい: チームメンバー全員が理解しやすく、保守しやすいシンプルなルールを設定します。
具体的な設計案
コンポーネントスタイル:
各コンポーネントは、独自のCSSモジュールを持ちます。
コンポーネント内部の要素のスタイルも、コンポーネントのスコープ内で定義します。
基本的にはBEMのような命名規則を用いることで、コンポーネント内の要素を識別しやすくします。ただし、厳密なBEMのルールに縛られる必要はありません。
コンポーネントのバリエーションは、propsやコンテキストAPIなどを活用して、動的にスタイルを適用します。
グローバルスタイル:
プロジェクト全体で共通のスタイル(タイポグラフィ、カラーパレット、リセットCSSなど)は、別途グローバルなCSSファイルで定義します。
グローバルスタイルは、FLOCSSのFoundationレイヤーの考え方を参考に、基本的なスタイル定義に限定します。
レイアウトスタイル:
ページ全体のレイアウトや、ヘッダー、フッター、サイドバーなど、複数のコンポーネントにまたがるスタイルは、レイアウト専用のCSSモジュールまたはファイルで定義します。
レイアウトスタイルでは、必要に応じてグリッドシステムやFlexboxを活用します。
SMACSSのLayoutカテゴリの考え方を参考に、レイアウト構造を明確に定義します。
ユーティリティクラス:
マージン、パディング、色の調整など、コンポーネントを横断して使用される軽微なスタイル調整のために、ユーティリティクラスを用意します。
ユーティリティクラスは、FLOCSSのUtilityレイヤーの考え方を参考に、命名規則を統一し、必要最小限に留めます。
実装例:
src
├── components
│ └── Button
│ ├── Button.tsx
│ └── button.module.css
├── layouts
│ └── Header
│ ├── Header.tsx
│ └── header.module.css
├── styles
│ ├── global.css
│ └── utilities.css
└── pages
└── index.tsx
components/Button ディレクトリ: ボタンコンポーネント
Button.tsx: コンポーネントのロジック
button.module.css: コンポーネントのスタイル
layouts/Header ディレクトリ: ヘッダーレイアウト
Header.tsx: レイアウトのロジック
header.module.css: レイアウトのスタイル
styles ディレクトリ: グローバルおよびユーティリティスタイル
global.css: プロジェクト全体の共通スタイル
utilities.css: ユーティリティクラス
<メリット>
コンポーネントの独立性が高く、再利用しやすい: コンポーネントスタイルが独立しているため、他のコンポーネントへの影響を心配することなく、コンポーネントを再利用できます。
スタイルの管理が容易: CSSがコンポーネント、レイアウト、グローバル、ユーティリティに分類されているため、スタイルの変更や追加が容易になります。
チーム開発に適している: 明確なルールと構造により、チームメンバー全員がCSSの役割を理解しやすく、一貫性を保った開発が促進されます。
<デメリット>
設計ルールをチーム全体で共有することが重要: 独自の設計思想を採用する場合は、ルールを文書化し、チーム全体で共有することが重要です。
過度な抽象化は避ける: 再利用性を追求しすぎると、抽象度が高くなり、コードの理解が難しくなる可能性があります。
ユーティリティクラスの乱用を防ぐ: ユーティリティクラスは便利ですが、多用するとスタイルの管理が複雑になる可能性があります。
まぁ、もうやってますよね(まとめ)
React(Next.js)でのCSS設計は、プロジェクトの特性に合わせて柔軟に設計することが重要です。既存の設計思想を参考にしつつ、Reactのコンポーネント指向の開発スタイルに適した、新たな設計思想を模索することで、より保守性、再利用性、可読性の高いCSSを実現できるでしょう。
し・か・し
生成AIはTaillwindをよく用いる。
…もうこんなの考える必要ないのでは。
こういう設計思想のそもそものcssの使い方の前提からひっくり返る時代になってますね。
僕はもうimportantさえ無ければOKだと思ってます。
議事録
- BEMをLPで使ったことがある
- LPへペライチでReactはコンポーネントだが、BEMの考え方をReactでも使っている
- ただ、BlockはReactコンポーネント自体になるので、Blockは無くしている
- Block部分にwrapperなどつけたりもしている
- つまり、Reactでは各種CSS記法の一番上の階層がそもそも不要なので、注目されなくなったのでは
- 一方CSSに@layerがある
- グローバルCSSを書く
- どのlayerを使うかを各CSSで使うイメージ
- 今、違うコンポーネントで同じCSSを使いたいというケースはある
- 生成AIとlayoutコンポーネントがあれば、それで解決してしまう
- Next.jsで生成されるCSSがBEM的になっているので、その知識だけあれば読みやすいくらい
- もし使うとしたらpanda cssはありかもしれない