AngularMaterialでカスタムテーマを作成する方法

Angular Materialは、Angular2 +のマテリアルデザインを実装する優れたライブラリです。コンポーネントの使用法については公式ドキュメントで十分ですが、テーマ自体、具体的にはテーマで使用される色をカスタマイズする方法についての記事はほとんどありません。

この投稿では、AngularMaterialテーマのカスタマイズから今月学んだことを要約したいと思います。

この記事は、AngularJS1.xで使用されるAngularJSマテリアルに関するものではないことに注意してください。

関連記事

テーマのカスタマイズに関する一般的な投稿は次のとおりです。

  • カスタムテーマの公式ガイドである「AngularMaterialアプリのテーマ設定」、
  • TomasTrajanによる「AngularMaterialThemesの完全ガイド」には、文書化されていない多くの手順が記載されています。強くお勧めします。

他に役立つ投稿は見つかりませんでした。コメントでリソースを提供していただければ幸いです。

カスタムテーマを作成する方法

マテリアルテーマの作成は非常に簡単です。プライマリアクセント警告の3色を選択するだけで、残りはAngularMaterialが自動的に行います。マテリアルパレットページでは、その仕組みが明確に説明されています。また、カラーツールを使用して視覚的にテーマを作成することもできます。

コードに関しては、次のテーマファイルを作成するだけです。

// [email protected] '[email protected]/material/theming';
$my-theme-primary: mat-palette($mat-green);$my-theme-accent : mat-palette($mat-amber);$my-theme-warn : mat-palette($mat-red);
$my-theme: mat-light-theme( $my-theme-primary, $my-theme-accent, $my-theme-warn);

次に、このテーマをメインstyle.scssファイルに適用する必要があります。

@import "theme.scss";
@include mat-core();@include angular-material-theme($my-theme);

コンポーネントでカスタムテーマを使用する方法

独自のテーマを作成した後、次のような要件が発生します。

テキストボックスを作成したい。テキストの色、背景色、境界線の色はすべて、ハードコーディングではなく、独自のテーマから取得する必要があります。

この要件は非常に一般的です。とにかく、コンポーネントで使用できることが、カスタムテーマを作成したい理由です。問題はその方法です。

ミックスインアプローチ

私が共有した最初の公式文書は、SCSSのミックスインを使用する方法を提案しました。私はこれを「ボトムアップ」アプローチと呼んでいます。これには次の手順が含まれます。

  1. 各コンポーネントはテーマミックスインを定義し、$themeパラメーターから色を取得します。
  2. グローバルtheme.scssはカスタムテーマを定義し、次にすべてのコンポーネントテーマミックスインを含み、カスタムテーマでそれらを呼び出します。

theme.scss上記の定義に加えて、各コンポーネントは次のようなテーマファイルを作成する必要があります。

// src/app/comp-a/[email protected] '[email protected]/material/theming';
@mixin comp-a-theme($theme) { // define mixin $primary: map-get($theme, primary); // retrieve color def button { // apply theme to component background-color: mat-color($primary); }}

そして、おそらくあなたはcustom-theme.scssすべてのコンポーネントレベルのテーマをインポートしたいと思うでしょう:

// src/app/[email protected] '[email protected]/material/theming';@import 'src/app/comp-a/comp-a.theme';@import 'src/app/comp-b/comp-b.theme';
@mixin custom-themes($theme) { @include comp-a-theme($theme); @include comp-b-theme($theme);}

次に、上記custom-theme.scssをあなたのtheme.scss:にインポートします

// [email protected] './custom-theme';@include custom-themes($my-theme);

この階層は機能し、複数のテーマをサポートする必要がある場合はおそらくこれが唯一の方法です

ただし、ほとんどの場合、サポートするテーマは1つだけであり、ミックスインの使用は面倒な場合があります。このアプローチには、主に3つの欠点があります。

  1. すべての単一の色の参照には、個別の.theme.scssファイルが必要です。
  2. custom-theme.scssどのコンポーネントがカスタムテーマを提供するかを正確に知る必要があります。これにより、不要な依存関係が作成されます。
  3. 最も重要なのは、コンポーネントレベルのテーマファイルがカプセル化されていないことです。

1番目と2番目のポイントはかなり自明です。ポイント3について少し説明します。これには、「ビューのカプセル化」と呼ばれる背景知識が含まれます。

Angular uses a technique called “View Encapsulation” to keep component CSS local. In other words, rules defined for one component will stay in that component and will not affect other components.

In this way you can define CSS class name freely in your component without worrying about naming conflicts. However, view encapsulation is done only if the CSS is defined through @Component, i.e. @Component({ styleUrls: ['./comp-a.scss'] }).

As to our custom theme file comp-a.theme.scss, since it is imported directly by custom-theme.scss, its rules are not encapsulated so it will apply to all elements on the page. In the example above, I used the following code (which was WRONG!):

@mixin comp-a-theme($theme) { button { ... } // This will apply to ALL buttons!}

But this will apply the style to all the buttons instead of those buttons belonging to comp-a only. You have to do something like comp-a button in order to make this work correctly.

The direct approach

Therefore I propose a better approach. Instead of using a mixin, we let each component include the theme file and use the color definition directly.

In this approach, the component theme file will look like this:

// NOTE: just do this in your regular scss file.// No need to create separate theme file!// src/app/comp-a/[email protected] 'src/theme.scss';
$primary: map-get($my-theme, primary);button { background-color: mat-color($primary);}

And that’s all.

Let’s see how this works. First, theme related rules are put into the component SCSS file, so no extra component level theme file required. Second, the main theme.scss does not need to know component level themes (since it does not need to import them), so a simple theme definition is adequate. Third, the component SCSS file is used with @Component so it is encapsulated correctly, which means we can simply define rules for button.

Predefined Theme Keys

Probably you have noticed the next problem. What are the foreground, primary in above theme files ( map-get($my-theme, primary))? Are there any other keys I can use?

Well these “keys” refer to different colors defined in the theme. However I could not find any documents explaining these “keys”, so the only way I could find out is to read the source code. (Although it is said that good programmers should read the code, having to read the code is definitely not a good sign for a library.)

Open node_modules/@angular/material/_theming.scss and you will see the definitions for these keys. For future reference, I would like to summarize the keys here.

$theme |- primary |- accent |- warn |- foreground | |- base | |- divider | |- dividers | |- disabled | |- disabled-button | |- disabled-text | |- hint-text | |- secondary-text | |- icon | |- icons | |- text | |- slider-min | |- slider-off | `- slider-off-active |- background | |- status-bar | |- app-bar | |- background | |- hover | |- card | |- dialog | |- disabled-button | |- raised-button | |- focused-button | |- selected-button | |- selected-disabled-button | |- disabled-button-toggle | |- unselected-chip | `- disabled-list-option `- is-dark // bool, whether dark theme or not

For example, if you want to render a disabled text in your component, you may want to use the following code:

$foreground: map-get($my-theme, foreground);.disabled-text { color: mat-color($foreground, disabled-text);}

Okay these are some lessons I’ve learned from struggling with Angular Material. Hope this post is helpful if you are facing similar problems.