Site search / サイト内検索

必要な情報を探す

ページ内の言葉を探す検索と、意味が近い内容を探す検索を使い分けられます。

「検索する」を実行すると、入力した検索語をOpenAI APIへ送り数値表現に変換し、その数値表現をCloudflare Vectorizeで当サイトの公開情報と照合します。あわせて、Acecore共通検索API(acecore.net)へ送信し、 Acecore関連サイトの公開情報を下部に表示することがあります。個人情報や機密情報は入力しないでください。個人情報の取り扱い

Insights / 技術解説

Astro View Transitionsの落とし穴と解決策 ― UX・コード品質改善ガイド

AstroのView Transitionsでスクリプトが動かなくなる問題の解決策、Pagefind全文検索の導入、TypeScript型安全性の向上、定数の一元管理など、UXとコード品質を改善した実践ガイドです。

  • 技術
  • Astro
  • Webサイト
Astro View Transitionsの落とし穴と解決策 ― UX・コード品質改善ガイド
この記事の目次
  1. はじめに
  2. View Transitionsのスクリプト問題
  3. なぜスクリプトが動かなくなるのか
  4. 解決パターン
  5. astro:after-swap と astro:page-load の使い分け
  6. Pagefind 全文検索の導入
  7. 基本構成
  8. ファセット検索
  9. 検索モーダル
  10. SearchAction連携
  11. キャッシュ設定
  12. インラインonclickの排除
  13. 改善パターン
  14. コンポーネントライブラリの整備
  15. TypeScript 型安全性の向上
  16. any型の排除
  17. コンテンツスキーマのリテラル型
  18. as const アサーション
  19. 非推奨インポートの移行
  20. 定数の一元管理
  21. その他のUX改善
  22. 目次のスクロール追従
  23. スクロールスパイ
  24. ページネーション
  25. スティッキーヘッダーのアンカーリンク
  26. まとめ
  27. この記事が含まれるシリーズ

はじめに

AstroのView Transitions(ClientRouter)は、ページ遷移をSPAのようにスムーズにする強力な機能です。しかし導入した途端、ハンバーガーメニューが開かない、検索ボタンが反応しない、スライダーが止まる……という問題に直面します。

この記事では、View Transitionsの落とし穴とその解決策、そしてUXとコード品質を改善するための実践的な手法を紹介します。


View Transitionsのスクリプト問題

なぜスクリプトが動かなくなるのか

通常のページ遷移では、ブラウザがHTMLを再パースしてすべてのスクリプトを実行します。しかしView TransitionsはページをDOM差分で更新するため、インラインスクリプトが再実行されません

影響を受けるのは以下のような処理です。

  • ハンバーガーメニューの開閉
  • 検索ボタンのクリックハンドラ
  • ヒーロー画像のスライダー
  • 目次のスクロール追従
  • YouTube埋め込みのファサードパターン

解決パターン

すべてのスクリプトを 名前付き関数に包み、astro:after-swap で再登録 するパターンに統一します。

<script>
  function initHeader() {
    const menuBtn = document.querySelector("[data-menu-toggle]");
    menuBtn?.addEventListener("click", () => {
      /* ... */
    });
  }

  // 初回実行
  initHeader();

  // View Transitions後に再実行
  document.addEventListener("astro:after-swap", initHeader);
</script>

astro:after-swap と astro:page-load の使い分け

  • astro:after-swap:DOMが差し替えられた直後に発火。初回読み込みでは発火しないため、関数を直接呼ぶ必要がある
  • astro:page-load:初回読み込み View Transitions後の両方で発火する。初回呼び出しの記述を省略できる

YouTube埋め込みのように初回読み込みでも確実に動作させたい場合は astro:page-load が便利です。


Pagefind 全文検索の導入

静的サイトに全文検索を実装するなら Pagefind がおすすめです。ビルド時にインデックスを生成し、ブラウザ側で検索を実行するため、サーバー不要で高速です。

基本構成

{
  "scripts": {
    "build": "astro build && pagefind --site dist"
  }
}

Astroのビルド後に Pagefind を実行し、dist/pagefind/ にインデックスを出力します。

ファセット検索

data-pagefind-filter 属性を使えば、著者・年・タグの3軸でフィルタリングできます。

<span data-pagefind-filter="author">gui</span>
<span data-pagefind-filter="year">2026</span>
<span data-pagefind-filter="tag">Astro</span>

検索モーダル

Ctrl+K ショートカットで開く検索モーダルを実装します。ゼロ結果時には記事一覧・サービスページ・お問い合わせへのリンクを表示し、ユーザーの離脱を防ぎます。

SearchAction連携

Google の構造化データ SearchAction?q= パラメータを定義しておけば、検索結果からサイト内検索に直接遷移できます。URLパラメータを検知して検索モーダルを自動起動する処理を追加しましょう。

キャッシュ設定

Pagefindのインデックスファイルは変更頻度が低いため、Cloudflare Pages のヘッダー設定でキャッシュを有効にします。

