This article was translated from Japanese by AI and may contain inaccuracies. For the most accurate content, please refer to the original Japanese version.
ワインソムリエのイラスト

静的サイト向けの全文検索エンジンと UI ライブラリの Pagefind

Pagefind は、静的サイト向けの全文検索エンジンと UI ライブラリです。特定のフレームワークに依存しない JavaScript ライブラリとして実装されており、静的サイトジェネレーターで生成された HTML ファイルに対して検索機能を追加できます。

Pagefind は、静的サイト向けの全文検索エンジンと UI ライブラリです。Cloud CMS を提供している CloudCannon 社により開発されています。

特定のフレームワークに依存しない JavaScript ライブラリとして実装されており、静的サイトジェネレーターで生成された HTML ファイルに対して検索機能を追加できます。追加するコードの量も少なく、簡単に検索機能を実装できます。

デモとして、このブログに Pagefind を導入してみました。ヘッダーの検索アイコンをクリックすると検索フォームが表示されるので、キーワードを入力して検索してみてください。

使い方

Pagefind は構築済みの UI ライブラリと、CLI コマンドとしてインデックスを作成するためのツールから構成されています。まずは UI ライブラリの部分から見てみましょう。

UI ライブラリ

Pagefind の UI ライブラリは、検索フォームと検索結果を表示するためのコンポーネントから構成されています。この UI は以下のコードを追加するだけで簡単に利用できます。

<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
<script src="/pagefind/pagefind-ui.js"></script>
<div id="search"></div>
<script>
  window.addEventListener("DOMContentLoaded", (event) => {
    new PagefindUI({ element: "#search" });
  });
</script>

/pagefind/pagefind-ui.css/pagefind/pagefind-ui.js は、後述する Pagefind の CLI コマンドでインデックスを生成する際に生成されるファイルです。js ファイルを読み込むことで PagefindUI クラスがグローバルに定義され、PagefindUI クラスのコンストラクタにオプションを渡すことで UI を初期化できます。

SvelteKit で js, css ファイルを読み込む

SvelteKit ではスタティックサイトジェネレーターとしてビルドする際に、すべてのリンクをクローリングして HTML ファイルを生成します。このとき、リンクに存在しないパスが含まれているとビルドに失敗します。

/pagefind/pagefind-ui.css/pagefind/pagefind-ui.js は SvelteKit によるビルド後にのみ存在するファイルですので、このファイルを読み込むように記述しているとビルドに失敗してしまいます。

特定のパスの読み込みに失敗してもビルドを継続するには、svelte.config.jskit.prerender.handleHttpError オプションにコールバック関数を指定します。

svelte.config.js
/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    prerender: {
      handleHttpError: ({ path, referrer, message }) => {
        // ignore deliberate link to shiny 404 page
        if (
          path === "/pagefind/pagefind-ui.css" ||
          path === "/pagefind/pagefind-ui.js"
        ) {
          return;
        }
 
        // otherwise fail the build
        throw new Error(message);
      },
    },
  },
};
 
export default config;

コンスタントに渡すオプションは element のみが必須です。element には検索フォームを表示するための要素をセレクタで指定します。

その他のオプションは以下のリンクを参照してください。

インデックスの作成

インデックスの作成は Next.js, Gatsby, Astro, SvelteKit などの静的サイトジェネレーターのビルドの後に実行されます。Pagefind は生成された HTML ファイルを読み込み、その中から検索対象として指定された要素を抽出しインデックスを作成します。

サイトのインデックスを作成するには npx pagefind コマンドを実行します。--site オプションには静的サイトジェネレーターが出力したディレクトリを指定します。例として SvelteKit + Vercel Adapter を使ってビルドした場合には .vercel/output/static となります。

npx pagefind --site <output directory>

コマンドに成功すると --site で指定したディレクトリ配下に pagefind ディレクトリが作成されます。

インデックス作成の対象の制御

Pagefind は HTML 属性を用いてインデックスの作成の対象を制御するのが特徴です。デフォルトでは <body> 要素の中にあるすべての要素がインデックスの対象となります。

インデックス対象の指定

data-pagefind-body 属性を指定することで、その要素の中にある要素のみがインデックスの対象となります。

<body>
  <main data-pagefind-body>
    <h1>Pagefind</h1>
    <p>Pagefind is a static site search engine and UI library.</p>
  </main>
 
  <aside>
    <!-- ここはインデックスの対象とならない -->
    <p>Author: Azusa Azuki</p>
  </aside>
</body>

data-pagefind-body 属性が指定された場合には、そのページに限らずその他のすべてのページで data-pagefind-body が指定されていない箇所がインデックスの対象から除外されることに注意してください。インデックスの対象としたいページ全てに対して data-pagefind-body 属性を指定する必要があります。

インデックス対象から除外する要素の指定

data-pagefind-body 要素の中にある要素のうち、インデックスの対象から除外したい要素がある場合には、data-pagefind-ignore 属性を指定します。

<main data-pagefind-body>
  <h1>Pagefind</h1>
  <p>Pagefind is a static site search engine and UI library.</p>
  <p data-pagefind-ignore>
    Pagefind is a static site search engine and UI library.
  </p>
</main>

HTML の特定の属性をインデックスの対象としたい場合には、data-pagefind-index-attrs 属性を指定して、値にインデックスの対象としたい属性をカンマ区切りで指定します。例えば、下記の例では画像の alt 属性の内容をインデックスの対象としています。

