Cover image for webpack+Sass+reactでカスタムフォントを使う
webpackreactsassfont

webpack+Sass+reactでカスタムフォントを使う

October 23, 2016

8 min read

mitsuruogMitsuru Ogawa

アイコンなどの画像には、少し前まで CSS Sprites を使うことが多かったと思います。しかし、最近は CSS でスタイリングできるため、カスタムフォントを作成して使用することが増えてきたように感じます。

今回は、webpack と Sass を使ってカスタムフォントを使用する方法と、その際にハマりやすいurl()での相対パス指定方法について紹介します。

tl;dr

  • Sass でのカスタムフォントの使い方
  • webpack でカスタムフォントをロードする方法
  • Sass でのurl()での相対パスの指定方法のハマりポイント

はじめに

はじめに登場人物が多いので、全体のフォルダ構成を明らかにしておきます。 JavaScript のフレームワークは React を使ったのもを想定していますが、カスタムフォント周りの設定については他のフレームワークでも代用できると思います。

src/
  components/
    icon/
      icon.scss
      icon.jsx
      My-font.eot
      My-font.woff
      My-font.ttf
      My-font.svg
  entry.js
  style
    style.scss
 webpack.config.js

entry.jsがアプリケーションのエントリーファイルです。 style.scssは Sass のエントリーファイルで、icon.scss@importしています。 entry.jsがロードされる時に、style.scssもロードされインポートされている Scss ファイルがコンパイルされます。

// src/style.scss
@import "../components/icon/icon.scss";

Sass でのカスタムフォントの使い方

Sass ファイルでカスタムフォントを読み込む

カスタムフォントを読み込むには@font-faceを利用してfont-familyにフォント名を設定します。

カスタムフォントの読み込みについては、Sass ではなく CSS の範疇ですし、詳細については他に良い記事が多くあるのでここでは触れません。

// src/components/icon/icon.scss
@font-face {
  font-family: "SuperCoolMyFont";
  src: url("./My-font.eot"),
    /* IE9 Compat Modes */ url("./My-font.eot?iefix") format("embedded-opentype"),
    /* IE6-IE8 */ url("./My-font.woff") format("woff"),
    /* Pretty Modern Browsers */ url("./My-font.ttf") format("truetype"), /* Safari, Android, iOS */
      url("./My-font.svg#myfont") format("svg"); /* Legacy iOS */
}

icon コンポーネントの作成

icon コンポーネントは stateless function components で作成した非常にシンプルなものです。 将来的には、アイコンの種類やサイズを prop から渡せるようにするべきだと思います。

// src/components/icon/icon.jsx
exports default function Icon() => {
  return <span className="icon" />
}

icon のスタイリング

icon のスタイリングについては、Bootstrap など既存の CSS フレームワークを参考にするのがいいと思います。 基本的には icon のベースとなるスタイルにfont-familyでフォントを指定した後、contentでフォントの Unicode を設定すればいいです。

// src/components/icon/icon.scss
.icon {
  position: relative;
  display: inline-block;

  font-family: "SuperCoolMyFont";
  font-style: normal;
  font-weight: normal;
  font-size: 24px;

  line-height: 1;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  &:before {
    content: "\01";
  }
}

フォントの Unicode の探し方(番外編)

フォントの Unicode の確認方法については、自分の場合は SVG ファイルから探すようにしています。 (正直、これがスマートな方法かどうかわかりません。)

SVG ファイルを開くとunicode要素があるので、そこから Unicode を抽出します。 例えば、unicode="&#x01;"の場合は&#x\に変換して\01として使います。

// src/components/icon/My-font.svg
// 抜粋です
<glyph unicode="&#x01;" d="something cool shape" />

もっと正しい方法があったら教えて欲しいです。

webpack でカスタムフォントをロードする方法

webpack でカスタムフォントを使うためには、url-loaderを使って各フォントファイルをロードできるようにする必要があります。

// webpack.config.js
// 抜粋です
loaders: [
  {
    test: /\.svg$/,
    loader: "url?mimetype=image/svg+xml&name=[path][name].[ext]",
  },
  {
    test: /\.woff$/,
    loader: "url?mimetype=application/font-woff&name=[path][name].[ext]",
  },
  {
    test: /\.woff2$/,
    loader: "url?mimetype=application/font-woff2&name=[path][name].[ext]",
  },
  {
    test: /\.[ot]tf$/,
    loader: "url?mimetype=application/octet-stream&name=[path][name].[ext]",
  },
  {
    test: /\.eot$/,
    loader:
      "url?mimetype=application/vnd.ms-fontobject&name=[path][name].[ext]",
  },
];

nameの部分は好みで変更してください。

私の場合は SVG ファイルをフォントと画像で利用することが多く、これらを別のフォルダで管理することが多いので、格納元のディレクトリ構造そのままで出力してくれる[path][name].[ext]を好んで利用しています。

Sass でのurl()での相対パスの指定方法のハマりポイント

ところが、上の方法では font ファイルが参照できずエラーとなってしまいます。

Module not found: Error: Cannot resolve 'file' or 'directory' . /My-font.svg

これは現時点では、Sass のコンパイラが URL の書き換えをサポートしていないため、css-loaderを利用する場合は Sass のエントリーファイルから相対パス指定する必要があるからです。

そのため、上のicon.scssでのパス指定は次のようにする必要があります。

// src/components/icon/icon.scss
$font-path: "../components/icon/";
@font-face {
  font-family: "SuperCoolMyFont";
  src: url("#{$font-path}My-font.eot"),
    /* IE9 Compat Modes */ url("#{$font-path}My-font.eot?iefix") format("embedded-opentype"),
    /* IE6-IE8 */ url("#{$font-path}My-font.woff") format("woff"),
    /* Pretty Modern Browsers */ url("#{$font-path}My-font.ttf") format("truetype"),
    /* Safari, Android, iOS */ url("#{$font-path}My-font.svg#myfont") format("svg"); /* Legacy iOS */
}

これで、相対パスの問題は解決されてフォントを読み込むことができるました。しかし、相対パスの指定が実物のフォントと異なるので違和感が残ります。

私の場合、$font-pathを Sass のエントリーファイルに移動することで、その違和感を解消しています。

// src/style.scss
$font-path: "../components/icon/";
@import "../components/icon/icon.scss";

// src/components/icon/icon.scss
@font-face {
  font-family: "SuperCoolMyFont";
  src: url("#{$font-path}My-font.eot"),
    /* IE9 Compat Modes */ url("#{$font-path}My-font.eot?iefix") format("embedded-opentype"),
    /* IE6-IE8 */ url("#{$font-path}My-font.woff") format("woff"),
    /* Pretty Modern Browsers */ url("#{$font-path}My-font.ttf") format("truetype"),
    /* Safari, Android, iOS */ url("#{$font-path}My-font.svg#myfont") format("svg"); /* Legacy iOS */
}

これなら違和感なしです。

まとめ

webpack+Sass でカスタムフォントを使う方法でした。 Sass の相対パスまわりでハマるケースが多いのではないかと思います。

url()に関してはフォントだけではなく画像についても同じことが言えるので、それぞれのエコシステムの癖を把握する必要があることを痛感しました。 この辺りは、あまり最近のフロントエンド開発に慣れてない人にとっては、学習が難しい部分となりそうな予感がします。

この記事を書くにあたって、次の記事も参考にしています。