[CSS設計] ITCSSをプロダクトに利用した所感

To end the Specificity Wars.

CSS の問題点

CSS はグローバルスコープ詳細度など CSS の仕様により、スタイル定義が複雑化しやすく、ファイルサイズも肥大化しやすい。

それらの問題を極力回避するため、CSS 設計が必要となってくる。

CSS 設計

CSS には破綻しにくい設計が求められる。

メジャーな CSS 設計手法としては、 BEMSMACSSFLOCSS などがあるが、本記事では ITCSS をプロダクトに利用した際の所感を含めて記載をしていく。

ITCSS とは

ITCSS は「Inverted Triangle CSS (逆三角形の CSS)」の略称である。

ITCSSの構造
ITCSSの構造

CSS のスタイル定義を詳細度の広い順に記述(階層化)することを提唱している。その記述が逆三角形として視覚化されるため、この呼名となっている。

レイヤー

スタイルの機能を基本的には下記のレイヤーで分類する。

順番 レイヤー
1 Settings
2 Tools
3 Generic
4 Elements (Base)
5 Objects
6 Components
7 Trumps (Utilities)

詳細度の広い順に定義をしていくため、下のレイヤーに行くにつれ詳細度は上がっていく。

Settings

プリプロセッサなどで利用するグローバル変数や全体の設定を置く。

ここではプロジェクト内のグローバルな設定を記述する。(SASS 変数や CSS Variables の定義)

$font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
  "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
  "Segoe UI Symbol", "Noto Color Emoji" !default;

$white: #fff !default;
$black: #000 !default;
$gray: #f8f9fa !default;

Tools

プリプロセッサで利用するmixinfunctionなどの定義を置く。

@function str-replace($string, $search, $replace: "") {
  $index: str-index($string, $search);

  @if $index {
    @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index +
            str-length($search)), $search, $replace);
  }

  @return $string;
}

@mixin text-truncate() {
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
}

Settings, Tools までのレイヤーで CSS を記述しない。

Generic

リセットスタイル(normalize.css など)や固有のリセットスタイル定義を置く。

Generic が CSS を記述する最初のレイヤーである。

@import "~normalize.css/normalize.css";
*,
*::before,
*::after {
  box-sizing: border-box;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin-top: 0;
  margin-bottom: 0;
}

印刷用 CSS(print.css)の定義ならここに置く。

Elements (Base)

素の HTML 要素(a, h1…6 など)のスタイルを定義する。クラスセレクターなどは使用せず、要素セレクターのみで定義する。

a {
  color: #333;
  text-decoration: none;
}

Elements 以下のレイヤーでは、要素セレクターは使用されない。要素レベルのスタイルを超えた実装はクラスを使用して実装する必要がある。

Objects

装飾を持たないレイアウトを定義する。(OOCSS)
また、プレフィックスに.o-が使われる。

.o-container {
  margin: 0 auto;
}

.o-layout {
  display: flex;
  align-items: center;
}

Components

サイト内のコンポーネント(UI パーツ)を定義する。
プレフィックスに.c-が使われる。

.c-alert {
  padding: 1rem 2rem;
  margin-bottom: 1rem;
  border: 1px solid #333;
  background: #eee;
}

Trumps (Utilities)

ヘルパー・ユーティリティ系のスタイルを定義する。

これまでのレイヤーを上書きするスタイルや明示的なスタイル定義(テキストの中央寄せ等)などスコープがもっとも狭い定義になる。

#main {
  margin: 1rem 1.5rem;
}
.text-justify {
  text-align: justify !important;
}
.text-nowrap {
  white-space: nowrap !important;
}

ここでは ID セレクターや!importantの定義がが許される。

構成

各レイヤーの定義をフラットに配置するよう提唱されている。

例:

@import "settings.global";
@import "settings.colors";

@import "tools.functions";
@import "tools.mixins";

@import "generic.box-sizing";
@import "generic.normalize";

@import "elements.headings";
@import "elements.links";

@import "objects.wrappers";
@import "objects.grid";

@import "components.site-nav";
@import "components.buttons";
@import "components.carousel";

@import "trumps.clearfix";
@import "trumps.utilities";
@import "trumps.ie8";

