LoginSignup
8

More than 3 years have passed since last update.

Angular アプリケーションで ITCSS やってみた

Last updated at Posted at 2019-07-01

Global CSSをどう管理するか

まだ@kouさんのAngularアプリケーションにおけるCSS設計手法を読んだことがない人はぜひ読んてみて。要諦はこのスライドに集約されているように思う。

Global CSSを一切使わずに済むのが理想的、だけど、なかなかそう綺麗にはいかないもの。また、CSSライブラリなんかはそもそもGlobal CSSとして読み込む必要があったりして、読み込み順序に気を付ける必要があったりする。これら諸々を適切に管理するために、ITCSSを導入してみた。

管理対象のGlobal CSSの種類

  • CSS Variables、Sass variables、mixin、function
  • サードパーティのCSSライブラリやフレームワーク(normalize.css, Bootstrap, etc...)
  • Componentをまたいで使用したいユーティリティクラス

などなど。

ITCSSを用いたCSS設計

ITCSSとはCSS設計における方法論の一つ。詳しくは以下を参照。非常によくまとまっていて参考になる。

ITCSSまとめ

ITCSSレイヤー名のアレンジ

一応、レイヤー名は決まったものがあるものの、馴染みがなかったりしっくりこないものがあるので、アレンジしてみた。

オリジナル > アレンジ後 変更理由
Settings > Variables 『変数』以外の設定はないのでこちらの方が直感的。
Tools > Tools -
Generic > Base アプリケーションのスタイリングの土台という意味を込めて。normalize.cssやminireset.cssなどがそれに当たる。他にも、BootstrapやBulmaなどの土台となるCSSフレームワークもここに入れることを意図している。
Base > Elements 要素セレクタに関するレイヤーであることが自明なので。この変更は事例が多い。
Objects > Objects -
Components > Components -
Trumps > Utilities 実は今はUtilitiesが正式名称。
Harry Roberts氏が自身のTwitterで述べている。

Angularプロジェクト上のフォルダ構成例

<project-name>
 └── src
      └── styles
           ├── variables
           │    ├── _colors.scss
           │    ├── _fonts.scss
           │    └── _index.scss
           ├── tools
           │    ├── _functions.scss
           │    ├── _mixins.scss
           │    ├── _xxx.scss
           │    └── _index.scss
           ├── base
           │    └── _index.scss
           ├── elements
           │    ├── _xxx.scss
           │    └── _index.scss
           ├── objects
           │    ├── _xxx.scss
           │    └── _index.scss
           ├── components
           │    ├── _angular-material.scss
           │    └── _index.scss
           ├── utilities
           │    ├── _xxx.scss
           │    └── _index.scss
           └── main.scss
  • まずstylesフォルダを作り、その下にITCSSのレイヤーごとのフォルダを作成。ITCSS的にはフラットな構成が推奨だけど、ファイルが多くなると煩雑になるので、あらかじめレイヤーごとにフォルダを用意しておく。
  • src/styles.scss -> src/styles/main.scss に変更し、これを単一エントリポイントとした(main.tsのような使い方)。
  • main.scss以外は全てファイル名にアンダースコアをプレフィックスしパーシャルファイル(_xxx.scss)として作成。
  • 各レイヤーフォルダ直下には<layer>/_index.scssファイルを置いて、他のパーシャルファイルをまとめる(index.tsのような使い方)。

angular.jsonの変更

angular.json
      ...
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            ...
            "stylePreprocessorOptions": {
              "includePaths": ["src/styles"]
            },
            "styles": [
              "src/styles/main.scss"
            ],
            ...
          },
          ...
  • src/styles/main.scssをビルドできるようにsrc/styles.scss -> "src/styles/main.scss"に変更。
  • "stylePreprocessorOptions.includePaths"オプションで"src/styles"を指定しimportパスを簡略化しておく。

実装例

Variables

定義する変数そのものが少ないなら_index.scssに直接書き込んでしまってもいいし、多くなるなら用途ごとにパーシャルファイルに切り出してimportしてもいい。

