開発記録
作品カテゴリを hash で表す:選択状態を URL とともに残す
作品ページのネイティブなアンカーリンクを例に、カテゴリ選択、CSS によるフォールバック、ブラウザ側のスクリプトの役割を分けます。共有、再読み込み、戻る操作、言語切り替えで同じ状態を保てるかを確認するノートです。
- URL
- HTML
- 状態設計
- プログレッシブエンハンスメント
作品ページをデザインカテゴリに切り替えて、その URL を誰かに送ったとします。相手が開いたときに初期カテゴリへ戻ってしまうなら、その選択は元の画面の中にしか残っていなかったことになります。
このサイトでは、作品カテゴリを URL の hash に入れています。「どのカテゴリを見たいか」を、共有、再読み込み、履歴からの復帰に使える形で表せます。ただし、周囲のリンクまですべて自動的に同期するわけではありません。2026 年 10 月 26 日に、カテゴリへの入口、スクリプトなしのフォールバック、言語リンクを併せて確認し、この状態が実際にどこまで引き継がれるかを調べました。
URL に残す価値のある状態を選びます
作品ページのカテゴリ選択は、読者がどのコンテンツを見るかを左右するため、URL に残すのに適しています。一方、その瞬間のカードの移動量、画像グリッドの高さ、表示アニメーションの進み具合は見せ方の詳細です。公開 URL に含める必要はありません。
三つのカテゴリは同じ作品ページのドキュメントに含まれ、それぞれ次の繁体字中国語版の URL に対応します。
/about/work#development
/about/work#design
/about/work#aigc
fragment は URI のうち # で始まる部分です。リソースを要求するときにはサーバーへ送られず、リソースを取得したあとにクライアント側で処理します。[3] そのため、この構成は三つのカテゴリに静的ページを一つずつ作るものでも、hash に応じてサーバーから別々のデータを取得するものでもありません。
この選択は、現在のコンテンツ規模に合っています。各カテゴリの本文はすでに HTML にあり、URL は読者が見たいセクションを表せば足ります。大量のデータへの問い合わせ、検索、複数条件の組み合わせへと要件が変わるなら、パス、query、データ取得の役割分担を検討し直します。この小さな事例を、あらゆる絞り込み UI の答えにはできません。
カテゴリへの入口は最初からリンクです
開発、デザイン、AIGC の入口は fragment を持つ anchor であり、click handler だけで動く見せかけのリンクではありません。各リンク先には、対応するセクション ID もあります。
スクリプトが有効な場合、通常のクリックでは location.hash を更新し、表示するセクション、カテゴリの見た目、aria-current を同期します。修飾キーを使ったクリックでは、この処理に介入せず、ブラウザ本来の動作を保ちます。
この一組のリンクは ARIA tabs として宣言しておらず、方向キーでタブを切り替えるルールも実装していません。現在読むセクションを切り替えるネイティブなリンクです。画面上で横一列に並んでいるという理由で、実装していないキーボード操作まで備えていることにはできません。
また、カテゴリの切り替えと画像グリッドの配置は別の処理です。hash が決めるのは表示するセクションです。カードをどの列に置くか、画像の読み込み後にどう配置し直すかは、引き続き作品ページ内のスケジューリングとオブザーバーが担当します。URL が各カードの座標を知る必要はありません。
CSS のフォールバックは、スクリプトありの表示と同一でなくても構いません
ページのスクリプトを無効にしても、関連するセレクターをサポートするブラウザなら、CSS が :target と :has() で現在の対象セクションを判断し、ほかのカテゴリを非表示にできます。
ローカルの Edge でページのスクリプト実行を禁止し、デザインカテゴリを直接開いてから、ネイティブのリンクで AIGC に切り替えました。どちらも対応する内容を表示できました。このモードでは、スクリプトによる画像グリッドの配置は実行されず、現在のカテゴリを示すアクセシビリティ上の状態もスクリプトでは補いません。
ここでは、一つの違いをそのまま捉える必要があります。hash がないとき、スクリプトなしの版では全カテゴリの内容が残ります。スクリプトありの版は、開発カテゴリを初期表示します。認識できない hash の場合も、スクリプトは開発カテゴリに戻します。
| 状況 | 表示の方針 |
|---|---|
| スクリプトあり、hash がデザインに対応 | デザインのセクションを表示する |
| スクリプトあり、カテゴリの hash がない、または認識できない | 開発のセクションを初期表示する |
| スクリプトなし、セレクターをサポートし、対応する target がある | CSS で対象カテゴリを表示する |
| スクリプトなし、target がない | 各カテゴリの内容を引き続き読める |
このフォールバックは、まずコンテンツを読める状態に保つことを優先します。どの環境でも追加機能が同じように働くとは見なしません。関連する CSS をサポートしない環境まで、この Edge での観察によって動作を保証することもできません。
戻る操作と再読み込みでも、同じ選択を読み戻します
click handler の中だけで画面を変えても十分ではありません。読者は hash 付きの URL を直接開くかもしれませんし、カテゴリのリンクをもう一度押すのではなく、ブラウザの「戻る」を使うかもしれません。
スクリプトは初期化時に hash を読み取り、hashchange と popstate を監視して、表示状態を URL に合わせ直します。ローカルでデザインから AIGC に切り替え、ブラウザの履歴で戻ると、表示はデザインに戻りました。再読み込み後もデザインカテゴリのままでした。
この観察で確認できるのは、カテゴリと URL の同期です。履歴から戻る際のあらゆるキャッシュの状況を検証したわけではありません。URL に状態が残っていても、その状態を画面に復元するには、正しい初期化とイベント処理が必要です。
別の入口が古い状態のスナップショットを持つことがあります
言語切り替えを確認したところ、単に「hash をコピーしているか」よりも具体的な制約が見つかりました。共通スクリプトが言語リンクを更新するのは、初期化時とデスクトップ用の言語メニューを開閉したときです。作品カテゴリが変わるたびに、すべての入口を同期しているわけではありません。
実際に、最初にデザインカテゴリを開いてから AIGC に切り替えると、モバイルナビゲーション内の英語リンクには、初期状態のデザインの hash が残っていました。デスクトップ用の言語メニューから更新を起こすと、その時点の AIGC の hash が付くようになりました。
したがって、「初期化時に URL をコピーした」を「言語を切り替えても、常に現在のカテゴリを保つ」と書き換えることはできません。これは実装の制約です。読者に毎回デスクトップ用のメニューを先に開くよう勧めているわけでも、このノートでその動作を修正したわけでもありません。
こうした状態の引き継ぎを整えるなら、まずどの入口がその状態に依存しているかを列挙します。そのうえで、状態が変わるたびに更新するのか、入口を使う直前に更新するのかを決めます。各リンクのそばにカテゴリを一つずつ別保存するより、どこに古い値が残っているかを確認しやすくなります。
静的な Astro ページに Canvas、GSAP、局所的なインタラクションを残すでは、インタラクションの責任範囲を説明しています。ここで扱ったのは、状態がどこへ引き継がれるかです。hash によってカテゴリを URL に残せても、実際の共有、戻る操作、異なる入口間の同期は、それぞれ確認する必要があります。
参考資料
[3] https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Fragment — MDN: URI fragment