next/font/googleでビルドが止まったので、JetBrains Monoを同梱してnext/font/localで読み込んだ

本記事にはアフィリエイト広告(PR)を含みます。詳しくは広告・アフィリエイトについてをご確認ください。

このサイトでは、ラベルや見出しの記号に等幅フォントの JetBrains Mono を使っています。最初は Next.js の next/font/google で読み込んでいましたが、ビルドが終わらなくなる問題が起きました。この記事では、フォントファイルをリポジトリに入れて next/font/local で読み込む形に切り替えた手順を書きます。

何が起きたか

next/font/google は、ビルドのときに Google Fonts からフォントを取りにいきます。私の環境では、この取得が止まってしまい、5分待ってもビルドが終わりませんでした。

同じ環境から curl で Google Fonts の CSS を取りにいくと、普通に返ってきます。なので、ネットワークがまったくつながらないわけではありません。ビルドの中での取得だけが止まっていたので、ビルド環境のネットワーク設定(プロキシなど)との相性が原因だと考えています。

原因を突き詰めるより、ビルドがネットワークに頼らない形にしたほうが確実です。そこで、フォントファイルをリポジトリに同梱することにしました。

フォントファイルを用意する

Google Fonts の CSS から、ファイルの URL を調べる

Google Fonts の CSS を取得すると、文字の範囲(サブセット)ごとに、フォントファイルの URL が書かれています。ブラウザ風の User-Agent を付けると、woff2 形式の URL が返ってきます。

curl -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124 Safari/537.36" \
  "https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap"

返ってきた CSS には、cyrillic、greek、latin などのコメントが付いた @font-face が並んでいます。このサイトで等幅フォントを使うのは、英数字と記号だけです。なので、/* latin */ の部分の URL だけを使いました。

太さ違いが同じファイルだった

wght@400;700 で2つの太さを指定したのに、latin の 400 と 700 の URL はまったく同じでした。JetBrains Mono は可変フォント(1つのファイルで太さを連続的に変えられるフォント)として配信されているからです。ファイルは1つあれば足ります。

ダウンロードしたファイルは src/fonts/JetBrainsMono-latin-variable.woff2 に置きました。

ライセンスの文書も一緒に置く

JetBrains Mono は SIL Open Font License(OFL)で公開されています。OFL では、フォントを同梱して配布することが認められていますが、ライセンスの文書を一緒に付ける必要があります。JetBrains Mono のリポジトリにある OFL.txt を取得して、フォントと同じ src/fonts/ に置きました。

next/font/local で読み込む

app/layout.tsx で、next/font/local を使って読み込みます。

import localFont from "next/font/local";

const jetBrainsMono = localFont({
  src: "../fonts/JetBrainsMono-latin-variable.woff2",
  weight: "100 800",
  variable: "--font-jetbrains-mono",
  display: "swap",
});

可変フォントなので、weight は「100 800」のように範囲で書きます。variable を指定すると、CSS 変数としてフォント名を参照できるようになります。

この CSS 変数を使うために、html 要素にクラスを付けます。

<html lang="ja" className={jetBrainsMono.variable}>

CSS 側では、この変数を先頭にしたフォントの指定を作っておきます。

:root {
  --font-mono:
    var(--font-jetbrains-mono), ui-monospace, "SFMono-Regular", Menlo, Consolas,
    "Hiragino Sans", "Hiragino Kaku Gothic ProN", monospace;
}

latin のサブセットには日本語の文字が入っていません。日本語の部分は、後ろに並べた日本語フォントで表示されます。

これで、ビルドのときにネットワークへ取りにいくことがなくなり、ビルドが止まることもなくなりました。

おまけ、記事のコードでは合字を切る

JetBrains Mono には合字(リガチャ)があり、=> や === が1つの記号のように表示されます。エディタでは読みやすいのですが、記事のコードを書き写す人が「この記号はどう打つのか」と迷います。

そこで、記事の中のコードだけ、合字を切っています。

code {
  font-variant-ligatures: none;
}

ロゴや見出しの記号のような飾りの部分は、合字のままにしています。

⎇ main✓ 13 deps9 articles
UTF-8LFMDX