はじめに

PageSpeed Insightsでトップページだけ100点を取るのは、それほど難しくありません。インラインCSS、最小限のDOM、画像なし。条件を揃えれば達成できます。

本当に難しいのは、サイト内の全ページタイプで100点を維持することです。記事詳細ページには動的コンテンツがあり、チームページにはスタッフカードのグリッドがあり、ツールページにはインタラクティブなJavaScriptがあります。ページタイプごとに異なる課題が潜んでいるのです。

この記事では、京谷商会の18ポータルサイト基盤(Cloudflare Workers上のSSRアプリケーション)において、全7種のページタイプでPerformance・Accessibility・Best Practices・SEOの4項目すべて100点を達成するまでの過程を、実際のLighthouse監査結果と修正コードとともに記録します。

対象サイトの構成

今回の対象は、1つのCloudflare Workerが18のサブドメインを動的にルーティングするポータルシステムです。テンプレートエンジンを使わず、TypeScriptの文字列テンプレートリテラルでHTMLを直接生成するSSR構成を採用しています。

ページタイプは以下の7種類です。

ページタイプ URL例 特徴
トップページ / 記事カードグリッド、サムネイル画像
記事一覧 /articles/ ページネーション、カードリスト
記事詳細 /articles/{slug}/ Markdownレンダリング、目次、著者情報
チーム /team/ スタッフカードグリッド、アバター
スタッフ詳細 /team/{code}/ プロフィール、担当記事一覧
用語集 /glossary/ 五十音グループ、カードリスト
ツール /tools/ PageSpeed診断(SEOポータルのみ)

トップページは既に全項目100点を達成済みでした。問題は残りの6タイプです。

発見された問題と修正

問題1:mainランドマークの欠如(Accessibility −9点)

Lighthouseの指摘landmark-one-main — 「ドキュメントにメインのランドマークが設定されていません」でした。

共通レイアウト関数 layout() では、ヘッダーとフッターの間にコンテンツを直接挿入していました。スクリーンリーダーは <main> 要素を使ってページの主要コンテンツ領域を特定するため、この要素がないとナビゲーション効率が大幅に低下します。

修正内容として、layout() 関数内でコンテンツを <main> タグで囲みました。この1行の修正が全ページタイプに自動的に適用されます。共通レイアウト関数を持つアーキテクチャの強みです。

問題2:canonical URLのパス未設定(SEO −8点)

Lighthouseの指摘canonical — 「ドキュメントに有効な rel=canonical が指定されていません」でした。

<link rel="canonical"> タグ自体は存在していましたが、パス部分が常に / にフォールバックしていました。チームページ(/team/)のcanonicalがトップページ(/)を指している状態は、Lighthouseが「無効」と判定します。

原因は、パスを渡すためのグローバル変数がどこからも設定されていなかったことです。

修正内容として、Honoミドルウェアを追加し、全リクエストで現在のURLパスをグローバルに設定しました。Cloudflare Workersはリクエストごとに独立したコンテキストで実行されるため、グローバル変数の競合リスクは事実上ありません。12個のレンダリング関数すべてにパス引数を追加するよりも、変更量が圧倒的に少なく済むアプローチです。

問題3:テキスト内リンクの識別不能(Accessibility −7点)

Lighthouseの指摘link-in-text-block — 「リンクは色に依存して識別可能です」でした。

グローバルCSSで a { text-decoration: none; } を設定していたため、リンクと通常テキストの区別が色のみに依存していました。WCAG 2.1の達成基準1.4.1は、色だけに依存せずにリンクを識別できることを要求しています。