しかしながら、こうしたフラットな配置はファイル数が増えるにつれて見通しが悪くなる。そのため、レイヤー毎にディレクトリを切る運用が散見されているように思う。(参考:GitHub | Search - ITCSS


私の場合は下記のような形でディレクトリを切り各ファイルを配置している。

例:

style
 ├── Base
 │   ├── _global.scss
 │   └── _typography.scss
 ├── Components
 │   ├── _alert.scss
 │   ├── _breadcrumbs.scss
 │   ├── _button.scss
 │   └── _card.scss
 ├── Generic
 │   ├── _font.scss
 │   ├── _print.scss
 │   └── _reset.scss
 ├── Objects
 │   └── _layout.scss
 ├── Settings
 │   ├── _colors.scss
 │   └── _variables.scss
 ├── Tools
 │   ├── _animation.scss
 │   └── _mixins.scss
 └── Trumps
     └── _index.scss

ITCSS のメリット

プロダクトに ITCSS を取り入れた際に実感したメリット

柔軟性

ITCSS はフレームワークではなく、単なるガイドラインと言ってもいい。

「CSS のスタイル定義を詳細度順に記述する(詳細度を管理する)」という規則から逸脱していなければ、先述のようにレイヤーを追加や不要なレイヤーを削除することができる。
また、命名規則は特にないため、クラス命名もプロダクトに応じて自由に決めることができる。


たとえば、ITCSS には下記のレイヤーが欠けている。

  • ページ固有のスタイル
  • ライブラリ固有のスタイル

私のプロダクトでは、それぞれPagesVendorのレイヤーを追加して構築している。

順番 レイヤー
1 Settings
2 Tools
3 Generic
4 Elements (Base)
5 Objects
6 Components
7 Pages
8 Vendor
9 Trumps

これは ITCSS をベースとした CSS フレームワークも同様の手法を取っている事が多い。

管理コスト

「CSS のスタイル定義を詳細度順に記述する(詳細度を管理する)」というルールにより、スタイル適用状態の見通しが良くなった。

スタイルを詳細度順とカスケーディング順に記述することは、CSS が詳細度順に適応され、カスケーディングによって記述の後のものから適応される仕様に合致する。そのため、スタイルが適応される状態をコード上から想像しやすくなる。

「見出し(heading)」のスタイルを実装する場合、Elements レイヤーにはh1h6を整えるようなスタイル定義を行い、見出し・小見出しの実装は、Component レイヤーにクラスに対して実装を行う(必要がある)。一貫してこういったスタイル定義になるため、各レイヤーには役割を果たすために必要なスタイルだけが定義される形になる。

場合によっては書き分けることにより全体的なコード量は増してしまうのだが、定義自体は複雑にはならない。見通しの良いコードになるため、管理コストは非常に少なくなる。
実際、後からジョインしたメンバーへの教育コストは減った。また、「どこに何が定義されているのか」「どこに定義を追加すれば良いのか」など判断も容易であった(ようだ)。

パフォーマンス

ITCSS のルールに則れば、詳細度を効率的に管理でき、不本意な競合や不要なオーバーライドを少なくすることができる。それにより、拡張性と冗長性が向上し、ファイルサイズのムダが少なくなる。

実際、CSS が肥大化していた既存プロダクトの CSS を ITCSS で再構築してみると、CSS のファイルサイズを 15.5KB から 10KB まで削減することができた。元々の CSS が悪すぎたのも理由にあるが、他の非 ITCSS プロダクトでも平均で 45%程度は削減できている。

CSS のファイルサイズが小さければ読み込み時間も少なくなり、不要なスタイル定義が無ければレンダリングのコストも少なくなり、パフォーマンスにも寄与する。CSS の圧縮アルゴリズムを使った最適化や単純な minify 化より、根本的なパフォーマンス最適化につながる。

おわり

ITCSS は CSS 設計とクラスの命名規則がないため、既存プロダクトに盛り込みやすい。例えば、BEM など別の CSS 設計との組み合わせをすることも容易である。
単純に ITCSS の規則に則るだけで、これまで紹介したようなメリットを享受することができ、小規模・大規模問わずプロジェクトに拡張性の向上とメンテナンスしやすい CSS を構築することができると思う。

参考

記事

コードサンプル