/pagefind/*
  Cache-Control: public, max-age=604800, stale-while-revalidate=86400

インラインonclickの排除

HTMLに直接書く onclick="..." は手軽ですが、CSP(Content Security Policy)で unsafe-inline を要求する原因になります。

改善パターン

onclickdata-* 属性 + addEventListener に置き換えます。

<!-- Before -->
<button onclick="window.openSearch?.()">検索</button>

<!-- After -->
<button data-search-trigger>検索</button>
document.querySelectorAll("[data-search-trigger]").forEach((btn) => {
  btn.addEventListener("click", () => window.openSearch?.());
});

コンポーネントライブラリの整備

ブログ記事を書くときに使えるコンポーネントを揃えておくと、記事の表現力が上がります。

コンポーネント 用途
Callout info / warning / tip / note の4種類の注釈
Timeline イベントの時系列表示
FAQ 構造化データ対応の質問回答
Gallery Lightbox付き画像ギャラリー
CompareTable ビフォーアフターの比較表
ProcessFigure ステップバイステップのプロセス図
LinkCard OGP風の外部リンクカード
YouTubeEmbed ファサードパターンで遅延読み込み

これらはすべてMarkdownのフロントマターから呼び出せるように設計しています。記事のテンプレートで data.callout があれば <Callout> を描画する、という仕組みです。


TypeScript 型安全性の向上

any型の排除

any[]CollectionEntry<'blog'>[] のように具体的な型を指定します。IDEの補完とコンパイル時のエラー検出が効くようになり、テンプレート内のプロパティアクセスが安全になります。

コンテンツスキーマのリテラル型

type: z.enum(["info", "warning", "tip", "note"]).default("info");

フロントマターの値をリテラル型ユニオンで定義すると、テンプレート側で if (callout.type === 'info') のような分岐が型安全になります。

as const アサーション

定数オブジェクトに as const を付けると、プロパティが readonly になり、型推論がリテラル型になります。SITE 定数には必ず付けましょう。

非推奨インポートの移行

Astro 7 で削除予定の import { z } from 'astro:content'import { z } from 'astro/zod' に変更しておきます。


定数の一元管理

ハードコードされた値は変更時に見落としの原因になります。以下の値を src/data/site.ts に集約しました。

定数 集約前の箇所数
AdSense Client ID 4ファイル
GA4 Measurement ID 2箇所
広告スロットID 4ファイル
ソーシャルURL(X・GitHub・Discord・Aceserver) 17箇所
電話番号・メール・LINE 3ファイル
export const SITE = {
  name: "Acecore",
  url: "https://acecore.net",
  ga4Id: "G-XXXXXXXXXX",
  adsenseClientId: "ca-pub-XXXXXXXXXXXXXXXX",
  social: {
    x: "https://x.com/acecore",
    github: "https://github.com/acecore-systems",
    discord: "https://discord.gg/...",
  },
} as const;

その他のUX改善

目次のスクロール追従

IntersectionObserver でコンテンツの見出しを監視し、アクティブな見出しをサイドバーの目次にハイライトします。scrollIntoView({ block: 'nearest', behavior: 'smooth' }) で目次自体もスクロールさせるのがポイントです。

スクロールスパイ

サービスページのようなシングルページ構成では、IntersectionObserver でナビゲーションのアクティブ項目を自動追従させます。

ページネーション

6記事ごとの自動ページ分割、省略記号付きナビゲーション(1 2 ... 9 10)、「← 前へ」「次へ →」テキストリンクを実装します。ページネーションのロジックは src/utils/pagination.ts に共通化しましょう。

スティッキーヘッダーのアンカーリンク

スティッキーヘッダーがあると、アンカーリンクで飛んだ先がヘッダーに隠れてしまいます。UnoCSS の preflight で以下を設定して解消します。

[id] {
  scroll-margin-top: 5rem;
}
html {
  scroll-behavior: smooth;
}

まとめ

View Transitionsを使うなら、スクリプトの初期化パターンを統一する ことが最重要です。astro:after-swap / astro:page-load の使い分けを理解し、すべてのインタラクションをテストしましょう。

コード品質の面では、TypeScriptの型安全性と定数の一元管理が長期的な保守性に大きく貢献します。最初は面倒に感じますが、IDEの補完が効くようになる恩恵は日々の開発で実感できます。


この記事が含まれるシリーズ

この記事は「Astroサイトの品質改善ガイド」シリーズの一部です。パフォーマンス・SEO・アクセシビリティの改善についても個別の記事で紹介しています。

UX改善の進め方

  1. 問題の発見

    View Transitions導入後の動作不良をリストアップ。

  2. パターンの統一

    すべてのスクリプトを統一された初期化パターンに変換。

  3. 検索の実装

    Pagefindで全文検索を導入し、導線を整備。

  4. 型安全の確保

    any型の排除・定数一元管理で保守性を向上。

改善前後の比較

改善前

  • ページ遷移後にハンバーガーメニューが動かない
  • サイト内検索なし
  • any型やハードコード定数が散在
  • インラインonclickでCSP違反リスク

改善後

  • astro:after-swapで全スクリプトが正常動作
  • Pagefindで3軸フィルタ付き全文検索
  • TypeScript型安全・定数一元管理
  • addEventListener + data属性でCSP準拠

よくある質問

View Transitionsなしでもこの改善は有効ですか?

スクリプトの初期化パターン以外の改善(Pagefind、TypeScript、定数管理)はView Transitionsの有無に関係なく有効です。

Pagefindはどのくらいのサイト規模まで対応できますか?

Pagefindは静的サイト向けに設計されており、数千ページ規模でも高速に動作します。検索インデックスはビルド時に生成され、ブラウザ側で実行されるためサーバー負荷もありません。

TypeScriptの型エラーは無視しても動きますか?

動作はしますが、型エラーはバグの予兆です。特にAstroのコンテンツスキーマは型安全にすることで、テンプレート内のプロパティアクセスでIDEの補完が効くようになり、開発効率が大きく向上します。