🙌

AppRouterとTailwindCSSを使っているNextプロジェクトでAdobeFontsを使ってみた

2023/09/22に公開

概要

AdobeFontsでは契約しているライセンスが有効な間、Webサービス向けにフォントを使用することができます

「Webプロジェクトに追加」を選ぶとJSスクリプトが表示されます

これをどうにかしてNext13のプロジェクトで使いたいのですがパッと思いつくdangerouslySetInnerHTMLはあまりにダーティーなので、コンポーネント化してみました
何故かまだあまり情報が出回っていなかったのでメモしておきます

env

  • typescript: "5.2.2"
  • next: "13.5.2"
  • react: "18.2.0"
  • tailwindcss: "3.3.3"

TypekitLoaderコンポーネントの作成

原文で渡されるスクリプトを示します。CSSテキストを直接読みにいっているわけではなく、kitIdをクエリとしたjsを呼んで環境に適用するものであることがわかります。

<script>
  (function(d) {
    var config = {
      kitId: 'xxxxxx',
      scriptTimeout: 3000,
      async: true
    },
    h=d.documentElement,t=setTimeout(function(){h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+=" wf-loading";tk.src='https://use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&&a!="complete"&&a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s)
  })(document);
</script>

これと同等のものをコンポーネントとして実装します。
このスクリプトはクライアントサイドで呼び出してあげる必要があるので、use clientをつけてクライアントコンポーネントとして作成しました。

できるだけ元のスクリプトを踏襲して書きましたが、読みにくいのでアレンジしたほうがいいかもです。

TypekitLoader.ts
"use client"
import { useEffect } from 'react';

const TypekitLoader: React.FC = () => {
  useEffect(() => {
    (function (d: Document) {
      const config = {
        kitId: `${process.env.NEXT_PUBLIC_ADOBE_FONTS_KIT_ID}`,
        scriptTimeout: 3000,
        async: true,
      };
      const h = d.documentElement;
      const t = setTimeout(() => {
        h.className = h.className.replace(/\bwf-loading\b/g, '') + ' wf-inactive';
      }, config.scriptTimeout);
      const tk = d.createElement('script');

      h.className += 'wf-loading';
      tk.src = `https://use.typekit.net/${config.kitId}.js`;
      tk.async = true;
      tk.onload = function () {
        clearTimeout(t);
        try {
          (window as any).Typekit.load(config);
        } catch (e) {}
      };
      tk.onerror = function () {
        clearTimeout(t);
      };

      const s = d.getElementsByTagName('script')[0];
      s.parentNode!.insertBefore(tk, s);
    })(document);
  }, []);

  return null;
};

export default TypekitLoader;

エラー処理とかはいい感じに変更しましょう。
useEffectを使ってロード時に呼び出しています。

複数のフォントを使う場合も似たような方針で書けるかと思います。

環境変数の指定

kitIdは追跡されるべきではないので、.env.localに書きます。クライアント側で使うのでNEXT_PUBLICとしています

.env.local
NEXT_PUBLIC_ADOBE_FONTS_KIT_ID=xxxxxx

CSSの設定

読み込んだCSSをTailwindで使用できるようにします。
AdobeFontsでスクリプトを作成した時に表示されるCSS用の名称を渡せばTailwindで有効になります。

tailwind.config.ts
import type { Config } from 'tailwindcss'

const config: Config = {
  content: [
    './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
    './src/components/**/*.{js,ts,jsx,tsx,mdx}',
    './src/app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
+    fontFamily: {
+     tsukuaoldmin: ['fot-tsukuaoldmin-pr6n', 'sans-serif'],
+   },
  },
  plugins: [],
}
export default config

スクリプトのロードが終わっていないとデフォルトのフォントがロードされて画面が崩れる可能性があるので、globals.csswf-activeクラスを持たない場合は表示しないようひと手間加えました。

globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;

html {
  visibility: hidden;
}
html.wf-active {
  visibility: visible;
}

layout.tsxから呼び出す

最上位のlayout.tsxから先ほど作成したコンポーネントを実行します。
これで{children}の要素では全てfont-ont-tsukuaoldminを指定できるようになるはずです。

layout.tsx
import '@/styles/globals.css'
import TypekitLoader from '@/app/TypekitLoader'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="ja">
      <TypekitLoader />
      <body>{children}</body>
    </html>
  )
}

使ってみた

<p className=" text-white font-tsukuaoldmin">FOT-筑紫Aオールド明朝 Pr6N</p>

こんな感じです。本当に綺麗なフォントですよね、筑紫オールド

Discussion