src/styles/variables/_index.scss
// colors
$primary-color: red;
$secondary-color: blue;

// fonts
$base-font-size: 16px;
...

// パーシャルファイルに切り出した場合
// @import 'colors';
// @import 'fonts';
...

Tools

_index.scssに直接書き込んでしまってもいいし、膨れ上がってきたら用途ごとにパーシャルファイルに切り出してimportしてもいい。

src/styles/tools/_index.scss
// functions
@function xxx

// mixins
@mixin yyy

// パーシャルファイルに切り出した場合
// @import 'functions';
// @import 'mixins';
...

ちなみに、VariablesやToolsをComponent CSSなどで使用したい場合は以下のようにimportできる。

xxx.component.scss
@import 'variables/index';
@import 'tools/index';
...

Base

土台となるサードパーティーのCSSをimportする。

src/styles/base/_index.scss
@import '~minireset.css/minireset';

Elements

_index.scssに直接書き込んでしまってもいいし、多くなるようならセレクタごとにパーシャルファイル作ってimportしてもいい。ただ、リセット系のCSSを入れているなら、このレイヤーで独自にCSSを書くことはほとんどなさそう。

src/styles/elements/_index.scss

h1 {
  ...
}

// @import 'headings';
...

Objects

わざわざGlobal CSSとしてこのレイヤーでスタイリングしなければならないものがあるのか微妙。このレイヤーは必要性が薄いような気がする。

src/styles/objects/_index.scss
@import 'xxx';
...

Components

Angularアプリケーションの場合、基本的に自作ComponentのスタイルはComponent CSSに書くべきなので、ここに自作Componentのスタイルを書いてはダメ。もしここが膨れるようなら、そもそもComponentとして切り出せないかを検討すべき。
ではこのレイヤーは何のためにあるかというと、Angular Materialなどのサードパーティー製のCSSをimportするために使う。

src/styles/components/_index.scss
@import 'angular-material';
@import 'another-third-party-components';
...

Angular Materialの場合、Sassだと記述が多くなるので、こんな感じでパーシャルファイルに分けてimportしたほうがいいと思う。

src/styles/components/_angular-material.scss
@import '~@angular/material/theming';
// Plus imports for other components in your app.

// Include the common styles for Angular Material. We include this here so that you only
// have to load a single css file for Angular Material in your app.
// Be sure that you only ever include this mixin once!
@include mat-core();

// Define the palettes for your theme using the Material Design palettes available in palette.scss
// (imported above). For each palette, you can optionally specify a default, lighter, and darker
// hue. Available color palettes: https://material.io/design/color/
$candy-app-primary: mat-palette($mat-indigo);
$candy-app-accent:  mat-palette($mat-pink, A200, A100, A400);

// The warn palette is optional (defaults to red).
$candy-app-warn:    mat-palette($mat-red);

// Create the theme object (a Sass map containing all of the palettes).
$candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn);

// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
// that you are using.
@include angular-material-theme($candy-app-theme);

Utilities

サードパーティーのユーティリティ系のCSSをimportする、あるいは、独自のユーティリティのスタイルを書く。他のレイヤーと同様、記述が多くなるならパーシャルファイルに切り出してもいい。

src/styles/utilities/_index.scss
// animate.css
@import '~animate.css/animate';

// fontawesome
// パスの解決のために$fa-font-pathの上書きが必要
// https://github.com/vuejs-templates/webpack/issues/1313#issuecomment-460714033
$fa-font-path: '~@fortawesome/fontawesome-free/webfonts';
@import '~@fortawesome/fontawesome-free/scss/solid';
@import '~@fortawesome/fontawesome-free/scss/fontawesome';

// その他独自のユーティリティ
@import 'aligns';
@import 'fonts';
@import 'widths';
...

main.scss

各レイヤーに_index.scssを置いたおかげでmain.scssは以下のようにスッキリ書ける。基本ここにはCSSを直接書かずimportのみにする。

