お知らせ フロントエンド バックエンド インフラ 品質保証 セキュリティ 製品 興味・関心 その他

CSS 設計原則からみた CSS のバッドプラクティス、ベストプラクティス

はじめに

業務で自分のCSSの変更が思わぬ CSS の不具合を起こしたケースがあり、正しいCSS 設計について調べてみたいと思い、このテーマを選びました。

Google のエンジニアである Philip Walton 氏が2012年11月に「CSSArchitecture」で言及した「優れた CSS とは」といくつかのバッドプラクティスとベストプラクティスをその原則と比べた考えを自分の意見も踏まえてまとめてみました。

優れたCSSとは

優れた CSS は優れたソフトウェアと大きく異ならず、大きく分けて以下の 4 点を満たします

  1. 予測可能性
    • HTML を見たときに第三者がclassからスタイルを予測しやすい
  2. 再利用性
    • 記述量を減らすことや変更に強くなることにつながる
  3. 保守性
    • 変更に影響されづらくなる
  4. 拡張性
    • 第三者の修正が容易になる

バッドプラクティス

親に基づいてコンポーネントを変更する

親に対して、新しいルールを追加していくので親のセレクターに依存してしまう

  • 予測可能性 ×
    • 場所によって見た目が変わってしまう
  • 再利用性と拡張性 ×
    • 特定の HTML に依存している
    • オープン/クローズの原則に違反
.widget {
 background: yellow;
 border: 1px solid black;
 color: black;
 width: 50%;
}

#sidebar .widget {
 width: 200px;
}

body.homepage .widget {
 background: white;
}

過度に複雑なセレクタ

セレクタが複雑であるほどHTMLとの結合が強くなってしまう

  • 予測性、再利用性、拡張性 ×
    • セレクタが複雑なので HTML との結合が強い
  • HTML はクリーンに保たれるが、そのメリットを上回るデメリットがある
#main-nav ul li ul li div {
}
#content article h1:first-child {
}
#sidebar > div > h3 + p {
}

過度に汎用的なクラス名

スコープを作ることでtitle、contents、actionクラスを持つ他のスタイルには影響しないが、同じ名前(widget)を持つclassのスタイルには影響してしまう

  • 予測可能性 ×
    • 他のスタイルに影響される可能性がある
.widget {
}
.widget .title {
}
.widget .contents {
}
.widget .action {
}
<div class="widget">
 <h3 class="title">...</h3>
 <div class="contents">
  Lorem ipsum dolor sit amet, consectetur adipiscing elit. In condimentum justo
  et est dapibus sit amet euismod ligula ornare. Vivamus elementum accumsan
  dignissim.
  <button class="action">Click Me!</button>
 </div>
</div>

ルールを作るとやりすぎてしまう

ルールを作りすぎると再利用できない

  •  再利用性、拡張性 ×
    •  同じルール内で構造と見た目を定義しているので、再利用可能なものと、不可能なものを分ける必要がある
.widget {
 position: absolute;
 top: 20px;
 left: 20px;
 background-color: red;
 font-size: 1.5em;
 text-transform: uppercase;
}

バッドプラクティスの対策

  • どれも CSS にスタイルの負担が多すぎる
    • HTML と CSS をプレゼンテーションレイヤーとして分ける
  • CSS が持つHTML 構造をできるだけ少なくする
    • CSS は一連の視覚要素の外観を定義して HTML から呼び出す

ベストプラクティス

意図的に行う

CSS を予測可能な状態に保つために、スタイルを設定する要素にクラスを直接適用する

  • 予測可能性、再利用性、保守性 〇
    • 不要な要素に誤って適用される可能性はなくなる
/* Bad: 不要な要素に誤って適用される可能性あり*/
#main-nav ul li ul {
}

/* Good: 直接適用するので不要な要素に適用される可能性なし*/
.subnav {
}

クラスの名前空間

クラスに名前空間を設定すると、コンポーネントが自己完結型でモジュール型に保たれ、既存のクラスが競合する可能性が最小限に抑えられ、子要素のスタイルを設定するために必要な具体性が低くなる

  • 予測可能性 〇
    • HTMLから予測可能
  • 保守性〇
    • クラス自体に名前空間を適用することで、既存のクラスが競合する可能性を抑える
/* Bad: 競合の可能性が高い */
.widget { 
}
.widget .title { 
}

/* Good: 競合の可能性が低い */
.widget { 
}
.widget-title { 
}

修飾子クラスを作成してコンポーネントを拡張する

場所ベースの上書きは特定の場所でのみ使用できるが、修飾子クラスは必要に応じて何度でも再利用することができる

  • 予測可能性 〇
    • HTMLから予測可能
  • 再利用性 〇
    • 再利用でき、開発者の意図を HTML 内で非常に明確に表現できる
/* Bad: 再利用不可*/
.widget {
}
#sidebar .widget {
}

/* Good: 再利用可能*/
.widget {
}
.widget-sidebar {
}

CSS を論理構造に整理する

パターンを抽出しやすくするためにBase,Layout,Module,State,Themeなどのカテゴリを用意する

  • 保守性 〇
  • SMACSS (Scalable and Modular Architecture for CSS)
    • CSSの設計原則の一つ

構造と見た目の分離

繰り返し定義される背景、色、フォントなどの見た目と位置、幅、高さ、マージンなどの構造自体を別で定義する

  • 再利用性、保守性 〇
  • OOCSS (Object Oriented CSS)
    • CSSの設計原則の一つ
.btn {
 /* ボタンの構造 */
 width: 200px;
 height: 50px;
 line-height: 50px;
 text-align: center;
}

.btn-red {
 /* ボタンの見た目 */
 background-color: red;
 color: white;
}
<p><a href="#" class="btn btn-red">ボタン</a></p>

コンテナとコンテンツの分離

場所に依存しないセレクタを書くことで、一つのスタイルの変更で複数の箇所の同じスタイルを変更できる

.header .sub-title {
}
.footer .sub-title {
}

.sub-title {
 font-size: 11px;
 color: #ccc;
 border-bottome: 1px solied #ccc;
}

最後に

「CSSArchitecture」を元に、素の CSS の問題点と解決策を整理することで、現在の乱立する様々なフレームワークが生まれた背景を知ることにつながりました。今後CSSがどう変わっていくかもより注視したくなりました。

内藤

内藤

記事一覧

2022年9月に入社しました。
現在は京都のオフィスで仕事をしています。