具体的には、パンくずリストのリンク色(#64748b)と周囲テキスト色(#4b5563)のコントラスト比が1.58:1しかなく、基準の3:1を大幅に下回っていました。

修正内容として、デフォルトのリンクスタイルをアンダーライン付きに変更し、ナビゲーション要素やカードリンクのみアンダーラインを除外する方針に転換しました。text-decoration-skip-ink: auto の指定により、アンダーラインが文字のディセンダー(g、y、pなどの下に伸びる部分)と重なる箇所で自動的に途切れ、視認性を維持しつつデザインの美しさを損ないません。

問題4:Coming Soonカードのコントラスト不足(Accessibility −7点)

Lighthouseの指摘color-contrast — 「背景色と前景色には十分なコントラスト比がありません」でした。

SEOポータルのツール一覧ページには、開発中のツール2つが opacity: 0.5 のカードとして表示されていました。親要素の opacity は子要素すべてに継承されるため、テキストの実効コントラスト比が基準を下回ります。

修正内容として、opacity による「無効化」表現を廃止し、border-style: dashedcolor: var(--text-muted) の組み合わせに変更しました。破線ボーダーは「未完成」「準備中」を視覚的に伝える表現として直感的であり、コントラスト比の問題も発生しません。

問題5:ポータルごとのアクセントカラーとボタンコントラスト

18ポータルはそれぞれ異なるテーマカラー(--accent)を持っています。一部のポータルでは、background: var(--accent); color: #fff のボタンがコントラスト基準4.5:1を満たしていませんでした。

修正内容として、ボタンの背景色を var(--accent) から var(--primary-header) に変更しました。--primary-header はヘッダー背景にも使われる暗い色で、白文字との組み合わせで全ポータルにおいて十分なコントラスト比を確保できます。

修正の検証結果

PageSpeed Insights 全4項目100点のスコア表示

全修正をデプロイした後、Google PageSpeed Insights APInocache=1 オプション付きで呼び出し、キャッシュを無視した新鮮なスコアを取得しました。

ページタイプ ポータル Performance Accessibility Best Practices SEO
チーム SEO 100 100 100 100
用語集 SEO 100 100 100 100
ツール(SEO) SEO 100 100 100 100
記事一覧 SEO 100 100 100 100
記事詳細 SEO 100 100 100 100
ツール(NoTools) ADS 100 100 100 100
チーム CLD 100 100 100 100

全ページタイプ、複数ポータルにまたがって全項目100点を確認しました。

アーキテクチャが支えた効率的な改善

今回の改善で修正したファイルは実質2つだけです。全ページのHTMLテンプレートを含む render.ts と、ルートハンドラおよびミドルウェアの index.ts です。

1つの layout() 関数が全ページタイプの共通構造を生成しているため、<main> タグの追加やリンクスタイルの変更が自動的に全ページに反映されました。テンプレートの共通化は、保守性だけでなく品質の均一化にも直結します。

また、Cloudflare WorkersのSSR構成はPageSpeed Performanceの面でも有利です。エッジサーバーで即座にHTMLを生成して返すため、TTFB(Time to First Byte)が極めて短く、外部リソースへの依存もありません。JavaScriptフレームワークのハイドレーションコストもゼロです。

100点を維持するために

スコアを一度達成することと、維持し続けることは別の課題です。

定期的な自動計測として、PageSpeed Insights APIをCI/CDパイプラインやcronジョブに組み込み、スコアの低下を早期に検知する仕組みが有効です。当サイトのPageSpeed診断ツールでも手動での確認が可能です。

新規ページタイプ追加時のチェックとして、新しいテンプレートやコンポーネントを追加する際は、必ずキャッシュを無視したPageSpeedテストを実行してください。特に opacitycolor の使い方はAccessibilityスコアに直結します。

外部リソース追加の慎重な判断も重要です。Google Fontsやアナリティクススクリプトの追加は、Performance 100点を崩す最大の要因になります。本当に必要か、代替手段はないかを検討してください。

まとめ

今回の改善で対処した問題は、いずれも「動くし見える、でもLighthouseが減点する」類のものでした。<main> タグがなくてもページは表示されますし、アンダーラインがなくてもリンクは機能します。しかし、これらの基準を満たすことは、スクリーンリーダーのユーザーや色覚特性を持つユーザーにとって、サイトが使えるかどうかの分かれ目です。

PageSpeed Insightsの100点は目標ではなく基準線です。全項目100点を全ページタイプで達成した今、次に取り組むべきは実ユーザーデータ(CrUX)での検証と、コンテンツ追加に伴うスコア維持の仕組みづくりです。

参考リンク

Lighthouseのアクセシビリティ監査の詳細はLighthouse accessibility scoringで確認できます。今回問題となったリンク識別の基準はWCAG 2.1 達成基準1.4.1 色の使用に定められています。canonical URLの正しい設定方法はGoogle検索セントラル canonical タグが公式ガイドです。Cloudflare WorkersでのSSR構成についてはCloudflare Workers documentationを参照してください。mainランドマークの仕様はWAI-ARIA main landmarkに解説されています。