<main data-pagefind-body>
  <h1>
    <img src="/pagefind.png" alt="Pagefind" data-pagefind-index-attrs="alt" />
  </h1>
</main>

CLI コマンドでのインデックスの対象の制御

HTML 属性を指定する方法以外にも、CLI コマンドでインデックスを生成する際に --glob オプションを指定することで、インデックスの対象とするファイルを正規表現で指定することもできます。

npx pagefind --site <output directory> --glob "**/blog/*.html"

メタデータの指定

data-pagefind-meta HTML 属性を指定することで、検索結果とともに表示されるメタデータを指定できます。デフォルトでは、以下のメタデータを各ページから自動で抽出します。

  • title:ページに最初にある <h1> 要素の内容
  • image<h1> 要素の直後にある最初の <img> 要素の src 属性の値
  • image_alt<h1> 要素の直後にある最初の <img> 要素の alt 属性の値

例えば、特定の画像要素を常に検索結果として表示したい場合には、data-pagefind-meta="image[src], image_alt[alt]" 属性を指定します。

<img
  src="/hero.png"
  alt="Pagefind"
  data-pagefind-meta="image[src], image_alt[alt]"
/>

なお、data-pagefind-meta 属性は data-pagefind-body 属性が指定された要素外の要素に対しても指定できます。

カスタムメタデータ

data-pagefind-meta 属性の値に key:value の形式で指定することで、カスタムメタデータを指定できます。例えば、以下のように date:2021-02-13T00:00:00.000+09:00 という値を指定することで、date というキーのメタデータを作成できます。

<main data-pagefind-body>
  <h1>Pagefind</h1>
  <p>Pagefind is a static site search engine and UI library.</p>
  <time data-pagefind-meta="date:2021-02-13T00:00:00.000+09:00"
    >2021/02/13</time
  >
</main>

デフォルトで生成される title, image, image_alt 以外のメタデータのペアは、UI 上では検索結果の下に表示されます。

UI のカスタマイズ

new PagefindUI() で初期化される UI はデフォルトでは /pagefind/pagefind-ui.css のスタイルが適用されます。この CSS を読み込まずに、クラス名を指定して独自のスタイルを適用できます。しかし、このクラス名は将来変更される可能性があり安定しないため推奨されていません。

Pagefind のデフォルトの UI をカスタマイズするには、CSS 変数を上書きします。デフォルトでは以下の CSS 変数が定義されています。

:root {
  --pagefind-ui-scale: 0.8;
  --pagefind-ui-primary: #393939;
  --pagefind-ui-text: #393939;
  --pagefind-ui-background: #ffffff;
  --pagefind-ui-border: #eeeeee;
  --pagefind-ui-tag: #eeeeee;
  --pagefind-ui-border-width: 2px;
  --pagefind-ui-border-radius: 8px;
  --pagefind-ui-image-border-radius: 8px;
  --pagefind-ui-image-box-ratio: 3 / 2;
  --pagefind-ui-font: system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular",
    "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu",
    "arial", sans-serif;
}

例えば、--pagefind-ui-primary#e60000 に変更すると、検索フォームのアイコンの色が赤色に変更されます。

:root {
  --pagefind-ui-primary: #e60000;
}

ラベルテキストの変更

Pagefind の UI のラベルに表示されるラベルテキストの言語は、html の lang 属性の値によって自動で切り替わります。例えば、lang="ja" が指定されている場合には、ラベルテキストは日本語になります。

日本語のデフォルトのラベルテキストは、以下のように定義されています。

{
  "thanks_to": "Tate",
  "comments": "",
  "direction": "ltr",
  "strings": {
    "placeholder": "検索",
    "clear_search": "消す",
    "load_more": "もっと読み込む",
    "search_label": "このサイトを検索",
    "filters_label": "フィルタ",
    "zero_results": "[SEARCH_TERM]の検索に一致する件はありませんでした",
    "many_results": "[SEARCH_TERM]の[COUNT]件の検索結果",
    "one_result": "[SEARCH_TERM]の[COUNT]件の検索結果",
    "alt_search": "[SEARCH_TERM]の検索に一致する件はありませんでした。[DIFFERENT_TERM]の検索結果を表示しています",
    "search_suggestion": "[SEARCH_TERM]の検索に一致する件はありませんでした。次のいずれかの検索を試してください",
    "searching": "[SEARCH_TERM]を検索しています"
  }
}

このテキストの内容を変更したい場合には、new PagefindUI() で初期化される UI のオプションに translations を指定します。translations には上記の JSON の strings プロパティをオブジェクトとして指定します。例として placeholder のテキストを変更する場合には以下のように指定します。

new PagefindUI({
  element: "#search",
  translations: {
    placeholder: "サイト内を検索",
  },
});

まとめ

  • Pagefind は静的サイト向けの全文検索エンジンと UI ライブラリ
  • 静的サイトジェネレーターのビルド後に CLI コマンドでインデックスを作成する
  • Pagefind のデフォルトの UI が提供されていて、簡単に検索機能を実装できる
  • HTML の属性を使ってインデックスの作成の対象を制御したり、メタデータを指定できる
  • Pagefind の UI は CSS 変数を上書きすることでカスタマイズできる

参考