Angular Material font hack ! for Japanese

この記事は Classi Advent Calendar 2019 10 日目の記事になります。フロントエンドエンジニアの@fusho-takahashi です。

私はデザイナーとしての業務経験もあり、デザインの中でもフォントが好きです。デザインにおいてフォントはとても大切な要素で、デザインの質や効果を左右します。
Angular使いの強い味方であるAngular Materialですが、日本語がメインのサイトでMaterial Design に準拠するには、日本語のための設定をする必要がありますので、今回はAngular Material のフォントをhackしていきたいと思います！

# ng add しただけでは日本語フォントがデバイス依存になる

せっかくAngular Material を入れているのに日本語フォントを設定しないのは、かなりもったいないです！
デフォルトではRobotoフォントが使われているので、Material Design 推奨和文フォントのNoto Sans JP に変更しましょう。

```diff:src/index.html
- <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
+ <link href="https://fonts.googleapis.com/css?family=Noto+Sans+JP:300,400,500&display=swap" rel="stylesheet">
```

```diff:src/styles.scss
body {
  margin: 0;
-  font-family: Roboto, "Helvetica Neue", sans-serif;
+  font-family: "Noto Sans JP", "Helvetica Neue", sans-serif;
}
```
# Material Design のTypography
Angular Material は最新のMaterial DesignのTypographyには準拠していませんので、Angular Material のTypographyがベースにしているGoogle I/O 2018 以前のもの（ https://material.io/archive/guidelines/style/typography.html# ）についてお話します。

Material Designでは一貫性を持たせるために、用途によって11種類のフォントスタイルを定義しています。
![script](https://material.io/archive/guidelines/assets/0Bzhp5Z4wHba3alhXZ2pPWGk3Zjg/style-typography-styles-scale.png)

Angular Material では、さらにsubheading をもう一種類と入力フォーム用のスタイルを追加した13種類のスタイルをcssで定義しており、html にclass をつけることでスタイルを適用します。(button とinput はAngular MaterialのComponentの中で使われることを想定していて、classは定義されていません）

|style name  |CSS classes  |
|---|---|---|
|display-4  |.mat-display-4  |
|display-3  |.mat-display-3  |
|display-2  |.mat-display-2  |
|display-1  |.mat-display-1  |
|headline  |.mat-h1, .mat-headline  |
|title  |.mat-h2, .mat-title  |
|subheading-2  |.mat-h3, .mat-subheading-2  |
|subheading-1  |.mat-h4, .mat-subheading-1  |
|body-1  |.mat-body, .mat-body-1  |
|body-2  |.mat-body-strong, .mat-body-2  |
|caption  |.mat-small, .mat-caption  |
|button  |-  |
|input  |-  |

ドキュメントには書いてありませんが、ソースコードを見ると.mat-h5 と.mat-h6 も定義されているようです。

# Typography をカスタマイズする
上記のスタイルはカスタマイズする事ができます！日本語用にするために、まずはカスタマイズ方法をドキュメントの例を元にご説明します。

```scss
@import '~@angular/material/theming';

$custom-typography: mat-typography-config(
  $font-family: 'Roboto, monospace',
  $headline: mat-typography-level(32px, 48px, 700),
  $body-1: mat-typography-level(16px, 24px, 500)
);

@include angular-material-typography($custom-typography);
```

まずmat-typography-config関数にカスタマイズ内容を記述します。

mat-typography-config関数の引数は$font-familyと前述した表の各 'style name' です。全体のfont-familyと13種類のスタイルをそれぞれ設定できます。全ての引数にはデフォルト値が設定されていますので、特に手を加える必要のないものは書かなくてOKです。

続いて各スタイルを定義しているmat-typography-level関数です。こちらの引数とデフォルト値は下記の通りです。

|引数|デフォルト値|
|---|---|
|$font-size|なし|
|$line-height|$font-size|
|$font-weight|400|
|$font-family|null|
|$letter-spacing|null|

最後に$custom-typographyを引数にangular-material-typographyを呼び出してスタイルを適用します。

# 日本語用にカスタマイズする

下記2点のカスタマイズを行います。

- font-familyをNoto Sans JP に変える
- font-sizeとline-heightを大きくする

font-familyは良いとして、2点目について少し説明します。

Googleではすべての言語を3つのカテゴリに分類しており、日本語はその中で中国語や韓国語と同じ 'Dense' という、文字の密度が高いカテゴリに属しています。Material DesignではこのDanseに属する言語はフォントサイズが小さくなったとき（具体的には16px以下）に欧文で指定されているfont-sizeより1px大きくなります。line-heightはすべて欧文に比べて0.1em大きくします。

具体的には下記のような設定になります。

```src/styles.scss
$custom-typography: mat-typography-config(
  $font-family: '"Noto Sans JP"',
  $display-4: mat-typography-level(112px, 123px, 300, $letter-spacing: -0.05em),
  $display-3: mat-typography-level(56px, 62px, 400, $letter-spacing: -0.02em),
  $display-2: mat-typography-level(45px, 53px, 400, $letter-spacing: -0.005em),
  $display-1: mat-typography-level(34px, 44px, 400),
  $headline: mat-typography-level(24px, 35px, 400),
  $title: mat-typography-level(20px, 32px, 500),
  $subheading-2: mat-typography-level(17px, 30px, 400),
  $subheading-1: mat-typography-level(16px, 26px, 400),
  $body-2: mat-typography-level(15px, 26px, 500),
  $body-1: mat-typography-level(15px, 22px, 400),
  $caption: mat-typography-level(13px, 20px, 400),
  $button: mat-typography-level(15px, 15px, 500)
);

@include angular-material-typography($custom-typography);
```

# 文字のコントラスト
これは日本語だけではないですが、背景と文字コントラストが強すぎるとテキストが読みにくくなります。コントラストが強いというのは、明るい部分と暗い部分の差が大きいということです。初期状態の背景#ffffff・文字が#000000の場合はコントラストがとても強いので、ユーザーに優しくありません。Material Designでは文字と背景のコントラスト比についても言及されており、とくに明るい背景の場合は各スタイルに87%もしくは54%のopacityを適用します。styles.scssに追記しましょう。

```src/styles.scss
...
.mat-display-4,
.mat-display-3,
.mat-display-2,
.mat-display-1,
.mat-small,
.mat-caption {
  opacity: 0.54;
}

.mat-h1,
.mat-headline,
.mat-h2,
.mat-title,
.mat-h3,
.mat-subheading-2,
.mat-h4,
.mat-subheading-1,
.mat-body,
.mat-body-1,
.mat-body-strong,
.mat-body-2 {
  opacity: 0.87;
}
```

#まとめ
以上の設定で、Angular Materialが日本語仕様になりました！
Angular Materialを使うときはコンポーネントに目が行きがちですが、これを機にぜひTypographyも気にしてみてください。
もっとこだわりたい人は、ゴリゴリカスタマイズしていきましょう！

明日は @toku345 さんです。お楽しみに〜