src/styles/main.scss
@import 'variables/index';
@import 'tools/index';
@import 'base/index';
@import 'elements/index';
@import 'objects/index';
@import 'components/index';
@import 'utilities/index';

QA

Q.サードパーティのCSSフレームワークやライブラリはVendorsレイヤーとか作ったほうがいいんじゃない?

A. そんなこともない

詳細度の観点からみると、一律Vendorsというレイヤーに押し込むのは無理があるかなという気がしてる。

レイヤー CSS Library CSS Framework
Variables Bootstrap
Bulma
Tools
Base minireset.css
normalize.css
Elements
Objects
Components Angular Material
Utilities Animate.css
Font Awesome

一口にCSSライブラリ・フレームワークといっても、上記のように、それらがカバーするレイヤーは異なるので、詳細度の異なるライブラリーを「サードパーティ」と一括りにしてVendorsレイヤーに押し込めると、他のレイヤーのスタイルと詳細度の順序で問題になりそう。なので、サードパーティであっても特別扱いせず、自作スタイルと同じように、それぞれの詳細度に応じたレイヤーに配置したほうが良いかなと思っている。

Q. なんでBootstrapとかBulmaはBaseレイヤーに入れてる?

A. 土台となるCSSだから

いろいろと調べてみると、BootstrapはObjectsレイヤーに入れてるよ、といったコメントがStackoverflowなどで見つかったりする。でも、Bootstrapはそれ自身でVariables ~ Utilitiesに相当するレイヤーを持っているので、ITCSSのレイヤー上のどこにも当てはまらない。とはいえ、どこかのレイヤーでimportしなくてはいけないので、じゃあどこにするか?と考えたときに、Baseレイヤーでimportしておくと、下のレイヤーでオーバーライドしやすく、逆に、変数は基本!default指定されているので、上のレイヤーでオーバーライドできるようになる。
なにより、フレームワーク≒土台としてのライブラリなのでBaseレイヤーに入れると概念的にしっくりくる。

Q. Componentsレイヤーは必要なの?

A. サードパーティーのコンポーネントCSSをimportするのに必要

Angularアプリの場合、このレイヤーは一見不要では?と疑問に感じるかもしれない。なぜならComponentのCSSにはComponent CSSに記述すべきで、ここにあってはならないので。もしここにスタイルを書こうと思ったら、その前にComponentとして切り出せないかを検討すべき。

では、このレイヤーの存在意義は何かというと、それはAngular MaterialなどのUI Component用のCSSをimportするため。例えばAngular MaterialのComponentはViewEncapsulation.Noneで実装されていて、CSSはGlobal CSSとして存在しているので、どこかでimportしないといけない。それをimportするのがこのレイヤーの役割となる。その他サードパーティ製UI ComponentもGlobal CSSとしてimportが必要になるものは同様。Components/_index.scssで直接importしてもよいし、記述量が多くなるようなら以下のように別途パーシャルファイルを作ってもよい。

Sassファイル内でCSSファイルインポートすると、読み込み順序変わっちゃうから期待どおりの読み込み順序にならないよ?

Sassでよくあるトラブルとして、CSSファイルをSassファイルと混在してimportをすると、ビルド後のimport順序が変わってしまうというものがある。

この問題は「.css」拡張子なしでインポートすることで解決することができる。詳しくは以下を参照。
https://github.com/sass/node-sass/issues/2362#issuecomment-388634848

やってみた所感

  • 初期のタイミングでは若干オーバースペックな感じもするけど、レイヤーごとに管理することで、どこにどんなスタイルをおけば良いか分かりやすくなるので、スタイルが増えてきたときにスケールしやすい感じ。
  • CSSライブラリの読み込み順序は意外とおざなりになりがちだけど、ITCSSを使うとどのレイヤーでimportすべきかを考えざるをえなくなるので、詳細度をしっかりとコントロールできる。結果として、意図しないスタイルのオーバーライドを未然に防ぐことにつながる。
  • Angularに限らず、React とか Vue でも適用可能だと思う。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8