Renovation History

この更新履歴の判定方法: 各版を単独で評価せず、次版以降の修正依頼・実装継続・撤去の有無と照合して、成果を「確定」「部分達成」「未達」「要実機確認」に再分類しています。
目的は、Codex/ChatGPTが次回の改修時に、成功した共通構造を再利用し、失敗した個別補正・重複CSS・概算座標・未検証の成功扱いを繰り返さないための設計資料にすることです。0.14.1以前の比較元ZIPがないため、0.14.1のみ完全diffではなく記録ベースです。

0.16.10

当時の改修目的

更新履歴を単なる作業日誌から、Codex/ChatGPTが次回の改修判断に使える設計資料へ再構築する。記録の最初から各版を前後関係で見直し、当時の自己申告ではなく後続版の修正結果から成否を判定する。

実装した変更

・全バージョンの見出しを「目的/実装/確定成果/後続版で判明した未達」へ統一

・各版へ後続バージョンの実要求を照合した判定を追加

・最新版は成功確定せず「要実機確認」と明記

・原因欄へ再利用可能な設計判断基準を追加

・Codex向けの更新履歴運用指針を文書化

後続版との照合で確定した成果

判定:暫定。現行最新版のため、次回改修で履歴の有用性と表示崩れの有無を確認して確定する。

履歴の評価単位を「その版で何をしたか」から「何が後続版でも有効だったか」へ変更した。成功・失敗・未検証を分離し、実装方式の再利用可否まで記録する構造にした。

後続版で未達・再修正と判明した項目

判定:要実機確認。履歴モジュールの表示量、可読性、スマートフォンでの空白、専用メニュー位置は実機確認が必要。

後続版がまだ存在しないため、0.16.10の成功は確定していない。次回の修正依頼が発生した場合は、その内容を本版へ遡及して反映する。

実際の変更箇所・実装内容

assets/history/history-content.html
全履歴を前後版照合方式へ再編集し、各版の確定成果・未達・次版要求・設計判断基準を追加した。

docs/RENOVATION-HISTORY-GUIDE.md
Codex/ChatGPTが更新履歴を読む際の優先順位、成功判定、禁止すべき実装パターン、更新時の記録手順を追加した。

inc/view-shell.php / functions.php / style.css / CHANGELOG.md
0.16.10を履歴専用メニューとテーマ情報へ追加した。

原因分析・次回改修への指針

従来は各版を作成した直後の説明をそのまま成功記録として残していたため、次版で修正された事実と矛盾した。今後は作成直後には暫定判定とし、実機確認または後続版で維持された時点で確定する。


再利用すべき判断基準

  • 成功とは「コードを書いたこと」ではなく、実機で要望を満たし、後続版でも撤去されなかったこと。
  • 次版で修正された箇所は、前版では未達または部分達成として遡及更新する。
  • 個別上書きより共通構造、固定値より共有基準、互換層より旧処理の撤去を優先する。
  • 最新版は常に暫定評価とし、未検証を成功欄へ入れない。

対象ZIP
hiroki-ohnuma-0.16.10-history-knowledge-base.zip

0.16.9

当時の改修目的

Linkと更新履歴のページ専用ナビゲーションの高さ・ボタン位置をPC/スマートフォンで統一し、0.16.2以降の失敗記録を後続の修正依頼に基づいて正確に書き直す。

実装した変更

・Link/Historyのページ専用ナビを共通の52px枠と44pxボタンで統一

・PC/スマートフォンとも上下paddingを4pxへ統一

・履歴の横スクロール領域とAbout/骸骨ボタンを同一垂直中心へ配置

・0.16.2〜0.16.8の失敗項目と失敗理由を後続会話から遡及反映

後続版との照合で確定した成果

判定:暫定。現行最新版のため、実機確認と次回改修での維持状況をもって確定する。

LinkとHistoryが同じページ専用ナビ寸法を共有し、ページ切替時のボタン位置の跳ねを抑える構造になった。過去版の記録も、実際に次版で修正依頼が出た内容と整合した。

後続版で未達・再修正と判明した項目

判定:要実機確認。最新版は後続版との比較がまだできないため、成功を確定扱いにしない。

実機上のブラウザUI・WordPress管理バーを含む最終的な見え方は、導入後のPC/スマートフォン確認が必要。

実際の変更箇所・実装内容

assets/css/theme.css
LinkとHistoryのページ専用ナビに共通の高さ、padding、44pxコントロール寸法を指定。履歴バージョン領域の高さをナビ内部の利用可能高へ合わせ、PC/モバイルで同じ垂直中心を使うよう整理した。過去の重複指定より後で一つの正規化ルールを適用した。

assets/history/history-content.html
0.16.2〜0.16.8の「失敗した項目」「失敗理由」を、その後ユーザーから実際に提示された修正点に基づいて書き換え、0.16.9を追加した。

inc/view-shell.php
履歴専用メニューへ0.16.9を追加した。

functions.php / style.css / CHANGELOG.md
テーマバージョンを0.16.9へ更新し、変更履歴を追記した。

原因分析・次回改修への指針

該当なし。今回はページ専用ナビの寸法基準の統一と記録の訂正に限定した。


対象ZIP
hiroki-ohnuma-0.16.9-context-history-audit.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.8

当時の改修目的

PC版TetrisのPlay/Pause表示と操作説明を省スペース化し、青骸骨モードにもSpaceの単押し/長押し操作を追加する。スマートフォン版にはStockボタンだけでなく、保持中ピースを確認できるStock画面も表示する。

実装した変更

・Play/Pauseの文字を廃止し、▶︎/JとⅡ/Kを2秒ごとに0.86秒でディゾルブ

・操作説明を指定の改行構成へ整理し、矢印、Space、Enterを太字化

・即落下と2倍速落下の説明を同一行内で2秒ごとに縦スクロール

・青モードのSpaceを単押し即落下、長押し2倍速落下へ変更

・スマートフォンのサイド欄へStockプレビューを表示

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.9以降でも撤去されず残った仕組みは継続採用とみなす。

PC操作欄の横幅を増やさず、キー表示と説明を時間差表示へ整理した。赤と青の長押し判定を同じ仕組みにまとめ、スマートフォンでもStock内容を確認できるようにした。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.9 の要望・修正内容と照合して確定。

スマホ版Linkと更新履歴のページ専用フッター、およびPC版ページ専用ヘッダーで高さ・内側余白が揃わず、ボタンの垂直位置がずれた。また0.16.2以降の失敗記録が実際の後続修正を反映していなかった。

次版で実際に求められたこと:Linkと更新履歴のページ専用ナビゲーションの高さ・ボタン位置をPC/スマートフォンで統一し、0.16.2以降の失敗記録を後続の修正依頼に基づいて正確に書き直す。

実際の変更箇所・実装内容

template-parts/view-tetris.php
Play/Pauseボタン内部を▶︎/J、Ⅱ/Kの二層表示へ変更。赤/青モードの説明を行単位へ分け、キー名をstrong要素、切替説明を専用の縦スクロール要素として記述した。

assets/js/tetris.js
赤の下キーと青のSpaceで共有できる長押し判定処理へ整理。青モードではSpaceを短く離すと即落下し、判定時間を超えて押し続けると自然落下間隔を半分にする。キーを離した時、Pause時、ウィンドウが非アクティブになった時に高速状態を解除する。

assets/css/tetris.css
▶︎/JおよびⅡ/Kの0.86秒クロスディゾルブ、説明文の0.86秒縦スクロール、太字キー表示を追加。スマートフォンで非表示だったStock欄を再表示し、狭い画面用の高さへ調整した。

inc/view-shell.php / functions.php / style.css / CHANGELOG.md
0.16.8を履歴専用メニューとテーマ情報へ追加した。

原因分析・次回改修への指針

LinkとHistoryに後続版ごとの個別CSSが積み重なり、44pxボタンに対してナビ本体の高さ・padding・内部スクロール領域が別々の値になっていた。履歴本文も各版作成時点の「既知の失敗なし」を保持し、後から判明した不具合を遡及更新していなかった。


対象ZIP
hiroki-ohnuma-0.16.8-tetris-input-refine.zip


再利用すべき判断基準

  • ページ別・版別の上書きを増やさず、共通クラス・共通変数・単一レンダラーへ統合する。
  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.7

当時の改修目的

Linkと更新履歴の骸骨ボタン寸法を揃え、PC版Tetrisの操作系、キーパターン、Stock機能、スマートフォン用Stockボタンを追加する。

実装した変更

・Link/更新履歴の骸骨ボタンを同寸法へ統一

・PC版Play/Pauseを横並び化

・赤/青骸骨のKey patternを追加

・赤モードの下キーを単押し即落下、長押し時は2倍速落下へ変更

・青モードを上下キー回転、Space即落下へ変更

・Next上部へStockを追加しEnterで入替

・スマートフォンのPause下へGoSkull画像のStockボタンを追加

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.8以降でも撤去されず残った仕組みは継続採用とみなす。

既存ゲーム盤と描画方式を維持しながら、操作系を二つのキーパターンへ分離し、StockをPCとスマートフォンの両方へ追加した。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.8 の要望・修正内容と照合して確定。

PC版TetrisのPlay/Pause表記、青モードのSpace長押し、操作説明の改行・強調・切替演出、スマホStock表示が要望どおりではなかった。

次版で実際に求められたこと:PC版TetrisのPlay/Pause表示と操作説明を省スペース化し、青骸骨モードにもSpaceの単押し/長押し操作を追加する。スマートフォン版にはStockボタンだけでなく、保持中ピースを確認できるStock画面も表示する。

実際の変更箇所・実装内容

template-parts/view-tetris.php
Stockキャンバス、Play/Pause横並び領域、赤/青Key patternボタン、文字のみの操作説明、スマートフォン用Stockボタンを追加した。

assets/js/tetris.js
Stock保持状態、1ピース1回制限、Enterおよびモバイルボタンによる入替、Stock描画を追加。時計回り/反時計回り回転を分離し、赤/青キーパターンを実装。赤モードでは短い下キー入力を即落下として扱い、長押し判定後は現在のピースの自然落下間隔を半分にする。

assets/css/tetris.css
PCのPlay/Pause、Stock/Next、Key pattern、色付き文字説明を整形。スマートフォンではStockプレビューとKey patternを隠し、Pauseとの間隔を空けた円形GoSkullボタンを表示した。

assets/css/theme.css
Linkと履歴専用メニューの骸骨ボタンを44px・padding 7pxへ統一した。

assets/img/smf_GoSkull.png
スマートフォンStockボタン用画像として追加した。

inc/view-shell.php / functions.php / style.css / CHANGELOG.md
0.16.7を履歴メニューとテーマ情報へ追加した。

原因分析・次回改修への指針

Stock機能とキーパターンの基礎実装を優先し、表示文言、長押し判定、説明UIのアニメーション、モバイル用Stockプレビューまで一度に完成させられなかった。


対象ZIP
hiroki-ohnuma-0.16.7-tetris-controls.zip


再利用すべき判断基準

  • 成功判定はPC/スマートフォンの実機確認後に行い、未確認を「成功」と記録しない。
  • 同じ役割の処理は一つの関数・一つの状態・一つのDOMに集約する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.6

当時の改修目的

スマートフォン版の更新履歴について、画面最下部の色、単一モジュール内の余白、一覧の説明、Aboutの骸骨表示、選択中バージョンボタンの中央位置を修正する。

実装した変更

・履歴モード時はクレジットより下の領域まで黒で統一

・単一モジュール表示時の固定的な高さと空白を撤去

・スマートフォンの一覧では閲覧方法モジュールを非表示

・About下部の骸骨をモバイルフッターより前面へ表示

・動きを減らす設定でも骸骨が消えないよう補完

・選択中のバージョンボタンを専用メニューの実測中央へ配置

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.7以降でも撤去されず残った仕組みは継続採用とみなす。

スマートフォン固有の表示問題だけを対象に、履歴モードの最下部、詳細モジュール、About演出、専用メニュー位置を整理した。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.7 の要望・修正内容と照合して確定。

Link側と履歴側で骸骨ボタンの画像サイズ・外径が一致していなかった。

次版で実際に求められたこと:Linkと更新履歴の骸骨ボタン寸法を揃え、PC版Tetrisの操作系、キーパターン、Stock機能、スマートフォン用Stockボタンを追加する。

実際の変更箇所・実装内容

assets/css/theme.css
スマートフォンの履歴モードでhtml、body、本文、固定フッター、その下側まで黒く覆う指定を追加。単一モジュールのmin-height、固定高さ、内部スクロールを解除して内容量どおりに詰めた。閲覧方法をモバイルだけ非表示にし、Aboutの骸骨を固定フッターより前面へ配置した。専用メニューの両端へ中央配置用の可変スペーサーを追加した。

assets/js/theme.js
ボタンのoffsetLeftだけで中央を計算する方式をやめ、ボタンとスクロール領域のgetBoundingClientRect()による実測中心差からscrollLeftを決定。レイアウト確定前後の2段階で再調整するよう変更した。

inc/view-shell.php
履歴専用メニューへ0.16.6を追加した。

functions.php / style.css / CHANGELOG.md
テーマバージョンと更新記録を0.16.6へ更新した。

原因分析・次回改修への指針

履歴側の骸骨ボタンだけを別寸法で定義しており、Link側と共有する寸法変数または共通クラスを使っていなかった。


対象ZIP
hiroki-ohnuma-0.16.6-history-mobile-fix.zip


再利用すべき判断基準

  • ページ別・版別の上書きを増やさず、共通クラス・共通変数・単一レンダラーへ統合する。
  • 同じ役割の処理は一つの関数・一つの状態・一つのDOMに集約する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.5

当時の改修目的

履歴専用メニュー、スマートフォン表示、フッター、書体を、履歴閲覧モードとしてより統一された操作と外観へ整える。

実装した変更

・選択したバージョンボタンを専用メニュー中央へ移動

・専用メニュー右端に本来の色の骸骨ボタンを追加

・スマートフォンの履歴モード時のみ最下部フッターを黒化

・スマートフォンの空項目と余分な空白を圧縮

・スマートフォンの履歴本文を約12pxへ縮小

・履歴本文と専用メニューを等幅書体へ統一

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.6以降でも撤去されず残った仕組みは継続採用とみなす。

履歴の選択位置、専用メニューの左右構造、モバイル表示密度、履歴専用書体とフッター色を一つの履歴モード内へ限定して実装した。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.6 の要望・修正内容と照合して確定。

スマホでクレジットより下が黒くならず、単一モジュールの空白行が残り、「閲覧方法」が表示された。About下部の骸骨も表示されず、選択ボタンの中央位置もずれた。

次版で実際に求められたこと:スマートフォン版の更新履歴について、画面最下部の色、単一モジュール内の余白、一覧の説明、Aboutの骸骨表示、選択中バージョンボタンの中央位置を修正する。

実際の変更箇所・実装内容

inc/view-shell.php
履歴バージョン配列へ0.16.5を追加。専用メニュー右端へ、反転フィルターを使わない骸骨画像ボタンを追加した。

assets/js/theme.js
バージョン選択時に該当ボタンの中心と横スクロール領域の中心を一致させる処理を追加。右端の骸骨ボタンから履歴一覧へ戻る処理と、内容が空の項目を判定する処理を追加した。

assets/css/theme.css
履歴専用メニューをAbout・バージョン列・骸骨の3領域に変更。履歴ページと専用メニューを等幅書体へ統一し、スマートフォンの本文を12px基準へ縮小。空項目、余白、内部スクロールの高さをモバイル向けに圧縮し、履歴モード時のモバイルフッターを黒へ変更した。

functions.php / style.css / CHANGELOG.md
テーマバージョンと更新記録を0.16.5へ更新した。

原因分析・次回改修への指針

フッターの見えている下端全体ではなく個別要素だけを着色していた。モジュール内部に残るmin-height・固定高さ・overflowを解除し切れず、骸骨レイヤーは固定フッターとの重なりと視差軽減設定への配慮が不足した。中央化もoffsetLeftによる概算だった。


対象ZIP
hiroki-ohnuma-0.16.5-history-menu-mobile.zip


再利用すべき判断基準

  • ページ別・版別の上書きを増やさず、共通クラス・共通変数・単一レンダラーへ統合する。
  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 新構造へ移行したら、競合する旧CSS・旧JS・互換処理・固定高を削除する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.4

当時の改修目的

履歴ページへの白黒反転を滑らかなディゾルブにし、Linkのメッセージ、履歴専用メニュー、バージョン順を既存サイトの操作設計へ揃える。

実装した変更

・Thank you, everyone — always! をPC/スマートフォンとも表示領域の中央へ固定

・履歴を最新バージョンから上に並べる

・履歴専用メニューを既存のページ専用メニューと同じ3領域構造・横スクロール動作へ統一

・履歴専用メニューの背景を完全な黒へ統一

・履歴への入退場にブラウザのView Transitionを用いた0.86秒の全画面クロスディゾルブを実装

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.5以降でも撤去されず残った仕組みは継続採用とみなす。

中央配置、最新順、専用メニューの初期位置と横移動、黒背景、全画面ディゾルブを一つの遷移処理へ統合した。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.5 の要望・修正内容と照合して確定。

選択バージョンのボタンが専用メニュー中央へ正確に来ず、右端が空欄だった。スマホでは履歴フッター下部、空白行、文字サイズ、等幅書体にも未対応だった。

次版で実際に求められたこと:履歴専用メニュー、スマートフォン表示、フッター、書体を、履歴閲覧モードとしてより統一された操作と外観へ整える。

実際の変更箇所・実装内容

assets/css/theme.css
Link専用メニューを相対配置にし、メッセージを中央基準、骸骨ボタンを右端基準で独立配置。履歴専用メニューを左・中央・右の共通グリッドへ変更し、黒背景とView Transition用アニメーションを追加。

assets/js/theme.js
履歴との往来だけ、色属性を先に変えて待つ方式を廃止。旧画面と新画面を0.86秒重ねて溶かす全画面View Transitionへ変更。非対応環境には従来型フェードの代替処理を残した。

inc/view-shell.php
履歴メニューのバージョン配列へ0.16.4を追加し、最新順へ反転。

assets/history/history-content.html
履歴モジュールを最新順へ並べ替え、0.16.4の作業記録を追加。

functions.php / style.css / CHANGELOG.md
テーマバージョンと更新履歴を0.16.4へ更新。

原因分析・次回改修への指針

ボタン中央化が概算位置に依存し、スクロール領域の実測中心を使っていなかった。専用メニュー右端とモバイル固有の固定フッター領域、単一モジュールの固定高さ、書体指定をまとめて扱えていなかった。


対象ZIP
hiroki-ohnuma-0.16.4-history-dissolve.zip


再利用すべき判断基準

  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 成功判定はPC/スマートフォンの実機確認後に行い、未確認を「成功」と記録しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.3

当時の改修目的

履歴閲覧モードのPC表示、白黒反転、色変化、一覧情報量、Aboutの骸骨演出を既存サイトの設計へ揃える。

実装した変更

・Linkページのメッセージを左右の構造に影響されず中央配置

・履歴モジュール幅と間隔をSchedule等の共通モジュール寸法へ統一

・一覧では改修依頼内容と改修項目だけを表示

・ヘッダー、ページ専用メニュー、Updatesを完全な黒基調へ反転

・履歴モード出入りをフェードアウト/インではなく0.86秒の色ディゾルブへ変更

・About下部の骸骨へ複数色とランダム動作を追加

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.4以降でも撤去されず残った仕組みは継続採用とみなす。

指定されたPC版の視覚調整と履歴モードの色遷移、一覧/単一表示の情報分離、複数色の骸骨演出を実装。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.4 の要望・修正内容と照合して確定。

Thank you文が右へ寄ったまま、履歴遷移のディゾルブが成立せず、履歴順序・専用メニュー背景・ボタン初期位置にも不整合が残った。

次版で実際に求められたこと:履歴ページへの白黒反転を滑らかなディゾルブにし、Linkのメッセージ、履歴専用メニュー、バージョン順を既存サイトの操作設計へ揃える。

実際の変更箇所・実装内容

変更ファイル

assets/css/theme.css、assets/js/theme.js、assets/history/history-content.html、inc/view-shell.php、functions.php、style.css、CHANGELOG.md、docs/BETA1-CHANGELOG.md

履歴専用のCSS選択範囲をbrand、news ticker、context navigationまで拡張し、一覧時の第3・第4項目を非表示化。SPA遷移では履歴との往来だけ通常のsurface opacity animationを使用せず、data-current-viewの切替による色補間を0.86秒完了させてから内容を交換する方式へ変更しました。

原因分析・次回改修への指針

中央配置を周辺要素の幅に依存するレイアウトで処理し、旧画面と新画面を同時に重ねない色切替だったため、見た目の中心とクロスディゾルブを実現できなかった。履歴専用メニューも既存ページ専用メニューと別構造のままだった。


対象ZIP
hiroki-ohnuma-0.16.3-history-refine.zip


再利用すべき判断基準

  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 新構造へ移行したら、競合する旧CSS・旧JS・互換処理・固定高を削除する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.2

当時の改修目的

Linkのページ専用メニューから更新履歴閲覧モードへ入り、テーマ本来のSPA、0.86秒ディゾルブ、モジュール型一覧・単一表示、About表示、骸骨集合演出を統合する。

実装した変更

・Link専用メニュー右端に黒い骸骨PNGボタンを追加

・履歴表示を独立HTMLへの外部遷移ではなく、既存SPAの新規viewとして追加

・履歴への入退場時に背景色・文字色を0.86秒でディゾルブ

・履歴専用メニューにAboutと全バージョン番号を追加

・beta表記をβへ短縮

・一覧では概要4項目、単一表示では詳細2項目を追加表示

・PCの各項目を左右50%で配置し、項目内スクロールを維持

・About表示時にボタンをBackへ変更

・About下部に1〜6体の骸骨がランダム位置から集合する演出を追加

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.3以降でも撤去されず残った仕組みは継続採用とみなす。

履歴をテーマ内部のviewとして統合し、主メニュー、履歴専用メニュー、ブラウザ履歴、色調遷移、モジュール開閉を既存サイトの仕組みに接続した。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.3 の要望・修正内容と照合して確定。

Linkページの中央文、履歴モジュール寸法・一覧項目、履歴モード全体の白黒反転、Updates反転、0.86秒ディゾルブ、Aboutの骸骨演出が要求どおりにならなかった。

次版で実際に求められたこと:履歴閲覧モードのPC表示、白黒反転、色変化、一覧情報量、Aboutの骸骨演出を既存サイトの設計へ揃える。

実際の変更箇所・実装内容

functions.php
テーマバージョンを0.16.2へ更新し、履歴機能ファイルを読み込むよう変更。

inc/history.php
仮想URL /history/、query var、テンプレート選択、テーマ切替時のrewrite登録を追加。

inc/view-shell.php
history viewの判定、URL、タイトル、専用メニュー、本文生成、REST view endpointを追加。Link専用メニューには履歴モードへ入る骸骨ボタンを追加。

assets/history/history-content.html
0.14.1〜0.16.2の履歴モジュール本文を格納。説明用の二本線を削除し、閲覧方法の文章をモジュール操作の説明へ変更。

assets/css/theme.css
履歴モードの黒背景・白文字・オレンジボタン、0.86秒色調遷移、丸みとニューモーフィック陰影、一覧/単一表示、PC 50%分割、内部スクロール、About、骸骨集合レイヤーを追加。

assets/js/theme.js
履歴モジュールの開閉、バージョン直接移動、About/Back切替、ブラウザ履歴連携、ランダムな骸骨集合演出を追加。既存のSPA遷移処理へhistory viewを接続。

page-templates/history.php
通常アクセス時も既存ヘッダー・フッターを使ってhistory viewを表示するテンプレートを追加。

docs/BETA1-CHANGELOG.md / CHANGELOG.md
0.16.2の変更記録を追加。

原因分析・次回改修への指針

初版では履歴ビューの追加を優先し、既存のページ専用メニューやSPA遷移へ個別の上書きを重ねたため、白背景が残り、色変更も連続的なディゾルブではなく画面のフェードアウト/フェードインに見えた。モジュールの表示条件と既存サイズ基準の統一も不足していた。


対象ZIP
hiroki-ohnuma-0.16.2-history.zip


再利用すべき判断基準

  • ページ別・版別の上書きを増やさず、共通クラス・共通変数・単一レンダラーへ統合する。
  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 新構造へ移行したら、競合する旧CSS・旧JS・互換処理・固定高を削除する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.1

当時の改修目的

機能を変えず、完成版として不要コード・読み込み・書式を安全に整理する。

実装した変更

変更: assets/css/theme.css, functions.php, inc/setup.php, style.css

追加: CHANGELOG.md, docs/RELEASE-AUDIT-0.16.1.md

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.2以降でも撤去されず残った仕組みは継続採用とみなす。

開発プレビューの本番分離、Google Fonts @import廃止とenqueue/preconnect、正式版表記、CHANGELOG/監査文書追加。

後続版で未達・再修正と判明した項目

判定:当時は成功扱いだったが、後続版 0.16.2 の修正依頼を踏まえ、未検証部分を再評価。

機能コードは意図的に触っていないため、新機能上の失敗なし。

次版で実際に求められたこと:Linkのページ専用メニューから更新履歴閲覧モードへ入り、テーマ本来のSPA、0.86秒ディゾルブ、モジュール型一覧・単一表示、About表示、骸骨集合演出を統合する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, functions.php, inc/setup.php, style.css
追加: CHANGELOG.md, docs/RELEASE-AUDIT-0.16.1.md

addedCHANGELOG.md
# Changelog

## 0.16.1

Release-cleanup build based on `0.16.0-beta.17`. Public behavior and layout are unchanged.

- Load localhost preview utilities only in local/development environments.
- Remove the production admin-menu entry for the development preview.
- Load Archivo Black through WordPress enqueue dependencies instead of CSS `@import`.
- Add preconnect hints for both Google Fonts origins.
- Promote the theme metadata from the beta label to version `0.16.1`.
- Preserve the completed focus, scroll, responsive, calendar, and Tetris behavior without refactoring those runtime paths.
addeddocs/RELEASE-AUDIT-0.16.1.md
# Release audit — 0.16.1

## Scope

This release is a conservative cleanup of `0.16.0-beta.17`. No layout, focus-view, scroll-position, SPA, calendar, or Tetris behavior was intentionally changed.

## Changes made

1. Development preview files are required only on localhost or when `wp_get_environment_type()` returns `local` or `development`.
2. Archivo Black is enqueued as a WordPress stylesheet dependency. The blocking CSS `@import` was removed.
3. Theme metadata and runtime version were synchronized at `0.16.1`.
4. A release changelog was added.

## Deliberately retained

- The two visually identical skull files remain separate because they belong to different asset namespaces (site artwork and Tetris pieces). Removing one would couple unrelated components for a negligible size saving.
- Legacy one-time migrations remain intact to protect installations that have not yet completed them.
- `theme.js` was not mechanically split or shortened. Its timing-sensitive viewport, focus, and SPA behavior is stable and was therefore left untouched.

## Validation

- PHP syntax checked for every PHP file.
- JavaScript syntax checked for every JavaScript file.
- CSS delimiter balance checked.
- Theme header and ZIP root structure checked.
- No debug calls (`console.log`, `var_dump`, `print_r`, `die`, `dd`) found in runtime files.
changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.1/assets/css/theme.css
@@ -1,5 +1,3 @@
-@import url("https://fonts.googleapis.com/css2?family=Archivo+Black&display=swap");
-
 :root {
   --blue: #1e73be;
   --green: #309b55;
changedfunctions.php
--- previous/functions.php
+++ 0.16.1/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.17' );
+define( 'HO_THEME_VERSION', '0.16.1' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
@@ -20,5 +20,18 @@
 require_once get_template_directory() . '/inc/legacy-redirects.php';
 require_once get_template_directory() . '/inc/importer.php';
 require_once get_template_directory() . '/inc/bootstrap-data.php';
-require_once get_template_directory() . '/inc/dev-preview.php';
-require_once get_template_directory() . '/inc/preview.php';
+
+/**
+ * Load development-only preview tools outside production.
+ */
+$ho_is_local_host = isset( $_SERVER['HTTP_HOST'] )
+	&& ( false !== strpos( strtolower( (string) $_SERVER['HTTP_HOST'] ), 'localhost' )
+		|| false !== strpos( strtolower( (string) $_SERVER['HTTP_HOST'] ), '127.0.0.1' ) );
+$ho_is_development = in_array( wp_get_environment_type(), array( 'local', 'development' ), true );
+
+if ( $ho_is_local_host || $ho_is_development ) {
+	require_once get_template_directory() . '/inc/dev-preview.php';
+	require_once get_template_directory() . '/inc/preview.php';
+}
+
+unset( $ho_is_local_host, $ho_is_development );
changedinc/setup.php
--- previous/inc/setup.php
+++ 0.16.1/inc/setup.php
@@ -29,7 +29,13 @@
 add_action( 'after_setup_theme', 'ho_theme_setup' );
 
 function ho_enqueue_assets() {
-	wp_enqueue_style( 'ho-theme', get_template_directory_uri() . '/assets/css/theme.css', array(), HO_THEME_VERSION );
+	wp_enqueue_style(
+		'ho-brand-font',
+		'https://fonts.googleapis.com/css2?family=Archivo+Black&display=swap',
+		array(),
+		null
+	);
+	wp_enqueue_style( 'ho-theme', get_template_directory_uri() . '/assets/css/theme.css', array( 'ho-brand-font' ), HO_THEME_VERSION );
 	wp_enqueue_script( 'ho-theme', get_template_directory_uri() . '/assets/js/theme.js', array(), HO_THEME_VERSION, true );
 	wp_localize_script(
 		'ho-theme',
@@ -82,12 +88,16 @@
 add_action( 'init', 'ho_trim_frontend_assets' );
 
 function ho_preload_brand_font( $urls, $relation_type ) {
-	if ( 'preconnect' === $relation_type ) {
-		$urls[] = array(
-			'href'        => 'https://fonts.gstatic.com',
-			'crossorigin' => 'anonymous',
-		);
+	if ( 'preconnect' !== $relation_type ) {
+		return $urls;
 	}
+
+	$urls[] = 'https://fonts.googleapis.com';
+	$urls[] = array(
+		'href'        => 'https://fonts.gstatic.com',
+		'crossorigin' => 'anonymous',
+	);
+
 	return $urls;
 }
 add_filter( 'wp_resource_hints', 'ho_preload_brand_font', 10, 2 );
changedstyle.css
--- previous/style.css
+++ 0.16.1/style.css
@@ -1,9 +1,9 @@
 /*
-Theme Name: Hiroki Ohnuma(β.1)
+Theme Name: Hiroki Ohnuma
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
-Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.17
+Description: Lightweight official-site theme for Hiroki Ohnuma, with modular archives, focused record views, calendar integration, and responsive navigation.
+Version: 0.16.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針


対象ZIP
hiroki-ohnuma-0.16.1.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.17

当時の改修目的

スマホの可変ブラウザUIに耐える位置合わせへ変更し、場当たり的な座標コードを精査する。

実装した変更

・単一表示と一覧復帰の位置合わせを、画面上の絶対座標保存から「現在の可視領域上端からの相対位置」へ変更。

・スマホのブラウザUI伸縮や可視領域変化後でも、同じモジュール上端が同じ位置に戻る構造へ統一。

・一覧末尾の位置合わせ用余白を、実際のscrollingElementの高さから必要量だけ算出。

・旧`activeVisualTop`と画面絶対座標依存の`alignElementToScreenTop()`を削除。

・一覧再表示直後のレイアウト確定後に一度だけ位置を確定し、スクロールアンカー等による差分のみ同一処理内で補正。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.1以降でも撤去されず残った仕組みは継続採用とみなす。

絶対画面座標を廃止し、可視領域上端からの相対offsetへ統一。旧activeVisualTop等を削除し、PC/スマホで位置合わせを成立。

後続版で未達・再修正と判明した項目

判定:当時は成功扱いだったが、後続版 0.16.1 の修正依頼を踏まえ、未検証部分を再評価。

ユーザー確認上、機能面の明示的失敗はなし。完成候補版となった。

次版で実際に求められたこと:機能を変えず、完成版として不要コード・読み込み・書式を安全に整理する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.17/assets/js/theme.js
@@ -164,29 +164,48 @@
     });
   }
 
-  function alignElementToScreenTop(element, screenTop, reserve) {
+  function alignElementToVisibleOffset(element, visibleOffset, reserve) {
     if (!element) return Promise.resolve();
-    syncVisibleMetrics();
+    cancelScrollAnimation();
+    var scrollingElement = document.scrollingElement || document.documentElement;
+    var offset = Number.isFinite(visibleOffset) ? visibleOffset : 8;
+
     return new Promise(function (resolve) {
-      window.requestAnimationFrame(function () {
+      function align() {
         if (!document.documentElement.contains(element)) {
           resolve();
           return;
         }
 
-        if (reserve) reserve.style.height = '0px';
-        var destination = window.scrollY + element.getBoundingClientRect().top - screenTop;
-        var maxScroll = Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
-        var shortfall = Math.ceil(destination - maxScroll);
-
-        if (reserve && shortfall > 0) {
-          reserve.style.height = shortfall + 'px';
-          destination = window.scrollY + element.getBoundingClientRect().top - screenTop;
-        }
-
-        window.scrollTo({ top: Math.max(0, Math.round(destination)), left: 0, behavior: 'auto' });
-        resolve();
-      });
+        var targetTop = visibleBounds().top + offset;
+        var destination = scrollingElement.scrollTop + element.getBoundingClientRect().top - targetTop;
+        var maxScroll = Math.max(0, scrollingElement.scrollHeight - scrollingElement.clientHeight);
+        var shortfall = Math.max(0, Math.ceil(destination - maxScroll));
+
+        if (reserve) reserve.style.height = shortfall + 'px';
+
+        window.requestAnimationFrame(function () {
+          if (!document.documentElement.contains(element)) {
+            resolve();
+            return;
+          }
+          targetTop = visibleBounds().top + offset;
+          destination = scrollingElement.scrollTop + element.getBoundingClientRect().top - targetTop;
+          window.scrollTo({ top: Math.max(0, Math.round(destination)), left: 0, behavior: 'auto' });
+          window.requestAnimationFrame(function () {
+            if (document.documentElement.contains(element)) {
+              var correction = element.getBoundingClientRect().top - (visibleBounds().top + offset);
+              if (Math.abs(correction) >= 1) {
+                window.scrollTo({ top: Math.max(0, Math.round(scrollingElement.scrollTop + correction)), left: 0, behavior: 'auto' });
+              }
+            }
+            resolve();
+          });
+        });
+      }
+
+      if (reserve) reserve.style.height = '0px';
+      window.requestAnimationFrame(align);
     });
   }
 
@@ -557,7 +576,7 @@
 
     var activeSource = null;
     var activeClone = null;
-    var activeVisualTop = null;
+    var activeVisibleOffset = 8;
     var listUrl = archiveUrl();
     var archiveTitle = document.title;
 
@@ -645,9 +664,7 @@
       var returnTarget = options.toStart ? currentViewStartTarget(page.getAttribute('data-view-root')) : sourceToRestore;
       var useDissolve = options.dissolve !== false && !options.toStart && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
       var outgoingLayer = null;
-      var targetTop = typeof activeVisualTop === 'number'
-        ? activeVisualTop
-        : visibleBounds().top + 8;
+      var targetOffset = Number.isFinite(activeVisibleOffset) ? activeVisibleOffset : 8;
 
       if (useDissolve) {
         var outgoingRect = activeClone.getBoundingClientRect();
@@ -669,12 +686,12 @@
       if (sourceToRestore) sourceToRestore.removeAttribute('aria-current');
       activeSource = null;
       activeClone = null;
-      activeVisualTop = null;
+      activeVisibleOffset = 8;
       document.title = archiveTitle;
 
       var alignment = options.toStart
         ? (returnReserve.style.height = '0px', activateTarget(returnTarget, { animate: options.animate !== false }))
-        : alignElementToScreenTop(returnTarget, targetTop, returnReserve);
+        : alignElementToVisibleOffset(returnTarget, targetOffset, returnReserve);
 
       return alignment.then(function () {
         if (!useDissolve || !outgoingLayer) {
@@ -723,7 +740,7 @@
         replaceLocation(archiveUrl() + '#focus-' + type + '-' + itemId, { hoPartial: true, hoListFocus: true });
         document.title = link.textContent.trim() + ' – ' + archiveTitle;
         activateTarget(activeClone, { animate: false, alignElement: true }).then(function () {
-          if (activeClone) activeVisualTop = activeClone.getBoundingClientRect().top;
+          if (activeClone) activeVisibleOffset = activeClone.getBoundingClientRect().top - visibleBounds().top;
         });
         loadDetails(activeClone, type, itemId);
       });
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.17/docs/BETA1-CHANGELOG.md
@@ -205,3 +205,12 @@
 - Year anchors now align to the current visible content top, including the WordPress admin bar and responsive context menu position.
 - Replaced fixed IntersectionObserver margins with one requestAnimationFrame-throttled section tracker using the same live bounds.
 - Removed calendar/Tetris page-specific top compensation and legacy CSS scroll offsets.
+
+
+## 0.16.0-beta.17
+
+- 単一表示と一覧復帰の位置合わせを、画面上の絶対座標保存から「現在の可視領域上端からの相対位置」へ変更。
+- スマホのブラウザUI伸縮や可視領域変化後でも、同じモジュール上端が同じ位置に戻る構造へ統一。
+- 一覧末尾の位置合わせ用余白を、実際のscrollingElementの高さから必要量だけ算出。
+- 旧`activeVisualTop`と画面絶対座標依存の`alignElementToScreenTop()`を削除。
+- 一覧再表示直後のレイアウト確定後に一度だけ位置を確定し、スクロールアンカー等による差分のみ同一処理内で補正。
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.17/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.16' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.17' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.17/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.16
+Version: 0.16.0-beta.17
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.17.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.16-fixed

当時の改修目的

beta.16のZIP構造をWordPressへインストール可能な単一テーマフォルダへ修正する。

実装した変更

変更: assets/js/theme.js

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.17以降でも撤去されず残った仕組みは継続採用とみなす。

ZIP梱包構造を修正しインストール可能にした。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.17 の要望・修正内容と照合して確定。

スマホの位置合わせ不具合は残った。

次版で実際に求められたこと:スマホの可変ブラウザUIに耐える位置合わせへ変更し、場当たり的な座標コードを精査する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/js/theme.js

changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.16-fixed/assets/js/theme.js
@@ -765,7 +765,6 @@
       window.removeEventListener('resize', onResize);
       clearParadeLayer();
       paradeLayer.remove();
-      returnReserve.remove();
       returnControl.remove();
       stage.remove();
       list.hidden = false;

原因分析・次回改修への指針

この版は梱包修正のみで、絶対座標方式のロジックを変更していなかった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.16-fixed.zip


再利用すべき判断基準

  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.16

当時の改修目的

単一表示の実カード座標を記録し、Back後の一覧カードを同じ画面座標へ合わせる。

実装した変更

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, style.css

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.16-fixed以降でも撤去されず残った仕組みは継続採用とみなす。

実座標記録、一覧末尾の不足スクロール余地、カード実寸ベースのディゾルブを実装。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.16-fixed の要望・修正内容と照合して確定。

最初のZIPはテーマフォルダが二重でインストール不能。さらにスマホでは出鱈目な位置へ飛んだ。

次版で実際に求められたこと:beta.16のZIP構造をWordPressへインストール可能な単一テーマフォルダへ修正する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.16/assets/css/theme.css
@@ -374,6 +374,7 @@
 .focus-page.is-focus [data-focus-item].is-picked.has-focus-details > .focus-item__extras { grid-template-rows:1fr; opacity:1; visibility:visible; }
 .focus-page.is-focus .event-card.is-picked.has-focus-details > .focus-item__extras { margin-top:var(--module-gap); }
 .focus-stage[hidden] { display:none; }
+.focus-return-reserve { height:0; pointer-events:none; width:100%; }
 .focus-page.is-focus [data-focus-list] { display:none; }
 [data-focus-list] { opacity:1; transition:opacity .86s ease; }
 [data-focus-list].is-dissolve-entering { opacity:0; }
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.16/assets/js/theme.js
@@ -160,6 +160,32 @@
         var bounds = visibleBounds();
         var destination = window.scrollY + anchor.getBoundingClientRect().top - bounds.top - 8;
         animateScroll(destination, options).then(resolve);
+      });
+    });
+  }
+
+  function alignElementToScreenTop(element, screenTop, reserve) {
+    if (!element) return Promise.resolve();
+    syncVisibleMetrics();
+    return new Promise(function (resolve) {
+      window.requestAnimationFrame(function () {
+        if (!document.documentElement.contains(element)) {
+          resolve();
+          return;
+        }
+
+        if (reserve) reserve.style.height = '0px';
+        var destination = window.scrollY + element.getBoundingClientRect().top - screenTop;
+        var maxScroll = Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
+        var shortfall = Math.ceil(destination - maxScroll);
+
+        if (reserve && shortfall > 0) {
+          reserve.style.height = shortfall + 'px';
+          destination = window.scrollY + element.getBoundingClientRect().top - screenTop;
+        }
+
+        window.scrollTo({ top: Math.max(0, Math.round(destination)), left: 0, behavior: 'auto' });
+        resolve();
       });
     });
   }
@@ -511,6 +537,11 @@
     stage.hidden = true;
     page.insertBefore(stage, list);
 
+    var returnReserve = document.createElement('div');
+    returnReserve.className = 'focus-return-reserve';
+    returnReserve.setAttribute('aria-hidden', 'true');
+    list.insertAdjacentElement('afterend', returnReserve);
+
     var returnControl = document.createElement('button');
     returnControl.className = 'focus-return-control';
     returnControl.type = 'button';
@@ -526,6 +557,7 @@
 
     var activeSource = null;
     var activeClone = null;
+    var activeVisualTop = null;
     var listUrl = archiveUrl();
     var archiveTitle = document.title;
 
@@ -609,16 +641,17 @@
     function closeItem(options) {
       if (!activeClone) return Promise.resolve();
       options = options || {};
-      var returnTarget = options.toStart ? currentViewStartTarget(page.getAttribute('data-view-root')) : activeSource;
+      var sourceToRestore = activeSource;
+      var returnTarget = options.toStart ? currentViewStartTarget(page.getAttribute('data-view-root')) : sourceToRestore;
       var useDissolve = options.dissolve !== false && !options.toStart && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
       var outgoingLayer = null;
-      var outgoingRect = null;
+      var targetTop = typeof activeVisualTop === 'number'
+        ? activeVisualTop
+        : visibleBounds().top + 8;
 
       if (useDissolve) {
-        outgoingRect = stage.getBoundingClientRect();
-        outgoingLayer = stage.cloneNode(true);
-        outgoingLayer.removeAttribute('data-focus-stage');
-        outgoingLayer.removeAttribute('hidden');
+        var outgoingRect = activeClone.getBoundingClientRect();
+        outgoingLayer = activeClone.cloneNode(true);
         outgoingLayer.classList.add('focus-dissolve-layer');
         outgoingLayer.style.left = Math.round(outgoingRect.left) + 'px';
         outgoingLayer.style.top = Math.round(outgoingRect.top) + 'px';
@@ -633,15 +666,17 @@
       stage.hidden = true;
       list.hidden = false;
       page.classList.remove('is-focus', 'is-focus-opening');
-      if (activeSource) activeSource.removeAttribute('aria-current');
+      if (sourceToRestore) sourceToRestore.removeAttribute('aria-current');
       activeSource = null;
       activeClone = null;
+      activeVisualTop = null;
       document.title = archiveTitle;
 
-      return activateTarget(returnTarget, {
-        animate: options.toStart ? options.animate !== false : false,
-        alignElement: !options.toStart
-      }).then(function () {
+      var alignment = options.toStart
+        ? (returnReserve.style.height = '0px', activateTarget(returnTarget, { animate: options.animate !== false }))
+        : alignElementToScreenTop(returnTarget, targetTop, returnReserve);
+
+      return alignment.then(function () {
         if (!useDissolve || !outgoingLayer) {
           list.classList.remove('is-dissolve-entering');
           paradeLayer.classList.remove('is-dissolving-out');
@@ -682,11 +717,14 @@
         stage.replaceChildren(activeClone);
         stage.hidden = false;
         list.hidden = true;
+        returnReserve.style.height = '0px';
         page.classList.remove('is-focus-opening');
         page.classList.add('is-focus');
         replaceLocation(archiveUrl() + '#focus-' + type + '-' + itemId, { hoPartial: true, hoListFocus: true });
         document.title = link.textContent.trim() + ' – ' + archiveTitle;
-        activateTarget(activeClone, { animate: false, alignElement: true });
+        activateTarget(activeClone, { animate: false, alignElement: true }).then(function () {
+          if (activeClone) activeVisualTop = activeClone.getBoundingClientRect().top;
+        });
         loadDetails(activeClone, type, itemId);
       });
     }
@@ -727,6 +765,7 @@
       window.removeEventListener('resize', onResize);
       clearParadeLayer();
       paradeLayer.remove();
+      returnReserve.remove();
       returnControl.remove();
       stage.remove();
       list.hidden = false;
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.16/docs/BETA1-CHANGELOG.md
@@ -1,4 +1,4 @@
-## 0.16.0-beta.15
+## 0.16.0-beta.16
 
 - 単一表示と一覧復帰で、同じモジュールカードの上端を同一の可視領域基準線へ合わせるよう統一。
 - 単一表示側はステージ枠ではなくカード本体を位置決め対象に変更。
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.16/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.15' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.16' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.16/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.15
+Version: 0.16.0-beta.16
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

梱包ミスに加え、PCで取得したような画面絶対座標方式がモバイルブラウザUI伸縮に耐えなかった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.16.zip


再利用すべき判断基準

  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 成功判定はPC/スマートフォンの実機確認後に行い、未確認を「成功」と記録しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.15

当時の改修目的

単一表示とBack後の同一カード上端を同じ表示高さへ揃える。

実装した変更

・単一表示と一覧復帰で、同じモジュールカードの上端を同一の可視領域基準線へ合わせるよう統一。

・単一表示側はステージ枠ではなくカード本体を位置決め対象に変更。

・Backのディゾルブ中に一覧側カードを同じ基準線へ即時配置し、縦方向に動かず表示だけが切り替わる構造へ整理。

・Focus専用の別座標補正は追加せず、既存の共通位置決め関数へ `alignElement` オプションを統合。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.16以降でも撤去されず残った仕組みは継続採用とみなす。

カード本体を位置合わせ対象として共通処理へ渡した。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.16 の要望・修正内容と照合して確定。

実際には高さが揃わなかった。

次版で実際に求められたこと:単一表示の実カード座標を記録し、Back後の一覧カードを同じ画面座標へ合わせる。

実際の変更箇所・実装内容

変更ファイル

変更: assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.15/assets/js/theme.js
@@ -148,7 +148,7 @@
 
   function activateTarget(target, options) {
     var activation = ++targetActivation;
-    var anchor = contextAnchor(target);
+    var anchor = options && options.alignElement ? target : contextAnchor(target);
     if (!anchor) return Promise.resolve();
     syncVisibleMetrics();
     return new Promise(function (resolve) {
@@ -638,7 +638,10 @@
       activeClone = null;
       document.title = archiveTitle;
 
-      return activateTarget(returnTarget, { animate: options.toStart ? options.animate !== false : false }).then(function () {
+      return activateTarget(returnTarget, {
+        animate: options.toStart ? options.animate !== false : false,
+        alignElement: !options.toStart
+      }).then(function () {
         if (!useDissolve || !outgoingLayer) {
           list.classList.remove('is-dissolve-entering');
           paradeLayer.classList.remove('is-dissolving-out');
@@ -683,7 +686,7 @@
         page.classList.add('is-focus');
         replaceLocation(archiveUrl() + '#focus-' + type + '-' + itemId, { hoPartial: true, hoListFocus: true });
         document.title = link.textContent.trim() + ' – ' + archiveTitle;
-        activateTarget(stage, { animate: false });
+        activateTarget(activeClone, { animate: false, alignElement: true });
         loadDetails(activeClone, type, itemId);
       });
     }
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.15/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.16.0-beta.15
+
+- 単一表示と一覧復帰で、同じモジュールカードの上端を同一の可視領域基準線へ合わせるよう統一。
+- 単一表示側はステージ枠ではなくカード本体を位置決め対象に変更。
+- Backのディゾルブ中に一覧側カードを同じ基準線へ即時配置し、縦方向に動かず表示だけが切り替わる構造へ整理。
+- Focus専用の別座標補正は追加せず、既存の共通位置決め関数へ `alignElement` オプションを統合。
+
 ## 0.16.0-beta.14
 
 - 単一表示のタイトルクリックをBackと同じ復帰処理へ統合。
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.15/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.14' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.15' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.15/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.14
+Version: 0.16.0-beta.15
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

単一表示時の実座標を記録せず、「同じ基準線を参照する」だけだったため。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.15.zip


再利用すべき判断基準

  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.14

当時の改修目的

単一表示のタイトル・Back・ブラウザ戻るを同じ復帰処理へ統一し、一覧復帰をディゾルブにする。

実装した変更

・単一表示のタイトルクリックをBackと同じ復帰処理へ統合。

・一覧復帰をスクロール移動から0.86秒のクロスディゾルブへ変更。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.15以降でも撤去されず残った仕組みは継続採用とみなす。

0.86秒クロスディゾルブ、復帰経路統一、主メニュー先頭移動はスクロール維持。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.15 の要望・修正内容と照合して確定。

単一表示と一覧側の同一モジュールの高さが一致しなかった。

次版で実際に求められたこと:単一表示とBack後の同一カード上端を同じ表示高さへ揃える。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.14/assets/css/theme.css
@@ -375,6 +375,12 @@
 .focus-page.is-focus .event-card.is-picked.has-focus-details > .focus-item__extras { margin-top:var(--module-gap); }
 .focus-stage[hidden] { display:none; }
 .focus-page.is-focus [data-focus-list] { display:none; }
+[data-focus-list] { opacity:1; transition:opacity .86s ease; }
+[data-focus-list].is-dissolve-entering { opacity:0; }
+.focus-dissolve-layer { box-sizing:border-box; opacity:1; overflow:hidden; pointer-events:none; position:fixed; transition:opacity .86s ease; z-index:9987; }
+.focus-dissolve-layer.is-dissolving-out { opacity:0; }
+.focus-parade-layer { transition:opacity .86s ease; }
+.focus-parade-layer.is-dissolving-out { opacity:0; }
 .focus-stage > [data-focus-item] { margin:0; }
 .focus-stage .is-picked.has-focus-details { overflow:visible; position:relative; z-index:20; }
 .focus-stage .is-picked.has-focus-details .focus-item__extras-inner { overflow:visible; }
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.14/assets/js/theme.js
@@ -554,12 +554,14 @@
       return url.href;
     }
 
-    function sanitizeClone(clone) {
+    function sanitizeClone(clone, returnUrl) {
       clone.removeAttribute('id');
       clone.querySelectorAll('[id]').forEach(function (node) { node.removeAttribute('id'); });
       clone.querySelectorAll('[data-focus-link]').forEach(function (link) {
         link.removeAttribute('data-focus-link');
-        link.setAttribute('tabindex', '-1');
+        link.setAttribute('data-focus-close', '');
+        link.setAttribute('href', returnUrl);
+        link.removeAttribute('tabindex');
       });
       return clone;
     }
@@ -605,10 +607,28 @@
     }
 
     function closeItem(options) {
-      if (!activeClone) return;
+      if (!activeClone) return Promise.resolve();
       options = options || {};
       var returnTarget = options.toStart ? currentViewStartTarget(page.getAttribute('data-view-root')) : activeSource;
-      clearParadeLayer();
+      var useDissolve = options.dissolve !== false && !options.toStart && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
+      var outgoingLayer = null;
+      var outgoingRect = null;
+
+      if (useDissolve) {
+        outgoingRect = stage.getBoundingClientRect();
+        outgoingLayer = stage.cloneNode(true);
+        outgoingLayer.removeAttribute('data-focus-stage');
+        outgoingLayer.removeAttribute('hidden');
+        outgoingLayer.classList.add('focus-dissolve-layer');
+        outgoingLayer.style.left = Math.round(outgoingRect.left) + 'px';
+        outgoingLayer.style.top = Math.round(outgoingRect.top) + 'px';
+        outgoingLayer.style.width = Math.round(outgoingRect.width) + 'px';
+        outgoingLayer.style.height = Math.max(1, Math.round(Math.min(outgoingRect.height, window.innerHeight - outgoingRect.top))) + 'px';
+        document.body.appendChild(outgoingLayer);
+        list.classList.add('is-dissolve-entering');
+        paradeLayer.classList.add('is-dissolving-out');
+      }
+
       stage.replaceChildren();
       stage.hidden = true;
       list.hidden = false;
@@ -617,7 +637,28 @@
       activeSource = null;
       activeClone = null;
       document.title = archiveTitle;
-      if (returnTarget) activateTarget(returnTarget, { animate: options.animate !== false });
+
+      return activateTarget(returnTarget, { animate: options.toStart ? options.animate !== false : false }).then(function () {
+        if (!useDissolve || !outgoingLayer) {
+          list.classList.remove('is-dissolve-entering');
+          paradeLayer.classList.remove('is-dissolving-out');
+          clearParadeLayer();
+          return;
+        }
+
+        return new Promise(function (resolve) {
+          window.requestAnimationFrame(function () {
+            outgoingLayer.classList.add('is-dissolving-out');
+            list.classList.remove('is-dissolve-entering');
+            window.setTimeout(function () {
+              outgoingLayer.remove();
+              paradeLayer.classList.remove('is-dissolving-out');
+              clearParadeLayer();
+              resolve();
+            }, viewFadeDuration);
+          });
+        });
+      });
     }
 
     function openItem(source, link) {
@@ -628,9 +669,9 @@
 
       activeSource = source;
       activeSource.setAttribute('aria-current', 'true');
-      activeClone = sanitizeClone(source.cloneNode(true));
+      listUrl = archiveUrl() + (source.id ? '#' + source.id : '');
+      activeClone = sanitizeClone(source.cloneNode(true), listUrl);
       activeClone.classList.add('is-picked');
-      listUrl = archiveUrl() + (source.id ? '#' + source.id : '');
       page.classList.add('is-focus-opening');
 
       activateTarget(source, { animate: true }).then(function () {
@@ -652,7 +693,7 @@
       if (close) {
         event.preventDefault();
         replaceLocation(listUrl, { hoPartial: true });
-        closeItem();
+        closeItem({ dissolve: true });
         return;
       }
       var link = event.target.closest('[data-focus-link]');
@@ -667,14 +708,15 @@
     window.addEventListener('resize', onResize, { passive: true });
 
     registerViewCleanup(listen(window, 'popstate', function () {
-      if (activeClone && !/^#focus-(event|work|credit)-[0-9]+$/.test(window.location.hash) && !new URL(window.location.href).searchParams.get('focus')) closeItem();
+      if (activeClone && !/^#focus-(event|work|credit)-[0-9]+$/.test(window.location.hash) && !new URL(window.location.href).searchParams.get('focus')) closeItem({ dissolve: true });
     }));
     registerViewCleanup(listen(document, 'ho:resetfocus', function (event) {
       if (!activeClone) return;
       replaceLocation(archiveUrl(), { hoPartial: true });
       closeItem({
         toStart: Boolean(event.detail && event.detail.toStart),
-        animate: !event.detail || event.detail.animate !== false
+        animate: !event.detail || event.detail.animate !== false,
+        dissolve: false
       });
     }));
     registerViewCleanup(function () {
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.14/docs/BETA1-CHANGELOG.md
@@ -1,4 +1,7 @@
-## 0.16.0-beta.13
+## 0.16.0-beta.14
+
+- 単一表示のタイトルクリックをBackと同じ復帰処理へ統合。
+- 一覧復帰をスクロール移動から0.86秒のクロスディゾルブへ変更。
 
 - 単一モジュールを開く前に、一覧上の元モジュールを実表示領域の上端へスクロールアニメーションで移動してから単一表示へ切り替える構造へ変更。
 - 同一ページで主メニューの Opus/Works/Schedule を押した際、各リストの先頭年見出しへスクロールアニメーションで戻る処理を追加。
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.14/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.13' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.14' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.14/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.13
+Version: 0.16.0-beta.14
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

共通基準線を使っただけで、実際のカード上端座標を一致させる処理がなかった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.14.zip


再利用すべき判断基準

  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 同じ役割の処理は一つの関数・一つの状態・一つのDOMに集約する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.13

当時の改修目的

単一モジュール選択時の上端移動アニメーションと、同一主メニュー再押下でリスト先頭へ移動する。

実装した変更

・単一モジュールを開く前に、一覧上の元モジュールを実表示領域の上端へスクロールアニメーションで移動してから単一表示へ切り替える構造へ変更。

・同一ページで主メニューの Opus/Works/Schedule を押した際、各リストの先頭年見出しへスクロールアニメーションで戻る処理を追加。

・単一表示中に同じ主メニューを押した場合は、一覧へ戻したうえでリスト先頭へ一度だけアニメーション移動するよう共通化。

・スクロール移動を一つのキャンセル可能な requestAnimationFrame ベースの処理へ統合し、距離に応じた時間と reduced-motion 対応を追加。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.14以降でも撤去されず残った仕組みは継続採用とみなす。

共通requestAnimationFrameスクロール、距離連動時間、既存移動のキャンセル、reduced-motion対応を実装。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.14 の要望・修正内容と照合して確定。

タイトルクリック/Backで一覧復帰する際に変な位置へ戻り、スクロール復帰の見え方も不自然。

次版で実際に求められたこと:単一表示のタイトル・Back・ブラウザ戻るを同じ復帰処理へ統一し、一覧復帰をディゾルブにする。

実際の変更箇所・実装内容

変更ファイル

変更: assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.13/assets/js/theme.js
@@ -8,6 +8,7 @@
   var lastLinkThanksAnimation = -1;
   var webAppQuery = window.matchMedia('(display-mode: standalone), (display-mode: fullscreen), (display-mode: minimal-ui)');
   var viewFadeDuration = 860;
+  var activeScrollFrame = 0;
 
 
   if ('scrollRestoration' in window.history) window.history.scrollRestoration = 'manual';
@@ -23,6 +24,7 @@
 
   function disposeView() {
     targetActivation += 1;
+    cancelScrollAnimation();
     if (root.getAttribute('data-current-view') === 'tetris' && window.HOTetrisApp) {
       window.HOTetrisApp.unmount();
     }
@@ -105,17 +107,67 @@
       : (target.querySelector('[data-context-anchor]') || target);
   }
 
-  function activateTarget(target) {
+  function cancelScrollAnimation() {
+    if (!activeScrollFrame) return;
+    window.cancelAnimationFrame(activeScrollFrame);
+    activeScrollFrame = 0;
+  }
+
+  function animateScroll(destination, options) {
+    options = options || {};
+    cancelScrollAnimation();
+    var start = window.scrollY;
+    var end = Math.max(0, Math.round(destination));
+    var distance = end - start;
+    var reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
+    var duration = options.animate === false || reducedMotion ? 0 : Math.min(680, Math.max(360, Math.abs(distance) * 0.32));
+
+    if (!duration || Math.abs(distance) < 2) {
+      window.scrollTo({ top: end, left: 0, behavior: 'auto' });
+      return Promise.resolve();
+    }
+
+    return new Promise(function (resolve) {
+      var started = performance.now();
+      function frame(now) {
+        var progress = Math.min(1, (now - started) / duration);
+        var eased = progress < 0.5
+          ? 4 * progress * progress * progress
+          : 1 - Math.pow(-2 * progress + 2, 3) / 2;
+        window.scrollTo({ top: start + distance * eased, left: 0, behavior: 'auto' });
+        if (progress < 1) {
+          activeScrollFrame = window.requestAnimationFrame(frame);
+          return;
+        }
+        activeScrollFrame = 0;
+        resolve();
+      }
+      activeScrollFrame = window.requestAnimationFrame(frame);
+    });
+  }
+
+  function activateTarget(target, options) {
     var activation = ++targetActivation;
     var anchor = contextAnchor(target);
-    if (!anchor) return;
+    if (!anchor) return Promise.resolve();
     syncVisibleMetrics();
-    window.requestAnimationFrame(function () {
-      if (activation !== targetActivation || !document.documentElement.contains(anchor)) return;
-      var bounds = visibleBounds();
-      var destination = window.scrollY + anchor.getBoundingClientRect().top - bounds.top - 8;
-      window.scrollTo({ top: Math.max(0, Math.round(destination)), left: 0, behavior: 'auto' });
-    });
+    return new Promise(function (resolve) {
+      window.requestAnimationFrame(function () {
+        if (activation !== targetActivation || !document.documentElement.contains(anchor)) {
+          resolve();
+          return;
+        }
+        var bounds = visibleBounds();
+        var destination = window.scrollY + anchor.getBoundingClientRect().top - bounds.top - 8;
+        animateScroll(destination, options).then(resolve);
+      });
+    });
+  }
+
+  function currentViewStartTarget(view) {
+    var scope = document.querySelector('[data-view-root="' + view + '"]');
+    if (!scope) return null;
+    return scope.querySelector('[data-context-anchor]') || scope.querySelector('[data-focus-list]') || scope;
   }
 
   function activateLocationTarget() {
@@ -124,7 +176,7 @@
       window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
       return;
     }
-    activateTarget(document.getElementById(hash));
+    activateTarget(document.getElementById(hash), { animate: false });
   }
 
   function contextSlot() {
@@ -357,7 +409,7 @@
         event.preventDefault();
         select(key, 'center');
         replaceLocation(link.href, window.history.state);
-        activateTarget(target);
+        activateTarget(target, { animate: true });
       });
     });
 
@@ -389,7 +441,7 @@
       var target = targetForLocation();
       if (!target) return;
       select(target.getAttribute('data-context-key'), 'center');
-      activateTarget(target);
+      activateTarget(target, { animate: true });
     }));
   }
 
@@ -552,19 +604,20 @@
       });
     }
 
-    function closeItem() {
+    function closeItem(options) {
       if (!activeClone) return;
-      var returnTarget = activeSource;
+      options = options || {};
+      var returnTarget = options.toStart ? currentViewStartTarget(page.getAttribute('data-view-root')) : activeSource;
       clearParadeLayer();
       stage.replaceChildren();
       stage.hidden = true;
       list.hidden = false;
-      page.classList.remove('is-focus');
+      page.classList.remove('is-focus', 'is-focus-opening');
       if (activeSource) activeSource.removeAttribute('aria-current');
       activeSource = null;
       activeClone = null;
       document.title = archiveTitle;
-      if (returnTarget) activateTarget(returnTarget);
+      if (returnTarget) activateTarget(returnTarget, { animate: options.animate !== false });
     }
 
     function openItem(source, link) {
@@ -578,15 +631,20 @@
       activeClone = sanitizeClone(source.cloneNode(true));
       activeClone.classList.add('is-picked');
       listUrl = archiveUrl() + (source.id ? '#' + source.id : '');
-
-      stage.replaceChildren(activeClone);
-      stage.hidden = false;
-      list.hidden = true;
-      page.classList.add('is-focus');
-      replaceLocation(archiveUrl() + '#focus-' + type + '-' + itemId, { hoPartial: true, hoListFocus: true });
-      document.title = link.textContent.trim() + ' – ' + archiveTitle;
-      activateTarget(stage);
-      loadDetails(activeClone, type, itemId);
+      page.classList.add('is-focus-opening');
+
+      activateTarget(source, { animate: true }).then(function () {
+        if (activeSource !== source || !activeClone) return;
+        stage.replaceChildren(activeClone);
+        stage.hidden = false;
+        list.hidden = true;
+        page.classList.remove('is-focus-opening');
+        page.classList.add('is-focus');
+        replaceLocation(archiveUrl() + '#focus-' + type + '-' + itemId, { hoPartial: true, hoListFocus: true });
+        document.title = link.textContent.trim() + ' – ' + archiveTitle;
+        activateTarget(stage, { animate: false });
+        loadDetails(activeClone, type, itemId);
+      });
     }
 
     var onFocusClick = function (event) {
@@ -611,10 +669,13 @@
     registerViewCleanup(listen(window, 'popstate', function () {
       if (activeClone && !/^#focus-(event|work|credit)-[0-9]+$/.test(window.location.hash) && !new URL(window.location.href).searchParams.get('focus')) closeItem();
     }));
-    registerViewCleanup(listen(document, 'ho:resetfocus', function () {
+    registerViewCleanup(listen(document, 'ho:resetfocus', function (event) {
       if (!activeClone) return;
       replaceLocation(archiveUrl(), { hoPartial: true });
-      closeItem();
+      closeItem({
+        toStart: Boolean(event.detail && event.detail.toStart),
+        animate: !event.detail || event.detail.animate !== false
+      });
     }));
     registerViewCleanup(function () {
       page.removeEventListener('click', onFocusClick);
@@ -887,8 +948,13 @@
       if (!targetView) return;
       if (targetView === currentView()) {
         event.preventDefault();
-        if (document.querySelector('.focus-page.is-focus') && ['opus', 'works', 'schedule'].indexOf(targetView) !== -1) {
-          document.dispatchEvent(new CustomEvent('ho:resetfocus'));
+        if (['opus', 'works', 'schedule'].indexOf(targetView) !== -1) {
+          if (document.querySelector('.focus-page.is-focus')) {
+            document.dispatchEvent(new CustomEvent('ho:resetfocus', { detail: { toStart: true, animate: true } }));
+          } else {
+            var startTarget = currentViewStartTarget(targetView);
+            if (startTarget) activateTarget(startTarget, { animate: true });
+          }
           return;
         }
         if (targetView === 'calendar' && targetUrl.href !== window.location.href) {
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.13/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.16.0-beta.13
+
+- 単一モジュールを開く前に、一覧上の元モジュールを実表示領域の上端へスクロールアニメーションで移動してから単一表示へ切り替える構造へ変更。
+- 同一ページで主メニューの Opus/Works/Schedule を押した際、各リストの先頭年見出しへスクロールアニメーションで戻る処理を追加。
+- 単一表示中に同じ主メニューを押した場合は、一覧へ戻したうえでリスト先頭へ一度だけアニメーション移動するよう共通化。
+- スクロール移動を一つのキャンセル可能な requestAnimationFrame ベースの処理へ統合し、距離に応じた時間と reduced-motion 対応を追加。
+
 ## 0.16.0-beta.12
 
 - 単一モジュールから一覧へ戻る際、該当モジュールを実際の表示領域上端へ配置。旧スクロール位置の保存・復元処理を削除。
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.13/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.12' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.13' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.13/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.12
+Version: 0.16.0-beta.13
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

一覧復帰にも移動アニメーションを使い、タイトルリンクとBackの復帰経路が統一されていなかった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.13.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.12

当時の改修目的

年号ボタンの初期左寄せと選択後中央化、フォーカス復帰位置を回復する。

実装した変更

・単一モジュールから一覧へ戻る際、該当モジュールを実際の表示領域上端へ配置。旧スクロール位置の保存・復元処理を削除。

・年号を明示的に選択した際、選択ボタンをページ専用メニュー中央へ移動。初期表示は従来どおり最新年を左端基準に配置。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.13以降でも撤去されず残った仕組みは継続採用とみなす。

theme.jsで失われた操作挙動を復元。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.13 の要望・修正内容と照合して確定。

単一モジュールを開く際の移動アニメーション、同一主メニュー再押下の先頭移動が不足。

次版で実際に求められたこと:単一モジュール選択時の上端移動アニメーションと、同一主メニュー再押下でリスト先頭へ移動する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.12/assets/js/theme.js
@@ -190,7 +190,7 @@
     message.setAttribute('data-link-thanks-animation', animations[choice]);
   }
 
-  function alignContextNavigation(nav) {
+  function alignContextNavigation(nav, placement) {
     var current = nav && nav.querySelector('[aria-current="page"]');
     if (!current) return;
     var strip = nav.querySelector('.context-nav__years') || nav;
@@ -209,8 +209,18 @@
 
     var stripRect = strip.getBoundingClientRect();
     var currentRect = current.getBoundingClientRect();
-    var paddingLeft = parseFloat(window.getComputedStyle(strip).paddingLeft) || 0;
-    strip.scrollLeft = Math.max(0, strip.scrollLeft + currentRect.left - stripRect.left - paddingLeft);
+    var targetLeft;
+    if (placement === 'center') {
+      targetLeft = strip.scrollLeft
+        + currentRect.left
+        - stripRect.left
+        - (strip.clientWidth - currentRect.width) / 2;
+    } else {
+      var paddingLeft = parseFloat(window.getComputedStyle(strip).paddingLeft) || 0;
+      targetLeft = strip.scrollLeft + currentRect.left - stripRect.left - paddingLeft;
+    }
+    var maximum = Math.max(0, strip.scrollWidth - strip.clientWidth);
+    strip.scrollLeft = Math.max(0, Math.min(maximum, targetLeft));
   }
 
   function calendarDateFromRoot(calendarRoot) {
@@ -253,7 +263,7 @@
         event.preventDefault();
         links.forEach(function (item) { item.removeAttribute('aria-current'); });
         link.setAttribute('aria-current', 'page');
-        alignContextNavigation(nav);
+        alignContextNavigation(nav, 'center');
       };
       link.addEventListener('click', onClick);
       registerViewCleanup(function () { link.removeEventListener('click', onClick); });
@@ -313,18 +323,20 @@
       return all.concat(Array.from(nav.querySelectorAll('[data-context-key]')));
     }, []);
 
-    function select(key) {
+    function select(key, placement) {
       links.forEach(function (link) {
         if (link.getAttribute('data-context-key') === key) link.setAttribute('aria-current', 'page');
         else link.removeAttribute('aria-current');
       });
-      navs.forEach(alignContextNavigation);
+      if (placement) {
+        navs.forEach(function (nav) { alignContextNavigation(nav, placement); });
+      }
     }
 
     if (!sections.length) {
       if (root.getAttribute('data-current-view') === 'calendar') {
         var requestedYear = new URL(window.location.href).searchParams.get('calendar_year');
-        if (requestedYear) select(requestedYear);
+        if (requestedYear) select(requestedYear, 'start');
         navs.forEach(function (nav) {
           setupCalendarYearNavigation(nav, Array.from(nav.querySelectorAll('[data-context-key]')));
         });
@@ -343,7 +355,7 @@
         var target = sections.find(function (section) { return section.getAttribute('data-context-key') === key; });
         if (!target) return;
         event.preventDefault();
-        select(key);
+        select(key, 'center');
         replaceLocation(link.href, window.history.state);
         activateTarget(target);
       });
@@ -376,7 +388,7 @@
       if (document.querySelector('.focus-page.is-focus')) return;
       var target = targetForLocation();
       if (!target) return;
-      select(target.getAttribute('data-context-key'));
+      select(target.getAttribute('data-context-key'), 'center');
       activateTarget(target);
     }));
   }
@@ -464,7 +476,6 @@
     var activeClone = null;
     var listUrl = archiveUrl();
     var archiveTitle = document.title;
-    var savedListScroll = 0;
 
     function clearParadeLayer() {
       resetEventSkullParades(paradeLayer);
@@ -543,6 +554,7 @@
 
     function closeItem() {
       if (!activeClone) return;
+      var returnTarget = activeSource;
       clearParadeLayer();
       stage.replaceChildren();
       stage.hidden = true;
@@ -552,10 +564,10 @@
       activeSource = null;
       activeClone = null;
       document.title = archiveTitle;
-      window.scrollTo({ top: savedListScroll, left: 0, behavior: 'auto' });
-    }
-
-    function openItem(source, link, initial) {
+      if (returnTarget) activateTarget(returnTarget);
+    }
+
+    function openItem(source, link) {
       if (!source || !link || activeClone) return;
       var type = source.getAttribute('data-focus-type');
       var itemId = source.getAttribute('data-focus-id');
@@ -565,7 +577,6 @@
       activeSource.setAttribute('aria-current', 'true');
       activeClone = sanitizeClone(source.cloneNode(true));
       activeClone.classList.add('is-picked');
-      savedListScroll = initial ? 0 : window.scrollY;
       listUrl = archiveUrl() + (source.id ? '#' + source.id : '');
 
       stage.replaceChildren(activeClone);
@@ -590,7 +601,7 @@
       var item = link ? link.closest('[data-focus-item]') : null;
       if (!link || !item || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
       event.preventDefault();
-      openItem(item, link, false);
+      openItem(item, link);
     };
     page.addEventListener('click', onFocusClick);
 
@@ -632,7 +643,7 @@
     var initialItem = initialType && initialId ? page.querySelector('[data-focus-item][data-focus-type="' + initialType.replace(/[^a-z]/g, '') + '"][data-focus-id="' + initialId.replace(/[^0-9]/g, '') + '"]') : null;
     page.removeAttribute('data-initial-focus-type');
     page.removeAttribute('data-initial-focus-id');
-    if (initialItem) openItem(initialItem, initialItem.querySelector('[data-focus-link]'), true);
+    if (initialItem) openItem(initialItem, initialItem.querySelector('[data-focus-link]'));
   }
 
   function initializeView(options) {
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.12/docs/BETA1-CHANGELOG.md
@@ -1,4 +1,7 @@
-## 0.16.0-beta.11
+## 0.16.0-beta.12
+
+- 単一モジュールから一覧へ戻る際、該当モジュールを実際の表示領域上端へ配置。旧スクロール位置の保存・復元処理を削除。
+- 年号を明示的に選択した際、選択ボタンをページ専用メニュー中央へ移動。初期表示は従来どおり最新年を左端基準に配置。
 
 - Removed the obsolete `.site-main` viewport-height minimum that duplicated the shared visible-area calculation and caused vertical page scrolling.
 - Unified viewport pages so Profile and Calendar stay inside the measured header/footer boundaries without document scrolling.
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.12/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.11' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.12' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.12/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.11
+Version: 0.16.0-beta.12
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

復帰と年号挙動を優先し、入口側のスクロール演出を含めなかった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.12.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.11

当時の改修目的

beta.10後のCSS上の表示差・余白・フッターを補正する。

実装した変更

・Removed the obsolete `.site-main` viewport-height minimum that duplicated the shared visible-area calculation and caused vertical page scrolling.

・Unified viewport pages so Profile and Calendar stay inside the measured header/footer boundaries without document scrolling.

・Added balanced Profile clearance while preserving scrolling only inside the language text panel.

・Gave the mobile context controls real vertical shadow clearance instead of clipping their shadows at the scroller boundary.

・Made the calendar root fill its viewport container and hide document-level overflow.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.12以降でも撤去されず残った仕組みは継続採用とみなす。

CSS中心の限定修正。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.12 の要望・修正内容と照合して確定。

機能面の年号・focus復帰は未対応。

次版で実際に求められたこと:年号ボタンの初期左寄せと選択後中央化、フォーカス復帰位置を回復する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.11/assets/css/theme.css
@@ -178,7 +178,6 @@
 @keyframes link-thanks-cascade { 0%,62%,100%{opacity:1;transform:translateY(0)} 72%{opacity:.72;transform:translateY(3px)} 84%{opacity:1;transform:translateY(-3px)} 92%{transform:translateY(0)} }
 @keyframes link-thanks-heartbeat { 0%,64%,100%{transform:scale(1)} 70%{transform:scale(1.035)} 76%{transform:scale(.995)} 82%{transform:scale(1.055)} 90%{transform:scale(1)} }
 
-.site-main { min-height:calc(100vh - var(--footer-height)); }
 html[data-view-transitioning="true"] .site-main,html[data-view-transitioning="true"] .context-nav { will-change:opacity; }
 .ho-js[data-current-view="home"]:not(.is-view-ready) .site-main,
 .ho-js[data-current-view="home"]:not(.is-view-ready) [data-context-slot] > * { opacity:0; }
@@ -318,7 +317,7 @@
 .profile-page__image figcaption { background:rgba(35,40,49,.58); border-radius:500px; bottom:10px; color:#fff; font-size:10px; padding:3px 9px; position:absolute; right:12px; }
 
 @media (min-width:768px) and (min-height:650px) {
-  .profile-page { padding-bottom:0; }
+  .profile-page { padding-bottom:var(--content-top); }
   .profile-page__layout { grid-template-rows:repeat(2,minmax(0,1fr)); height:100%; }
   .profile-page__image { height:auto; min-height:0; }
   .profile-page .language-tabs { min-height:0; }
@@ -534,7 +533,7 @@
     grid-template-columns:auto minmax(0,1fr) auto;
     height:auto;
     min-height:46px;
-    overflow:hidden;
+    overflow:visible;
     padding:6px 8px;
     position:relative;
   }
@@ -554,11 +553,11 @@
   .context-nav__section-link { align-self:center; position:relative; z-index:2; }
   .context-nav__years {
     gap:8px;
-    height:var(--context-control-height);
+    height:calc(var(--context-control-height) + 12px);
     justify-content:flex-start;
     mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%);
     min-width:0;
-    padding:0 4px;
+    padding:6px 4px;
     -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%);
   }
   .context-nav__years :is(a,button)[aria-current="page"] { background:var(--context-accent); color:#fff; }
@@ -645,9 +644,11 @@
   width:100%;
 }
 .calendar-page--jaikki { padding:var(--content-top) var(--content-inline) 12px; }
-.calendar-page--jaikki .jaikki-calendar-root { margin:0 auto; max-width:1180px; }
+.calendar-page--jaikki .jaikki-calendar-root { height:100%; margin:0 auto; max-width:1180px; min-height:0; }
 .calendar-page--jaikki .jaikki-calendar {
   background:var(--surface);
+  height:100%;
+  min-height:0;
   border:1px solid var(--neu-border);
   box-shadow:var(--neu-raised);
 }
@@ -663,12 +664,12 @@
 @media (max-width: 767px) {
   .calendar-page--jaikki {
     background:var(--surface);
-    overflow:visible;
-    padding:12px 14px;
+    overflow:hidden;
+    padding:10px 14px;
     width:100%;
   }
   .calendar-page--jaikki .jaikki-calendar-root { min-height:100%; width:100%; }
-  .profile-page { padding-bottom:0; }
+  .profile-page { padding-bottom:10px; padding-top:10px; }
   .profile-page__layout { gap:8px; grid-template-rows:minmax(0,1fr) minmax(0,1fr); height:100%; }
   .profile-page__image { border-radius:20px; height:auto; min-height:0; }
   .profile-page__image img { object-position:center 32%; }
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.11/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.16.0-beta.11
+
+- Removed the obsolete `.site-main` viewport-height minimum that duplicated the shared visible-area calculation and caused vertical page scrolling.
+- Unified viewport pages so Profile and Calendar stay inside the measured header/footer boundaries without document scrolling.
+- Added balanced Profile clearance while preserving scrolling only inside the language text panel.
+- Gave the mobile context controls real vertical shadow clearance instead of clipping their shadows at the scroller boundary.
+- Made the calendar root fill its viewport container and hide document-level overflow.
+
 ## 0.16.0-beta.10
 
 - Tetrisを通常の共通viewport-pageフローへ統合し、fixed座標配置を撤去。
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.11/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.10' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.11' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.11/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.10
+Version: 0.16.0-beta.11
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

機能JSではなく見た目の調整に限定した版だった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.11.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.10

当時の改修目的

テーマ全体を監査し、Tetrisライフサイクル、viewport、focus、header/footerの残骸を整理する。

実装した変更

・Tetrisを通常の共通viewport-pageフローへ統合し、fixed座標配置を撤去。

・Tetrisの初期化・破棄をSPAのビューライフサイクルへ統合。

・ページ専用メニューを単一DOMへ統合し、PC/スマホ間で同じ要素を配置。

・Profile/Calendar/Tetrisの画面内ページ構造をviewport-pageへ統一。

・未使用の固定高さ・旧色・旧影変数を削除。

・キーボード操作時のフォーカス表示を復元。

・年号アンカー移動の二重requestAnimationFrameを撤去。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.11以降でも撤去されず残った仕組みは継続採用とみなす。

Tetris mount/unmount、view-shell、template、JS/CSS/PHPを広範囲に再構成。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.11 の要望・修正内容と照合して確定。

一部ページ位置、年ボタン中央化、focus復帰挙動などが後続で再調整となった。

次版で実際に求められたこと:beta.10後のCSS上の表示差・余白・フッターを補正する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/tetris.css, assets/css/theme.css, assets/js/tetris.js, assets/js/theme.js, docs/BETA1-CHANGELOG.md, footer.php, functions.php, header.php, inc/view-shell.php, style.css, template-parts/view-tetris.php

changedassets/css/tetris.css
--- previous/assets/css/tetris.css
+++ 0.16.0-beta.10/assets/css/tetris.css
@@ -1,29 +1,39 @@
-/* ====== THEME CSS ISOLATION (critical) ====== */
-    #tetris-doodle-shell{ font-size:16px; line-height:1; }
-    #tetris-doodle-shell, #tetris-doodle-shell *{ box-sizing:border-box; }
-
-    @supports (all: revert){
-      #tetris-doodle-shell{ all: revert; display:block; }
-      #tetris-doodle-shell *{ all: revert; }
-      #tetris-doodle-shell, #tetris-doodle-shell *{ box-sizing:border-box; }
-    }
-
-    #tetris-doodle-shell{
-      margin:0 !important; padding:0 !important; border:0 !important;
-      background: transparent !important;
-      height:100%;
-      max-width: 100%;
-      width:100%;
-    }
-
-    /* theme max-width reset */
-    #tetris-doodle-shell canvas{ max-width:none !important; height:auto; display:block; }
-    #tetris-doodle-shell button{
-      appearance:none; -webkit-appearance:none;
-      border:none; background:none; padding:0; margin:0;
-      font:inherit; color:inherit; line-height:1;
-      cursor:pointer;
-    }
+/* Tetris component boundary. Theme-level placement lives in theme.css. */
+#tetris-doodle-shell,
+#tetris-doodle-shell * {
+  box-sizing: border-box;
+}
+
+#tetris-doodle-shell {
+  background: transparent;
+  border: 0;
+  display: block;
+  font-size: 16px;
+  height: 100%;
+  line-height: 1;
+  margin: 0;
+  max-width: 100%;
+  padding: 0;
+  width: 100%;
+}
+
+#tetris-doodle-shell canvas {
+  display: block;
+  height: auto;
+  max-width: none;
+}
+
+#tetris-doodle-shell button {
+  appearance: none;
+  background: none;
+  border: 0;
+  color: inherit;
+  cursor: pointer;
+  font: inherit;
+  line-height: 1;
+  margin: 0;
+  padding: 0;
+}
 
     /* ====== GAME CSS (fully scoped) ====== */
     #tetris-doodle-root{
changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.10/assets/css/theme.css
@@ -9,18 +9,15 @@
   --teal: #008080;
   --olive: #808000;
   --pink: #e84dd8;
-  --cyan: #22b7de;
   --ink: #202124;
-  --muted: #868686;
   --page: 1280px;
   --brand-height: 120px;
-  --ticker-height: 35px;
-  --primary-nav-height: 58px;
   --context-height: 52px;
-  --mobile-context-height: 56px;
   --content-top: 12px;
   --content-inline: 24px;
-  --content-bottom: calc(100dvh - var(--visible-bottom) + 26px);
+  --bottom-ui-space: 0px;
+  --content-end-gap: 26px;
+  --content-bottom: calc(var(--bottom-ui-space) + var(--content-end-gap));
   --module-gap: 28px;
   --module-section-gap: 34px;
   --module-radius: 24px;
@@ -30,21 +27,17 @@
   --footer-height: 54px;
   --mobile-nav-height: 48px;
   --mobile-credit-height: 16px;
-  --mobile-dock-height: 64px;
-  --view-fade-duration: .86s;
   --visible-top: 0px;
   --visible-bottom: 100dvh;
   --visible-height: 100dvh;
   --skull-parade-max-width: calc(var(--record-shell) - (var(--record-shell-inline) * 2) - (var(--module-padding) * 2));
   --surface: #edf3f8;
-  --surface-soft: #f5f8fb;
   --neu-dark: rgba(143, 158, 184, .4);
   --neu-light: rgba(255, 255, 255, .96);
   --neu-border: rgba(139, 153, 175, .34);
   --neu-raised: 8px 9px 20px rgba(143, 158, 184, .36), -7px -7px 17px var(--neu-light);
   --neu-small: 4px 5px 10px var(--neu-dark), -4px -4px 10px var(--neu-light);
   --neu-inset: inset 3px 3px 7px rgba(143, 158, 184, .4), inset -3px -3px 7px rgba(255, 255, 255, .92);
-  --shadow: var(--neu-small);
 }
 
 * { box-sizing: border-box; }
@@ -59,27 +52,14 @@
   overflow-x: clip;
   padding-bottom:0;
 }
-html[data-current-view="tetris"],
-html[data-current-view="tetris"] body {
-  overscroll-behavior:none;
-}
-html[data-current-view="tetris"] .site-top-stack {
-  box-shadow:none;
-}
-html[data-current-view="tetris"] .tetris-page {
-  height:var(--visible-height);
-  left:0;
-  overflow:hidden;
-  position:fixed;
-  right:0;
-  top:var(--visible-top);
-  touch-action:none;
-}
 a { color: inherit; }
-a:focus,
+a:focus:not(:focus-visible),
+button:focus:not(:focus-visible) { outline:none; }
 a:focus-visible,
-button:focus,
-button:focus-visible { outline:none; }
+button:focus-visible {
+  outline:2px solid currentColor;
+  outline-offset:3px;
+}
 img { height: auto; max-width: 100%; }
 button, input, textarea { font: inherit; }
 .skip-link { background:#fff; left:8px; padding:8px 12px; position:fixed; top:-80px; z-index:100000; }
@@ -97,6 +77,7 @@
 .site-header { background:var(--surface); }
 .site-main { min-height:1px; }
 .context-slot { height:var(--context-height); overflow:visible; }
+.context-slot-host { display:none; }
 .context-slot:empty { height:0; }
 html[data-has-context="false"] { --context-height:0px; }
 .brand { background:var(--surface); height:var(--brand-height); position:relative; }
@@ -202,6 +183,19 @@
 .ho-js[data-current-view="home"]:not(.is-view-ready) .site-main,
 .ho-js[data-current-view="home"]:not(.is-view-ready) [data-context-slot] > * { opacity:0; }
 .content-shell { margin:0 auto; max-width:var(--page); padding:var(--content-top) var(--content-inline) var(--content-bottom); }
+.viewport-page {
+  box-sizing:border-box;
+  height:var(--visible-height);
+  min-height:0;
+}
+.tetris-page {
+  margin:0;
+  overflow:hidden;
+  padding:0;
+  touch-action:none;
+  width:100%;
+}
+
 .page-title { font-size:32px; margin:0 0 22px; }
 .page-title--profile { color:var(--blue); }
 .page-title--opus { color:var(--green); }
@@ -324,7 +318,7 @@
 .profile-page__image figcaption { background:rgba(35,40,49,.58); border-radius:500px; bottom:10px; color:#fff; font-size:10px; padding:3px 9px; position:absolute; right:12px; }
 
 @media (min-width:768px) and (min-height:650px) {
-  .profile-page { box-sizing:border-box; height:var(--visible-height); min-height:0; padding-bottom:0; }
+  .profile-page { padding-bottom:0; }
   .profile-page__layout { grid-template-rows:repeat(2,minmax(0,1fr)); height:100%; }
   .profile-page__image { height:auto; min-height:0; }
   .profile-page .language-tabs { min-height:0; }
@@ -402,12 +396,12 @@
 .release-card img { aspect-ratio:1; border-radius:18px; box-shadow:var(--neu-inset); display:block; object-fit:cover; width:100%; }
 .release-card strong { display:block; padding:9px; }
 
-.calendar-page { box-sizing:border-box; height:var(--visible-height); max-width:1300px; min-height:0; }
+.calendar-page { max-width:1300px; }
 
 .site-footer { align-items:center; background:rgba(119,128,143,.86); border-top:1px solid rgba(255,255,255,.55); box-shadow:0 -6px 15px rgba(163,177,198,.38); bottom:0; color:#fff; display:flex; font-size:14px; font-weight:600; height:var(--footer-height); justify-content:center; left:0; position:fixed; right:0; z-index:9991; }
-.mobile-footer-stack,.mobile-dock,.context-slot--mobile { display:none; }
+.mobile-footer-stack,.mobile-dock { display:none; }
 .mobile-dock__credit { align-items:center; background:rgba(119,128,143,.94); color:#fff; display:flex; font-size:9px; font-weight:600; height:var(--mobile-credit-height); justify-content:center; line-height:1; }
-:root.is-web-app { --mobile-nav-height:58px; --mobile-credit-height:18px; --mobile-dock-height:calc(76px + env(safe-area-inset-bottom, 0px)); }
+:root.is-web-app { --mobile-nav-height:58px; --mobile-credit-height:18px; }
 :root.is-web-app .mobile-dock__credit { display:flex; }
 
 @media (min-width: 768px) and (max-width: 1100px) {
@@ -488,7 +482,7 @@
 }
 
 @media (max-width: 767px) {
-  :root { --content-top:4px; --content-inline:14px; --content-bottom:calc(100dvh - var(--visible-bottom) + 6px); --module-gap:22px; --module-section-gap:28px; --module-padding:14px; --module-radius:20px; --record-shell-inline:14px; }
+  :root { --content-top:4px; --content-inline:14px; --content-end-gap:6px; --module-gap:22px; --module-section-gap:28px; --module-padding:14px; --module-radius:20px; --record-shell-inline:14px; }
   .admin-bar .site-top-stack { top:46px; }
   .brand { height:79px; }
   .brand::after { top:52px; }
@@ -524,10 +518,10 @@
   .release-card { font-size:14px; }
   .calendar-page { min-width:0; overflow-x:visible; padding-left:5px; padding-right:5px; width:100%; }
   .site-footer { display:none; }
-  .context-slot--desktop { display:none; }
   .mobile-footer-stack { bottom:0; display:flex; flex-direction:column; left:0; position:fixed; right:0; z-index:9999; }
-  .context-slot--mobile { display:block; flex:0 0 auto; height:auto; position:static; }
-  .context-slot--mobile:empty { display:none; }
+  .context-slot-host--mobile { display:block; flex:0 0 auto; }
+  .context-slot-host--mobile .context-slot { height:auto; position:static; }
+  .context-slot-host--mobile .context-slot:empty { display:none; }
   .mobile-dock { display:block; flex:0 0 auto; position:static; }
   :root.is-web-app .mobile-footer-stack { background:rgba(119,128,143,.94); padding-bottom:env(safe-area-inset-bottom, 0px); }
   .context-nav {
@@ -669,20 +663,12 @@
 @media (max-width: 767px) {
   .calendar-page--jaikki {
     background:var(--surface);
-    box-sizing:border-box;
-    height:var(--visible-height);
-    left:0;
-    min-height:0;
     overflow:visible;
     padding:12px 14px;
-    position:fixed;
-    right:0;
-    top:var(--visible-top);
     width:100%;
-    z-index:1;
   }
   .calendar-page--jaikki .jaikki-calendar-root { min-height:100%; width:100%; }
-  .profile-page { height:var(--visible-height); min-height:0; padding-bottom:0; }
+  .profile-page { padding-bottom:0; }
   .profile-page__layout { gap:8px; grid-template-rows:minmax(0,1fr) minmax(0,1fr); height:100%; }
   .profile-page__image { border-radius:20px; height:auto; min-height:0; }
   .profile-page__image img { object-position:center 32%; }
changedassets/js/tetris.js
--- previous/assets/js/tetris.js
+++ 0.16.0-beta.10/assets/js/tetris.js
@@ -1,7 +1,12 @@
 (function(){
-    // 既に初期化済みなら二重起動しない
-    if (window.__tetrisDoodleSkullInited) return;
-    window.__tetrisDoodleSkullInited = true;
+  'use strict';
+
+  var activeCleanup = null;
+
+  function mount(){
+    var shell = document.getElementById('tetris-doodle-shell');
+    if (!shell || shell.getAttribute('data-mounted') === 'true') return;
+    shell.setAttribute('data-mounted', 'true');
 
     const COLS=10, ROWS=20, LINES_PER_LEVEL=10;
     const LOCK_DELAY_MS=520, LOCK_RESET_LIMIT=18;
@@ -15,6 +20,7 @@
     // DOM
     const boardCanvas = document.getElementById('td-board');
     const nextCanvas  = document.getElementById('td-next');
+    if (!boardCanvas || !nextCanvas) { shell.removeAttribute('data-mounted'); return; }
     const bctx = boardCanvas.getContext('2d');
     const nctx = nextCanvas.getContext('2d');
 
@@ -75,7 +81,8 @@
       });
     }
     window.addEventListener('resize',scheduleCanvasFit);
-    if ('ResizeObserver' in window) new ResizeObserver(scheduleCanvasFit).observe(canvWrap);
+    const canvasResizeObserver = 'ResizeObserver' in window ? new ResizeObserver(scheduleCanvasFit) : null;
+    if (canvasResizeObserver) canvasResizeObserver.observe(canvWrap);
 
     // pieces
     const PIECES = {
@@ -643,15 +650,17 @@
       setRunning(true);
     }
 
-    btnStart.addEventListener('click', restart);
-    btnPause.addEventListener('click', ()=>{
+    const onStartClick = restart;
+    const onPauseClick = ()=>{
       if(!state.running || state.clearAnim) return;
       state.paused=!state.paused;
       elStatus.textContent=state.paused?'PAUSED':'PLAYING';
-    });
+    };
+    btnStart.addEventListener('click', onStartClick);
+    btnPause.addEventListener('click', onPauseClick);
 
     // keyboard
-    window.addEventListener('keydown', (e)=>{
+    const onKeyDown = (e)=>{
       if(e.repeat) return;
       const target=e.target;
       if(target && (target.matches?.('input,textarea,select') || target.isContentEditable)) return;
@@ -671,17 +680,18 @@
       else if(e.key==='ArrowUp'){ e.preventDefault(); tryRotate(); }
       else if(e.key==='ArrowDown'){ e.preventDefault(); hardDrop(); }
       else if(k===' '){ e.preventDefault(); hardDrop(); }
-    }, {passive:false});
+    };
+    window.addEventListener('keydown', onKeyDown, {passive:false});
 
     // Mobile gestures: tap to rotate, swipe sideways to move, swipe down to drop.
     let gestureStart=null;
-    boardCanvas.addEventListener('pointerdown',(e)=>{
+    const onPointerDown=(e)=>{
       if(!state.running||state.paused||state.gameOver||state.clearAnim) return;
       e.preventDefault();
       gestureStart={x:e.clientX,y:e.clientY};
       boardCanvas.setPointerCapture?.(e.pointerId);
-    },{passive:false});
-    boardCanvas.addEventListener('pointerup',(e)=>{
+    };
+    const onPointerUp=(e)=>{
       if(!gestureStart||!state.running||state.paused||state.gameOver||state.clearAnim) return;
       e.preventDefault();
       const dx=e.clientX-gestureStart.x, dy=e.clientY-gestureStart.y;
@@ -689,9 +699,13 @@
       if(Math.abs(dx)>28&&Math.abs(dx)>Math.abs(dy)){ tryMove(dx>0?1:-1,0); }
       else if(dy>28){ hardDrop(); }
       else if(Math.abs(dx)<18&&Math.abs(dy)<18){ tryRotate(); }
-    },{passive:false});
-    boardCanvas.addEventListener('pointercancel',()=>{ gestureStart=null; });
-    gameRoot.addEventListener('touchmove',(e)=>{ e.preventDefault(); },{passive:false});
+    };
+    const onPointerCancel=()=>{ gestureStart=null; };
+    const onTouchMove=(e)=>{ e.preventDefault(); };
+    boardCanvas.addEventListener('pointerdown',onPointerDown,{passive:false});
+    boardCanvas.addEventListener('pointerup',onPointerUp,{passive:false});
+    boardCanvas.addEventListener('pointercancel',onPointerCancel);
+    gameRoot.addEventListener('touchmove',onTouchMove,{passive:false});
 
     // init
     fitCanvases();
@@ -699,4 +713,29 @@
     ensureQueue(); spawn();
     drawBoard(); drawNext();
     elStatus.textContent='READY';
-  })();
+
+    activeCleanup = function(){
+      state.running=false;
+      if(tickFrame){ cancelAnimationFrame(tickFrame); tickFrame=0; }
+      if(fitFrame){ cancelAnimationFrame(fitFrame); fitFrame=0; }
+      window.removeEventListener('resize',scheduleCanvasFit);
+      window.removeEventListener('keydown',onKeyDown);
+      if(canvasResizeObserver) canvasResizeObserver.disconnect();
+      btnStart.removeEventListener('click',onStartClick);
+      btnPause.removeEventListener('click',onPauseClick);
+      boardCanvas.removeEventListener('pointerdown',onPointerDown);
+      boardCanvas.removeEventListener('pointerup',onPointerUp);
+      boardCanvas.removeEventListener('pointercancel',onPointerCancel);
+      gameRoot.removeEventListener('touchmove',onTouchMove);
+      shell.removeAttribute('data-mounted');
+      activeCleanup=null;
+    };
+  }
+
+  function unmount(){
+    if(activeCleanup) activeCleanup();
+  }
+
+  window.HOTetrisApp={ mount:mount, unmount:unmount };
+  mount();
+}());
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.10/assets/js/theme.js
@@ -23,6 +23,9 @@
 
   function disposeView() {
     targetActivation += 1;
+    if (root.getAttribute('data-current-view') === 'tetris' && window.HOTetrisApp) {
+      window.HOTetrisApp.unmount();
+    }
     viewCleanups.splice(0).forEach(function (cleanup) { cleanup(); });
   }
 
@@ -48,11 +51,10 @@
   }
 
   function visibleBounds() {
-    var stack = document.getElementById('site-top-stack');
-    var top = isRendered(stack) ? Math.max(0, stack.getBoundingClientRect().bottom) : 0;
+    var topBoundary = Array.from(document.querySelectorAll('[data-viewport-boundary="top"]')).find(isRendered);
+    var top = topBoundary ? Math.max(0, topBoundary.getBoundingClientRect().bottom) : 0;
     var bottom = window.innerHeight;
-    var candidates = [document.getElementById('smf-mobile-footer-stack'), document.querySelector('.site-footer')];
-    candidates.forEach(function (element) {
+    document.querySelectorAll('[data-viewport-boundary="bottom"]').forEach(function (element) {
       if (!isRendered(element)) return;
       var rect = element.getBoundingClientRect();
       if (rect.top > top && rect.top < bottom) bottom = rect.top;
@@ -65,6 +67,7 @@
     root.style.setProperty('--visible-top', Math.round(bounds.top) + 'px');
     root.style.setProperty('--visible-bottom', Math.round(bounds.bottom) + 'px');
     root.style.setProperty('--visible-height', Math.round(bounds.height) + 'px');
+    root.style.setProperty('--bottom-ui-space', Math.max(0, Math.round(window.innerHeight - bounds.bottom)) + 'px');
     return bounds;
   }
 
@@ -83,7 +86,7 @@
     window.addEventListener('orientationchange', schedule, { passive: true });
     if (window.visualViewport) window.visualViewport.addEventListener('resize', schedule, { passive: true });
     var observer = 'ResizeObserver' in window ? new ResizeObserver(schedule) : null;
-    [document.getElementById('site-top-stack'), document.getElementById('smf-mobile-footer-stack'), document.querySelector('.site-footer')].forEach(function (element) {
+    document.querySelectorAll('[data-viewport-boundary]').forEach(function (element) {
       if (observer && element) observer.observe(element);
     });
     if (document.fonts && document.fonts.ready) document.fonts.ready.then(schedule);
@@ -106,13 +109,12 @@
     var activation = ++targetActivation;
     var anchor = contextAnchor(target);
     if (!anchor) return;
+    syncVisibleMetrics();
     window.requestAnimationFrame(function () {
-      window.requestAnimationFrame(function () {
-        if (activation !== targetActivation || !document.documentElement.contains(anchor)) return;
-        var bounds = syncVisibleMetrics();
-        var destination = window.scrollY + anchor.getBoundingClientRect().top - bounds.top - 8;
-        window.scrollTo({ top: Math.max(0, Math.round(destination)), left: 0, behavior: 'auto' });
-      });
+      if (activation !== targetActivation || !document.documentElement.contains(anchor)) return;
+      var bounds = visibleBounds();
+      var destination = window.scrollY + anchor.getBoundingClientRect().top - bounds.top - 8;
+      window.scrollTo({ top: Math.max(0, Math.round(destination)), left: 0, behavior: 'auto' });
     });
   }
 
@@ -125,28 +127,37 @@
     activateTarget(document.getElementById(hash));
   }
 
-  function contextSlots() {
-    return Array.from(document.querySelectorAll('[data-context-slot]'));
-  }
-
-  function activeContextSlot() {
-    return contextSlots().find(isRendered) || contextSlots()[0] || null;
+  function contextSlot() {
+    return document.getElementById('site-context-slot');
+  }
+
+  function placeContextSlot() {
+    var slot = contextSlot();
+    var topStack = document.getElementById('site-top-stack');
+    var mobileHost = document.getElementById('mobile-context-host');
+    if (!slot || !topStack || !mobileHost) return;
+    var mobile = window.matchMedia('(max-width: 767px)').matches;
+    var target = mobile ? mobileHost : topStack;
+    if (slot.parentNode !== target) target.appendChild(slot);
+    syncVisibleMetrics();
   }
 
   function contextHtml() {
-    var slot = activeContextSlot();
+    var slot = contextSlot();
     return slot ? slot.innerHTML : '';
   }
 
   function setContextHtml(html) {
+    var slot = contextSlot();
+    if (!slot) return;
     var value = html || '';
-    contextSlots().forEach(function (slot) { slot.innerHTML = value; });
+    slot.innerHTML = value;
     root.setAttribute('data-has-context', value.trim() ? 'true' : 'false');
-    window.requestAnimationFrame(syncVisibleMetrics);
+    placeContextSlot();
   }
 
   function visibleContext() {
-    var slot = activeContextSlot();
+    var slot = contextSlot();
     return slot ? slot.firstElementChild : null;
   }
 
@@ -293,7 +304,8 @@
   }
 
   function setupContextNavigation() {
-    var navs = contextSlots().map(function (slot) { return slot.querySelector('.context-nav'); }).filter(Boolean);
+    var slot = contextSlot();
+    var navs = slot ? Array.from(slot.querySelectorAll('.context-nav')) : [];
     var sections = Array.from(document.querySelectorAll('[data-context-section]'));
     if (!navs.length) return;
     var links = navs.reduce(function (all, nav) {
@@ -632,6 +644,10 @@
     setupCalendarInteraction();
     setupEventSkullParades();
     setupListFocus();
+    if (root.getAttribute('data-current-view') === 'tetris' && window.HOTetrisApp) {
+      window.HOTetrisApp.mount();
+      registerViewCleanup(function () { window.HOTetrisApp.unmount(); });
+    }
     if (!settings.preserveScroll && !document.querySelector('.focus-page.is-focus')) {
       if (window.location.hash) activateLocationTarget();
       else window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
@@ -908,6 +924,10 @@
     })).then(function () { root.classList.add('is-view-ready'); });
   }
 
+  placeContextSlot();
+  var contextMedia = window.matchMedia('(max-width: 767px)');
+  if (contextMedia.addEventListener) contextMedia.addEventListener('change', placeContextSlot);
+  else if (contextMedia.addListener) contextMedia.addListener(placeContextSlot);
   syncWebAppMode();
   if (webAppQuery.addEventListener) webAppQuery.addEventListener('change', syncWebAppMode);
   setupVisibleMetrics();
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.10/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.16.0-beta.10
+
+- Tetrisを通常の共通viewport-pageフローへ統合し、fixed座標配置を撤去。
+- Tetrisの初期化・破棄をSPAのビューライフサイクルへ統合。
+- ページ専用メニューを単一DOMへ統合し、PC/スマホ間で同じ要素を配置。
+- Profile/Calendar/Tetrisの画面内ページ構造をviewport-pageへ統一。
+- 未使用の固定高さ・旧色・旧影変数を削除。
+- キーボード操作時のフォーカス表示を復元。
+- 年号アンカー移動の二重requestAnimationFrameを撤去。
+
 ## 0.16.0-beta.9
 
 - Tetris表示時にだけ文書スクロールを停止していた処理を撤去し、stickyヘッダーが管理バー分だけ押し下げられる原因を削除。
changedfooter.php
--- previous/footer.php
+++ 0.16.0-beta.10/footer.php
@@ -8,9 +8,9 @@
 </main>
 <img class="home-dog" src="<?php echo esc_url( get_template_directory_uri() . '/assets/img/8686_dog.webp' ); ?>" alt="" aria-hidden="true">
 <div class="home-runner-track" aria-hidden="true"><a href="<?php echo esc_url( ho_view_url( 'calendar' ) ); ?>" data-view-link="calendar" tabindex="-1"><img src="<?php echo esc_url( get_template_directory_uri() . '/assets/img/8686_skull-run.webp' ); ?>" alt=""></a></div>
-<footer class="site-footer">&copy;Skeleton Music Factory, Japan</footer>
-<div class="mobile-footer-stack" id="smf-mobile-footer-stack">
-	<div class="context-slot context-slot--mobile" data-context-slot><?php echo ho_view_context_html( ho_current_view() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
+<footer class="site-footer" data-viewport-boundary="bottom">&copy;Skeleton Music Factory, Japan</footer>
+<div class="mobile-footer-stack" id="smf-mobile-footer-stack" data-viewport-boundary="bottom">
+	<div class="context-slot-host context-slot-host--mobile" id="mobile-context-host"></div>
 	<div class="mobile-dock" id="smf-mobile-dock">
 		<?php ho_render_primary_nav( 'mobile-nav' ); ?>
 		<div class="mobile-dock__credit">&copy;Skeleton Music Factory, Japan</div>
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.10/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.9' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.10' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedheader.php
--- previous/header.php
+++ 0.16.0-beta.10/header.php
@@ -20,7 +20,7 @@
 <body <?php body_class( 'ho-site' ); ?>>
 <?php wp_body_open(); ?>
 <a class="skip-link" href="#main-content">本文へ移動</a>
-<div class="site-top-stack" id="site-top-stack">
+<div class="site-top-stack" id="site-top-stack" data-viewport-boundary="top">
 <header class="site-header" id="site-header">
 	<div class="brand">
 		<?php $brand_name = get_theme_mod( 'ho_brand_name', 'Hiroki Ohnuma' ); $brand_role = get_theme_mod( 'ho_role', 'composer' ); ?>
@@ -31,6 +31,6 @@
 	<?php ho_recent_posts_ticker(); ?>
 	<?php ho_render_primary_nav(); ?>
 </header>
-	<div class="context-slot context-slot--desktop" data-context-slot><?php echo $context_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
+	<div class="context-slot" id="site-context-slot" data-context-slot><?php echo $context_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
 </div>
 <main class="site-main" id="main-content">
changedinc/view-shell.php
--- previous/inc/view-shell.php
+++ 0.16.0-beta.10/inc/view-shell.php
@@ -140,7 +140,7 @@
 
 function ho_profile_view_html() {
 	$page = get_page_by_path( 'profile' );
-	if ( ! $page ) return '<article class="content-shell profile-page"><p>Profile data was not found.</p></article>';
+	if ( ! $page ) return '<article class="content-shell viewport-page profile-page"><p>Profile data was not found.</p></article>';
 	$vi = get_post_meta( $page->ID, '_ho_profile_vi', true );
 	$zh = get_post_meta( $page->ID, '_ho_profile_zh', true );
 	if ( ! trim( $zh ) && preg_match( '/(?:\R\s*){2,}(?=大沼弘基\s*[((]作曲家[))])/u', $vi, $language_break, PREG_OFFSET_CAPTURE ) ) {
@@ -156,7 +156,7 @@
 	);
 	ob_start();
 	?>
-	<article class="content-shell profile-page" data-view-root="profile">
+	<article class="content-shell viewport-page profile-page" data-view-root="profile">
 		<h1 class="screen-reader-text">Profile</h1>
 		<div class="module-layout record-shell profile-page__layout">
 			<figure class="module module--media profile-page__image"><img src="https://8686.jaikki-rocky.com/wp-content/uploads/2025/02/8686_HirokiOhnuma-scaled.jpg" alt="Hiroki Ohnuma" loading="eager"><figcaption>(c)Megumi Matsuda</figcaption></figure>
@@ -220,7 +220,7 @@
 	}
 	ob_start();
 	?>
-	<div class="content-shell calendar-page calendar-page--jaikki" data-view-root="calendar"><?php if ( shortcode_exists( 'jaikki_calendar' ) ) : echo do_shortcode( $shortcode ); else : ?><p class="module module--prose calendar-fallback">カレンダーを読み込めませんでした。<a href="<?php echo esc_url( ho_view_url( 'schedule' ) ); ?>">スケジュール一覧</a>をご覧ください。</p><?php endif; ?></div>
+	<div class="content-shell viewport-page calendar-page calendar-page--jaikki" data-view-root="calendar"><?php if ( shortcode_exists( 'jaikki_calendar' ) ) : echo do_shortcode( $shortcode ); else : ?><p class="module module--prose calendar-fallback">カレンダーを読み込めませんでした。<a href="<?php echo esc_url( ho_view_url( 'schedule' ) ); ?>">スケジュール一覧</a>をご覧ください。</p><?php endif; ?></div>
 	<?php
 	return trim( ob_get_clean() );
 }
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.10/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.9
+Version: 0.16.0-beta.10
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma
changedtemplate-parts/view-tetris.php
--- previous/template-parts/view-tetris.php
+++ 0.16.0-beta.10/template-parts/view-tetris.php
@@ -1,4 +1,4 @@
-<div class="tetris-page" data-view-root="tetris">
+<div class="viewport-page tetris-page" data-view-root="tetris">
 	<div id="tetris-doodle-shell">
 		<div id="tetris-doodle-root">
 			<div class="paper"><div class="wrap"><div class="row"><div class="card">

原因分析・次回改修への指針

大規模整理で旧機能の一部(過去に成立していた細かな挙動)が削られた。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.10.zip


再利用すべき判断基準

  • 新構造へ移行したら、競合する旧CSS・旧JS・互換処理・固定高を削除する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.9

当時の改修目的

モバイルフッターの高さ・余白・影・ボタン寸法をCSSで調整する。

実装した変更

・Tetris表示時にだけ文書スクロールを停止していた処理を撤去し、stickyヘッダーが管理バー分だけ押し下げられる原因を削除。

・スマホのページ専用フッターを、左右リンク・年号ボタンで共通の高さと最小幅を使う単一コントロール設計へ統合。

・ページ専用フッターの上下余白を6pxで対称化し、全体高を内容から自然に決定する構造へ変更。

・年号移動処理に残っていた重複変数宣言を削除。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.10以降でも撤去されず残った仕組みは継続採用とみなす。

表示上の一部寸法を改善。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.10 の要望・修正内容と照合して確定。

Tetris全体の開始位置など構造由来の問題は未解決。

次版で実際に求められたこと:テーマ全体を監査し、Tetrisライフサイクル、viewport、focus、header/footerの残骸を整理する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.9/assets/css/theme.css
@@ -61,8 +61,6 @@
 }
 html[data-current-view="tetris"],
 html[data-current-view="tetris"] body {
-  height:100%;
-  overflow:hidden;
   overscroll-behavior:none;
 }
 html[data-current-view="tetris"] .site-top-stack {
@@ -153,7 +151,7 @@
 
 .context-slot .context-nav { height:var(--context-height); padding-block:6px; }
 
-.context-nav { --context-accent:var(--green); align-items:center; background:linear-gradient(var(--surface),rgba(237,241,247,.82)); display:grid; gap:10px; grid-template-columns:100px minmax(0,1fr) 100px; height:40px; overflow:visible; padding:0 12px; white-space:nowrap; }
+.context-nav { --context-accent:var(--green); --context-control-height:28px; --context-control-min-width:72px; align-items:center; background:linear-gradient(var(--surface),rgba(237,241,247,.82)); display:grid; gap:10px; grid-template-columns:100px minmax(0,1fr) 100px; height:40px; overflow:visible; padding:0 12px; white-space:nowrap; }
 .context-nav--schedule { --context-accent:var(--yellow); }
 .context-nav--works { --context-accent:var(--blue); }
 .context-nav.context-nav--profile { --context-accent:var(--blue); grid-template-columns:minmax(0,1fr); }
@@ -161,6 +159,7 @@
 .context-nav--profile .context-nav__years { display:grid; grid-template-columns:repeat(4,minmax(88px,120px)); justify-content:center; overflow:visible; }
 .context-nav--profile .context-nav__years button { width:100%; }
 .context-nav a,.context-nav button { border:0; border-radius:500px; color:#747c89; cursor:pointer; font-size:13px; font-weight:800; min-width:30px; padding:4px 10px; text-align:center; text-decoration:none; }
+.context-nav__section-link,.context-nav__years :is(a,button) { min-height:var(--context-control-height); }
 .context-nav button { font-family:inherit; }
 .context-nav__years { align-items:center; display:flex; gap:9px; height:100%; justify-content:center; min-width:0; overflow-x:auto; overflow-y:visible; padding:6px 8px; scrollbar-width:none; }
 .context-nav__years.is-overflowing { justify-content:flex-start; }
@@ -531,16 +530,48 @@
   .context-slot--mobile:empty { display:none; }
   .mobile-dock { display:block; flex:0 0 auto; position:static; }
   :root.is-web-app .mobile-footer-stack { background:rgba(119,128,143,.94); padding-bottom:env(safe-area-inset-bottom, 0px); }
-  .context-nav { background:rgba(237,241,247,.97); box-shadow:0 -5px 14px rgba(163,177,198,.5); display:grid; gap:0; grid-template-columns:20% minmax(0,60%) 20%; height:52px; overflow:hidden; padding:0; position:relative; }
-  .context-nav__section-link { align-items:center; align-self:stretch; background:linear-gradient(90deg,#fff,var(--surface)); border-radius:16px; display:flex; justify-content:center; padding:0 5px; position:relative; z-index:2; }
-  .context-nav__section-link:last-child { background:linear-gradient(90deg,var(--surface),#fff); }
-  .context-nav__years { height:52px; justify-content:flex-start; mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%); min-width:0; padding:7px 12px; -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%); }
-  .context-nav__years :is(a,button) { min-width:72px; padding:5px 12px; }
+  .context-nav {
+    --context-control-height:34px;
+    --context-control-min-width:72px;
+    background:rgba(237,241,247,.97);
+    box-shadow:0 -5px 14px rgba(163,177,198,.5);
+    display:grid;
+    gap:8px;
+    grid-template-columns:auto minmax(0,1fr) auto;
+    height:auto;
+    min-height:46px;
+    overflow:hidden;
+    padding:6px 8px;
+    position:relative;
+  }
+  .context-nav__section-link,
+  .context-nav__years :is(a,button) {
+    align-items:center;
+    background:var(--surface);
+    border:1px solid rgba(255,255,255,.64);
+    box-shadow:2px 3px 6px rgba(143,158,184,.34),-2px -2px 6px rgba(255,255,255,.92);
+    display:flex;
+    height:var(--context-control-height);
+    justify-content:center;
+    min-height:var(--context-control-height);
+    min-width:var(--context-control-min-width);
+    padding:0 12px;
+  }
+  .context-nav__section-link { align-self:center; position:relative; z-index:2; }
+  .context-nav__years {
+    gap:8px;
+    height:var(--context-control-height);
+    justify-content:flex-start;
+    mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%);
+    min-width:0;
+    padding:0 4px;
+    -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%);
+  }
   .context-nav__years :is(a,button)[aria-current="page"] { background:var(--context-accent); color:#fff; }
-  .context-nav--profile { display:block; padding:0; }
-  .context-nav--profile .context-nav__years { display:grid; gap:clamp(4px,1.5vw,9px); grid-template-columns:repeat(4,minmax(0,1fr)); mask-image:none; overflow:visible; padding:7px 8px; -webkit-mask-image:none; }
-  .context-nav--profile .context-nav__years button { font-size:clamp(10px,3.3vw,13px); min-width:0; padding:5px 3px; width:100%; }
-  .context-nav--link { align-items:center; display:flex; justify-content:center; padding:0; }
+  .context-nav--profile { display:block; min-height:46px; padding:6px 8px; }
+  .context-nav--profile .context-nav__years { display:grid; gap:clamp(4px,1.5vw,9px); grid-template-columns:repeat(4,minmax(0,1fr)); mask-image:none; overflow:visible; padding:0; -webkit-mask-image:none; }
+  .context-nav--profile .context-nav__years button { font-size:clamp(10px,3.3vw,13px); min-width:0; padding:0 3px; width:100%; }
+  .context-nav--link { align-items:center; display:flex; justify-content:center; min-height:46px; padding:6px 8px; }
   .context-nav--link .link-thanks { font-size:clamp(11px,3.2vw,14px); padding:0 12px; width:100%; }
   .mobile-nav { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); }
   .mobile-nav { border-top:1px solid rgba(255,255,255,.72); box-shadow:inset 0 4px 9px rgba(255,255,255,.18),0 -5px 16px rgba(143,158,184,.34); }
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.9/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.16.0-beta.9
+
+- Tetris表示時にだけ文書スクロールを停止していた処理を撤去し、stickyヘッダーが管理バー分だけ押し下げられる原因を削除。
+- スマホのページ専用フッターを、左右リンク・年号ボタンで共通の高さと最小幅を使う単一コントロール設計へ統合。
+- ページ専用フッターの上下余白を6pxで対称化し、全体高を内容から自然に決定する構造へ変更。
+- 年号移動処理に残っていた重複変数宣言を削除。
+
 ## 0.16.0-beta.8
 
 - Rebuilt the mobile footer as one fixed structural stack: page-specific navigation, primary navigation, and credit now occupy one contiguous layout container.
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.9/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.8' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.9' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.9/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.8
+Version: 0.16.0-beta.9
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

フッター内部CSSの調整であり、ページ全体の生成順・ライフサイクルには影響しなかった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.9.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.8

当時の改修目的

ヘッダー・フッター・ページ専用メニューを再整理し、モバイルの二層固定問題を解消する。

実装した変更

・Rebuilt the mobile footer as one fixed structural stack: page-specific navigation, primary navigation, and credit now occupy one contiguous layout container.

・Removed independent fixed-position calculations between the page-specific footer and primary footer.

・Balanced page-specific footer vertical padding so the space above and below its buttons is equal.

・Updated visible-area measurement and context-menu synchronization for the explicit desktop/mobile context slots.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.9以降でも撤去されず残った仕組みは継続採用とみなす。

header/footer/theme.js/cssを同時に変更。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.9 の要望・修正内容と照合して確定。

ページフッターの高さ、影のクリップ、ボタン寸法、隙間が残った。

次版で実際に求められたこと:モバイルフッターの高さ・余白・影・ボタン寸法をCSSで調整する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, footer.php, functions.php, header.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.8/assets/css/theme.css
@@ -406,7 +406,7 @@
 .calendar-page { box-sizing:border-box; height:var(--visible-height); max-width:1300px; min-height:0; }
 
 .site-footer { align-items:center; background:rgba(119,128,143,.86); border-top:1px solid rgba(255,255,255,.55); box-shadow:0 -6px 15px rgba(163,177,198,.38); bottom:0; color:#fff; display:flex; font-size:14px; font-weight:600; height:var(--footer-height); justify-content:center; left:0; position:fixed; right:0; z-index:9991; }
-.mobile-dock { display:none; }
+.mobile-footer-stack,.mobile-dock,.context-slot--mobile { display:none; }
 .mobile-dock__credit { align-items:center; background:rgba(119,128,143,.94); color:#fff; display:flex; font-size:9px; font-weight:600; height:var(--mobile-credit-height); justify-content:center; line-height:1; }
 :root.is-web-app { --mobile-nav-height:58px; --mobile-credit-height:18px; --mobile-dock-height:calc(76px + env(safe-area-inset-bottom, 0px)); }
 :root.is-web-app .mobile-dock__credit { display:flex; }
@@ -525,18 +525,20 @@
   .release-card { font-size:14px; }
   .calendar-page { min-width:0; overflow-x:visible; padding-left:5px; padding-right:5px; width:100%; }
   .site-footer { display:none; }
-  .mobile-dock { bottom:0; display:block; left:0; position:fixed; right:0; z-index:9999; }
-  :root.is-web-app .mobile-dock { background:rgba(119,128,143,.94); padding-bottom:env(safe-area-inset-bottom, 0px); }
-  .context-slot { bottom:var(--mobile-dock-height); height:var(--mobile-context-height); left:0; position:fixed; right:0; z-index:9998; }
-  .context-slot:empty { display:none; }
-  .context-nav { background:rgba(237,241,247,.97); bottom:auto; box-shadow:0 -5px 14px rgba(163,177,198,.5); display:grid; gap:0; grid-template-columns:20% minmax(0,60%) 20%; height:56px; left:auto; overflow:hidden; padding:0; position:relative; right:auto; z-index:auto; }
+  .context-slot--desktop { display:none; }
+  .mobile-footer-stack { bottom:0; display:flex; flex-direction:column; left:0; position:fixed; right:0; z-index:9999; }
+  .context-slot--mobile { display:block; flex:0 0 auto; height:auto; position:static; }
+  .context-slot--mobile:empty { display:none; }
+  .mobile-dock { display:block; flex:0 0 auto; position:static; }
+  :root.is-web-app .mobile-footer-stack { background:rgba(119,128,143,.94); padding-bottom:env(safe-area-inset-bottom, 0px); }
+  .context-nav { background:rgba(237,241,247,.97); box-shadow:0 -5px 14px rgba(163,177,198,.5); display:grid; gap:0; grid-template-columns:20% minmax(0,60%) 20%; height:52px; overflow:hidden; padding:0; position:relative; }
   .context-nav__section-link { align-items:center; align-self:stretch; background:linear-gradient(90deg,#fff,var(--surface)); border-radius:16px; display:flex; justify-content:center; padding:0 5px; position:relative; z-index:2; }
   .context-nav__section-link:last-child { background:linear-gradient(90deg,var(--surface),#fff); }
-  .context-nav__years { height:56px; justify-content:flex-start; mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%); min-width:0; padding:10px 12px; -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%); }
+  .context-nav__years { height:52px; justify-content:flex-start; mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%); min-width:0; padding:7px 12px; -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%); }
   .context-nav__years :is(a,button) { min-width:72px; padding:5px 12px; }
   .context-nav__years :is(a,button)[aria-current="page"] { background:var(--context-accent); color:#fff; }
   .context-nav--profile { display:block; padding:0; }
-  .context-nav--profile .context-nav__years { display:grid; gap:clamp(4px,1.5vw,9px); grid-template-columns:repeat(4,minmax(0,1fr)); mask-image:none; overflow:visible; padding:10px 8px; -webkit-mask-image:none; }
+  .context-nav--profile .context-nav__years { display:grid; gap:clamp(4px,1.5vw,9px); grid-template-columns:repeat(4,minmax(0,1fr)); mask-image:none; overflow:visible; padding:7px 8px; -webkit-mask-image:none; }
   .context-nav--profile .context-nav__years button { font-size:clamp(10px,3.3vw,13px); min-width:0; padding:5px 3px; width:100%; }
   .context-nav--link { align-items:center; display:flex; justify-content:center; padding:0; }
   .context-nav--link .link-thanks { font-size:clamp(11px,3.2vw,14px); padding:0 12px; width:100%; }
@@ -664,3 +666,4 @@
   *,*::before,*::after { animation-duration:.01ms !important; animation-iteration-count:1 !important; scroll-behavior:auto !important; }
 }
 
+
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.8/assets/js/theme.js
@@ -51,7 +51,7 @@
     var stack = document.getElementById('site-top-stack');
     var top = isRendered(stack) ? Math.max(0, stack.getBoundingClientRect().bottom) : 0;
     var bottom = window.innerHeight;
-    var candidates = [document.querySelector('[data-context-slot]'), document.getElementById('smf-mobile-dock'), document.querySelector('.site-footer')];
+    var candidates = [document.getElementById('smf-mobile-footer-stack'), document.querySelector('.site-footer')];
     candidates.forEach(function (element) {
       if (!isRendered(element)) return;
       var rect = element.getBoundingClientRect();
@@ -83,7 +83,7 @@
     window.addEventListener('orientationchange', schedule, { passive: true });
     if (window.visualViewport) window.visualViewport.addEventListener('resize', schedule, { passive: true });
     var observer = 'ResizeObserver' in window ? new ResizeObserver(schedule) : null;
-    [document.getElementById('site-top-stack'), document.querySelector('[data-context-slot]'), document.getElementById('smf-mobile-dock'), document.querySelector('.site-footer')].forEach(function (element) {
+    [document.getElementById('site-top-stack'), document.getElementById('smf-mobile-footer-stack'), document.querySelector('.site-footer')].forEach(function (element) {
       if (observer && element) observer.observe(element);
     });
     if (document.fonts && document.fonts.ready) document.fonts.ready.then(schedule);
@@ -125,30 +125,28 @@
     activateTarget(document.getElementById(hash));
   }
 
-  function contextSlot() {
-    return document.querySelector('[data-context-slot]');
-  }
-
   function contextSlots() {
-    var slot = contextSlot();
-    return slot ? [slot] : [];
+    return Array.from(document.querySelectorAll('[data-context-slot]'));
+  }
+
+  function activeContextSlot() {
+    return contextSlots().find(isRendered) || contextSlots()[0] || null;
   }
 
   function contextHtml() {
-    var slot = contextSlot();
+    var slot = activeContextSlot();
     return slot ? slot.innerHTML : '';
   }
 
   function setContextHtml(html) {
-    var slot = contextSlot();
     var value = html || '';
-    if (slot) slot.innerHTML = value;
+    contextSlots().forEach(function (slot) { slot.innerHTML = value; });
     root.setAttribute('data-has-context', value.trim() ? 'true' : 'false');
     window.requestAnimationFrame(syncVisibleMetrics);
   }
 
   function visibleContext() {
-    var slot = contextSlot();
+    var slot = activeContextSlot();
     return slot ? slot.firstElementChild : null;
   }
 
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.8/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.16.0-beta.8
+
+- Rebuilt the mobile footer as one fixed structural stack: page-specific navigation, primary navigation, and credit now occupy one contiguous layout container.
+- Removed independent fixed-position calculations between the page-specific footer and primary footer.
+- Balanced page-specific footer vertical padding so the space above and below its buttons is equal.
+- Updated visible-area measurement and context-menu synchronization for the explicit desktop/mobile context slots.
+
 ## 0.16.0-beta.7
 
 - Tetris view: removed the shared top-stack drop shadow while the game view is active. The shadow occupied visual space below the header and appeared as an unresolved top gap even though the game container itself began at the measured header boundary.
changedfooter.php
--- previous/footer.php
+++ 0.16.0-beta.8/footer.php
@@ -9,9 +9,12 @@
 <img class="home-dog" src="<?php echo esc_url( get_template_directory_uri() . '/assets/img/8686_dog.webp' ); ?>" alt="" aria-hidden="true">
 <div class="home-runner-track" aria-hidden="true"><a href="<?php echo esc_url( ho_view_url( 'calendar' ) ); ?>" data-view-link="calendar" tabindex="-1"><img src="<?php echo esc_url( get_template_directory_uri() . '/assets/img/8686_skull-run.webp' ); ?>" alt=""></a></div>
 <footer class="site-footer">&copy;Skeleton Music Factory, Japan</footer>
-<div class="mobile-dock" id="smf-mobile-dock">
-	<?php ho_render_primary_nav( 'mobile-nav' ); ?>
-	<div class="mobile-dock__credit">&copy;Skeleton Music Factory, Japan</div>
+<div class="mobile-footer-stack" id="smf-mobile-footer-stack">
+	<div class="context-slot context-slot--mobile" data-context-slot><?php echo ho_view_context_html( ho_current_view() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
+	<div class="mobile-dock" id="smf-mobile-dock">
+		<?php ho_render_primary_nav( 'mobile-nav' ); ?>
+		<div class="mobile-dock__credit">&copy;Skeleton Music Factory, Japan</div>
+	</div>
 </div>
 <?php wp_footer(); ?>
 </body>
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.8/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.7' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.8' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedheader.php
--- previous/header.php
+++ 0.16.0-beta.8/header.php
@@ -31,6 +31,6 @@
 	<?php ho_recent_posts_ticker(); ?>
 	<?php ho_render_primary_nav(); ?>
 </header>
-	<div class="context-slot" data-context-slot><?php echo $context_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
+	<div class="context-slot context-slot--desktop" data-context-slot><?php echo $context_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
 </div>
 <main class="site-main" id="main-content">
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.8/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.7
+Version: 0.16.0-beta.8
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

固定レイヤーを一体化したものの、内部寸法とoverflowの旧指定が残った。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.8.zip


再利用すべき判断基準

  • 新構造へ移行したら、競合する旧CSS・旧JS・互換処理・固定高を削除する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.7

当時の改修目的

beta.6後のCSS競合と表示余白を追加修正する。

実装した変更

・Tetris view: removed the shared top-stack drop shadow while the game view is active. The shadow occupied visual space below the header and appeared as an unresolved top gap even though the game container itself began at the measured header boundary.

・No Tetris-specific padding, negative margin, or coordinate offset was added.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.8以降でも撤去されず残った仕組みは継続採用とみなす。

CSS中心の限定修正。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.8 の要望・修正内容と照合して確定。

根本的なTetris開始位置とモバイルフッター構造は解決しなかった。

次版で実際に求められたこと:ヘッダー・フッター・ページ専用メニューを再整理し、モバイルの二層固定問題を解消する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.7/assets/css/theme.css
@@ -64,6 +64,9 @@
   height:100%;
   overflow:hidden;
   overscroll-behavior:none;
+}
+html[data-current-view="tetris"] .site-top-stack {
+  box-shadow:none;
 }
 html[data-current-view="tetris"] .tetris-page {
   height:var(--visible-height);
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.7/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.16.0-beta.7
+
+- Tetris view: removed the shared top-stack drop shadow while the game view is active. The shadow occupied visual space below the header and appeared as an unresolved top gap even though the game container itself began at the measured header boundary.
+- No Tetris-specific padding, negative margin, or coordinate offset was added.
+
 
 ## 0.16.0-beta.2
 
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.7/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.6' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.7' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.7/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.6
+Version: 0.16.0-beta.7
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

CSSのみの調整でDOM/ライフサイクル構造に踏み込まなかった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.7.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.6

当時の改修目的

PC Tetris上余白、スマホCalendarの位置、既定フォーカス枠を修正する。

実装した変更

・PC版Tetrisの外側上余白を撤去し、ゲーム面をヘッダー直下から開始。

・リンクとボタンに残っていたブラウザ既定の青いフォーカス枠を撤去。

・スマホ版Calendarを、実測した可視領域上端・下端に固定する画面内レイアウトへ統一。

・Calendarの本文開始位置を通常フローとスクロール状態に依存させず、主ヘッダー直下へ確定。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.7以降でも撤去されず残った仕組みは継続採用とみなす。

Tetris外側上余白撤去、Calendarを実測可視領域へ固定。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.7 の要望・修正内容と照合して確定。

アクセシビリティ上必要なフォーカス表示まで消したため後の監査で問題視。Tetris/フッター問題も残存。

次版で実際に求められたこと:beta.6後のCSS競合と表示余白を追加修正する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/tetris.css, assets/css/theme.css, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/css/tetris.css
--- previous/assets/css/tetris.css
+++ 0.16.0-beta.6/assets/css/tetris.css
@@ -52,8 +52,8 @@
         var(--td-surface);
       height:100%;
       overflow:hidden;
-      padding:8px 10px;
-      border-radius:14px;
+      padding:0 10px 8px;
+      border-radius:0 0 14px 14px;
     }
 
     #tetris-doodle-root .wrap{
changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.6/assets/css/theme.css
@@ -75,6 +75,10 @@
   touch-action:none;
 }
 a { color: inherit; }
+a:focus,
+a:focus-visible,
+button:focus,
+button:focus-visible { outline:none; }
 img { height: auto; max-width: 100%; }
 button, input, textarea { font: inherit; }
 .skip-link { background:#fff; left:8px; padding:8px 12px; position:fixed; top:-80px; z-index:100000; }
@@ -628,11 +632,18 @@
 
 @media (max-width: 767px) {
   .calendar-page--jaikki {
+    background:var(--surface);
     box-sizing:border-box;
     height:var(--visible-height);
+    left:0;
     min-height:0;
     overflow:visible;
     padding:12px 14px;
+    position:fixed;
+    right:0;
+    top:var(--visible-top);
+    width:100%;
+    z-index:1;
   }
   .calendar-page--jaikki .jaikki-calendar-root { min-height:100%; width:100%; }
   .profile-page { height:var(--visible-height); min-height:0; padding-bottom:0; }
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.6/docs/BETA1-CHANGELOG.md
@@ -134,6 +134,13 @@
 
 
 
+## 0.16.0-beta.6
+
+- PC版Tetrisの外側上余白を撤去し、ゲーム面をヘッダー直下から開始。
+- リンクとボタンに残っていたブラウザ既定の青いフォーカス枠を撤去。
+- スマホ版Calendarを、実測した可視領域上端・下端に固定する画面内レイアウトへ統一。
+- Calendarの本文開始位置を通常フローとスクロール状態に依存させず、主ヘッダー直下へ確定。
+
 ## 0.16.0-beta.5
 
 - Unified all viewport-bound layout against the actually rendered header, context navigation, footer, and mobile dock.
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.6/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.5' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.6' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.6/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.5
+Version: 0.16.0-beta.6
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

見た目の青枠を全面除去したため、キーボード操作時の視認性まで失われた。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.6.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.5

当時の改修目的

Profile/Calendar/Tetris/アンカー/Focusを、実際に描画された上下境界から算出する。

実装した変更

・Unified all viewport-bound layout against the actually rendered header, context navigation, footer, and mobile dock.

・Removed fixed header-stack and viewport-height arithmetic from Profile, Calendar, Tetris, anchor navigation, and focus UI.

・Year anchors now align to the current visible content top, including the WordPress admin bar and responsive context menu position.

・Replaced fixed IntersectionObserver margins with one requestAnimationFrame-throttled section tracker using the same live bounds.

・Removed calendar/Tetris page-specific top compensation and legacy CSS scroll offsets.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.6以降でも撤去されず残った仕組みは継続採用とみなす。

固定高さ算術、旧scroll offset、固定observer marginを削除し、live boundsへ統一。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.6 の要望・修正内容と照合して確定。

PC Tetris上余白、スマホCalendar固定位置、フォーカス枠などが残った。

次版で実際に求められたこと:PC Tetris上余白、スマホCalendarの位置、既定フォーカス枠を修正する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/tetris.css, assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/css/tetris.css
--- previous/assets/css/tetris.css
+++ 0.16.0-beta.5/assets/css/tetris.css
@@ -60,7 +60,7 @@
       display:grid;
       height:100%;
       margin:0 auto;
-      width:min(1100px,calc((var(--viewport-content-height) - 80px)/2 + 250px),calc(100vw - 20px));
+      width:min(1100px,calc((var(--visible-height) - 80px)/2 + 250px),calc(100vw - 20px));
     }
     #tetris-doodle-root .row{ display:grid; grid-template-columns:minmax(0,1fr); height:100%; min-height:0; align-items:stretch; }
 
changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.5/assets/css/theme.css
@@ -18,13 +18,9 @@
   --primary-nav-height: 58px;
   --context-height: 52px;
   --mobile-context-height: 56px;
-  --desktop-top-stack-height: calc(var(--brand-height) + var(--ticker-height) + var(--primary-nav-height) + var(--context-height));
-  --mobile-top-stack-height: 149px;
-  --content-edge-gap: 20px;
-  --page-top-clearance: var(--content-edge-gap);
   --content-top: 12px;
   --content-inline: 24px;
-  --content-bottom: 80px;
+  --content-bottom: calc(100dvh - var(--visible-bottom) + 26px);
   --module-gap: 28px;
   --module-section-gap: 34px;
   --module-radius: 24px;
@@ -35,10 +31,10 @@
   --mobile-nav-height: 48px;
   --mobile-credit-height: 16px;
   --mobile-dock-height: 64px;
-  --viewport-content-top: var(--desktop-top-stack-height);
-  --viewport-content-bottom: calc(100dvh - var(--footer-height));
-  --viewport-content-height: calc(var(--viewport-content-bottom) - var(--viewport-content-top));
   --view-fade-duration: .86s;
+  --visible-top: 0px;
+  --visible-bottom: 100dvh;
+  --visible-height: 100dvh;
   --skull-parade-max-width: calc(var(--record-shell) - (var(--record-shell-inline) * 2) - (var(--module-padding) * 2));
   --surface: #edf3f8;
   --surface-soft: #f5f8fb;
@@ -52,7 +48,7 @@
 }
 
 * { box-sizing: border-box; }
-html { -webkit-tap-highlight-color:transparent; scroll-padding-top:calc(var(--desktop-top-stack-height) + var(--page-top-clearance)); }
+html { -webkit-tap-highlight-color:transparent; }
 body {
   background: var(--surface);
   color: var(--ink);
@@ -61,7 +57,7 @@
   line-height: 1.45;
   margin: 0;
   overflow-x: clip;
-  padding-bottom: var(--footer-height);
+  padding-bottom:0;
 }
 html[data-current-view="tetris"],
 html[data-current-view="tetris"] body {
@@ -70,12 +66,12 @@
   overscroll-behavior:none;
 }
 html[data-current-view="tetris"] .tetris-page {
-  bottom:var(--footer-height);
+  height:var(--visible-height);
   left:0;
   overflow:hidden;
   position:fixed;
   right:0;
-  top:calc(var(--desktop-top-stack-height) + var(--page-top-clearance));
+  top:var(--visible-top);
   touch-action:none;
 }
 a { color: inherit; }
@@ -95,10 +91,9 @@
 .admin-bar .site-top-stack { top:32px; }
 .site-header { background:var(--surface); }
 .site-main { min-height:1px; }
-[data-context-section], [data-focus-item] { scroll-margin-top:calc(var(--desktop-top-stack-height) + 8px); }
 .context-slot { height:var(--context-height); overflow:visible; }
 .context-slot:empty { height:0; }
-html[data-has-context="false"] { --context-height:0px; --desktop-top-stack-height:calc(var(--brand-height) + var(--ticker-height) + var(--primary-nav-height)); }
+html[data-has-context="false"] { --context-height:0px; }
 .brand { background:var(--surface); height:var(--brand-height); position:relative; }
 .brand::after { background:rgba(117,128,146,.48); content:""; height:1px; left:10px; position:absolute; right:10px; top:79px; }
 .brand__name, .brand__role {
@@ -234,7 +229,6 @@
 .module-grid { display:grid; gap:var(--module-gap); }
 .record-shell,.module-archive,.focus-stage { margin-inline:auto; max-width:var(--record-shell); padding-inline:var(--record-shell-inline); width:100%; }
 .module-archive,.focus-stage { padding-bottom:28px; padding-top:10px; }
-.focus-stage { scroll-margin-top:0; }
 .module-section { display:grid; gap:var(--module-gap); }
 .module-section + .module-section { margin-top:var(--module-section-gap); }
 
@@ -324,14 +318,14 @@
 .profile-page__image figcaption { background:rgba(35,40,49,.58); border-radius:500px; bottom:10px; color:#fff; font-size:10px; padding:3px 9px; position:absolute; right:12px; }
 
 @media (min-width:768px) and (min-height:650px) {
-  .profile-page { box-sizing:border-box; height:var(--viewport-content-height); min-height:0; padding-bottom:0; }
+  .profile-page { box-sizing:border-box; height:var(--visible-height); min-height:0; padding-bottom:0; }
   .profile-page__layout { grid-template-rows:repeat(2,minmax(0,1fr)); height:100%; }
   .profile-page__image { height:auto; min-height:0; }
   .profile-page .language-tabs { min-height:0; }
   .profile-page .language-tabs__panel { height:100%; overflow:auto; overscroll-behavior:contain; }
 }
 .event-skull-parade { align-items:end; display:grid; gap:clamp(4px,1.5vw,18px); grid-template-columns:repeat(var(--skull-count),minmax(0,1fr)); isolation:isolate; margin:0 auto; max-width:var(--skull-parade-max-width); min-height:clamp(92px,14vw,170px); overflow:visible; padding:12px clamp(6px,2vw,20px) 6px; position:relative; width:100%; }
-.focus-parade-layer { bottom:calc(100dvh - var(--viewport-content-bottom) + 8px); left:50%; max-width:calc(100vw - 28px); pointer-events:none; position:fixed; transform:translateX(-50%); width:min(calc(100vw - 28px),var(--skull-parade-max-width)); z-index:9988; }
+.focus-parade-layer { bottom:calc(100dvh - var(--visible-bottom) + 8px); left:50%; max-width:calc(100vw - 28px); pointer-events:none; position:fixed; transform:translateX(-50%); width:min(calc(100vw - 28px),var(--skull-parade-max-width)); z-index:9988; }
 .focus-parade-layer:empty { display:none; }
 .event-skull-parade__slot { align-self:end; display:block; justify-self:center; max-height:150px; max-width:150px; opacity:0; transform-origin:50% 100%; width:100%; }
 .event-skull-parade__slot img { --skull-visual-scale:1; display:block; transform:scale(var(--skull-visual-scale)); transform-origin:50% 100%; width:100%; }
@@ -371,7 +365,7 @@
 .event-focus__note { background:rgba(245,248,251,.48); border:1px solid rgba(255,255,255,.58); border-radius:11px; box-shadow:inset 1px 1px 3px rgba(143,158,184,.2),inset -1px -1px 3px rgba(255,255,255,.72); font-size:16px; font-weight:700; line-height:1.45; padding:12px 14px; }
 .event-focus__note p { margin:0 0 1em; }
 .event-focus__note p:last-child { margin-bottom:0; }
-.focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--desktop-top-stack-height); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
+.focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--visible-top); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
 .focus-return-control svg { fill:none; height:28px; overflow:visible; stroke:currentColor; stroke-linecap:round; stroke-linejoin:round; stroke-width:3.4; width:28px; }
 .focus-page.is-focus .focus-return-control { opacity:1; pointer-events:auto; transform:translateY(0); }
 .focus-return-control:active { box-shadow:var(--neu-inset); transform:translateY(1px); }
@@ -402,7 +396,7 @@
 .release-card img { aspect-ratio:1; border-radius:18px; box-shadow:var(--neu-inset); display:block; object-fit:cover; width:100%; }
 .release-card strong { display:block; padding:9px; }
 
-.calendar-page { box-sizing:border-box; height:var(--viewport-content-height); max-width:1300px; min-height:0; }
+.calendar-page { box-sizing:border-box; height:var(--visible-height); max-width:1300px; min-height:0; }
 
 .site-footer { align-items:center; background:rgba(119,128,143,.86); border-top:1px solid rgba(255,255,255,.55); box-shadow:0 -6px 15px rgba(163,177,198,.38); bottom:0; color:#fff; display:flex; font-size:14px; font-weight:600; height:var(--footer-height); justify-content:center; left:0; position:fixed; right:0; z-index:9991; }
 .mobile-dock { display:none; }
@@ -423,21 +417,21 @@
   background-position:50% 50%;
   background-repeat:no-repeat;
   background-size:cover;
-  bottom:var(--footer-height);
+  bottom:calc(100dvh - var(--visible-bottom));
   left:0;
   overflow:hidden;
   position:fixed;
   right:0;
-  top:calc(var(--desktop-top-stack-height) + var(--page-top-clearance));
+  top:var(--visible-top);
 }
 .home-stage__album-link { align-items:center; color:var(--red); display:flex; flex-direction:column; font-weight:900; left:20%; position:absolute; text-decoration:none; top:20px; width:154px; z-index:2; }
 .home-stage__album-link img { animation:home-skull 2.4s ease-in-out infinite; width:148px; }
 .home-stage__tap { animation:home-tap 1.7s ease-in-out infinite; font-size:18px; line-height:1; margin-bottom:3px; text-shadow:0 1px 0 #fff; white-space:nowrap; }
 @keyframes home-skull { 50% { transform:rotate(-4deg) scale(1.04); } }
 @keyframes home-tap { 50% { transform:scale(1.08); } }
-.home-dog { bottom:var(--footer-height); height:20dvh; max-width:none; pointer-events:none; position:fixed; right:0; transition:height .72s cubic-bezier(.18,.82,.24,1); user-select:none; width:auto; z-index:9990; }
+.home-dog { bottom:calc(100dvh - var(--visible-bottom)); height:20dvh; max-width:none; pointer-events:none; position:fixed; right:0; transition:height .72s cubic-bezier(.18,.82,.24,1); user-select:none; width:auto; z-index:9990; }
 html[data-current-view="home"] .home-dog { height:50vh; }
-.home-runner-track { --home-runner-size:64px; bottom:var(--footer-height); height:125px; left:66.667vw; pointer-events:none; position:fixed; transition:left .72s cubic-bezier(.18,.82,.24,1); width:33.333vw; z-index:9992; }
+.home-runner-track { --home-runner-size:64px; bottom:calc(100dvh - var(--visible-bottom)); height:125px; left:66.667vw; pointer-events:none; position:fixed; transition:left .72s cubic-bezier(.18,.82,.24,1); width:33.333vw; z-index:9992; }
 html[data-current-view="home"] .home-runner-track { left:33.333vw; }
 .home-runner-track a { animation:home-run 14s linear infinite; bottom:0; display:block; pointer-events:auto; position:absolute; transform-origin:center; width:var(--home-runner-size); }
 .home-runner-track img { animation:home-skip 1.05s cubic-bezier(.37,0,.63,1) infinite; display:block; transform-origin:50% 100%; width:var(--home-runner-size); }
@@ -461,7 +455,7 @@
 @media (min-width: 768px) {
   .link-row__button { font-size:clamp(14px,1.25vw,16px); white-space:nowrap; }
   .home-stage {
-    --home-head-position-unit:min(7.708333vw,calc((100dvh - var(--desktop-top-stack-height) - var(--footer-height) - 64px) / 5));
+    --home-head-position-unit:min(7.708333vw,calc((var(--visible-height) - 64px) / 5));
     --home-head-size:clamp(224px,15.416667vw,296px);
     background-position:center center;
     background-size:cover;
@@ -488,10 +482,7 @@
 }
 
 @media (max-width: 767px) {
-  :root { --page-top-clearance:8px; --viewport-content-top:var(--mobile-top-stack-height); --viewport-content-bottom:calc(100dvh - var(--mobile-dock-height) - var(--mobile-context-height)); --viewport-content-height:calc(var(--viewport-content-bottom) - var(--viewport-content-top)); --content-top:4px; --content-inline:14px; --content-bottom:calc(var(--mobile-dock-height) + var(--mobile-context-height) + 6px); --module-gap:22px; --module-section-gap:28px; --module-padding:14px; --module-radius:20px; --record-shell-inline:14px; }
-  body { padding-bottom:calc(var(--mobile-dock-height) + var(--mobile-context-height)); }
-  html[data-has-context="false"] body { padding-bottom:var(--mobile-dock-height); }
-  html[data-current-view="tetris"] .tetris-page { bottom:var(--mobile-dock-height); }
+  :root { --content-top:4px; --content-inline:14px; --content-bottom:calc(100dvh - var(--visible-bottom) + 6px); --module-gap:22px; --module-section-gap:28px; --module-padding:14px; --module-radius:20px; --record-shell-inline:14px; }
   .admin-bar .site-top-stack { top:46px; }
   .brand { height:79px; }
   .brand::after { top:52px; }
@@ -553,11 +544,11 @@
   html[data-current-view="home"] .home-dog { height:auto; width:min(90vw,800px); }
   html[data-current-view="home"] .home-runner-track { left:0; width:100vw; }
   html:not([data-current-view="home"]) .home-dog,html:not([data-current-view="home"]) .home-runner-track { display:none; }
-  .home-dog,.home-runner-track { bottom:var(--mobile-dock-height); }
+  .home-dog,.home-runner-track { bottom:calc(100dvh - var(--visible-bottom)); }
   :root.is-web-app[data-current-view="home"] .home-dog { display:block !important; opacity:1; visibility:visible; }
-  .admin-bar .home-stage { top:calc(var(--mobile-top-stack-height) + var(--page-top-clearance)); }
+  .admin-bar .home-stage { top:var(--visible-top); }
   .focus-parade-layer {
-    bottom:calc(100dvh - var(--viewport-content-bottom) + 8px);
+    bottom:calc(100dvh - var(--visible-bottom) + 8px);
     max-width:calc(100vw - 16px);
   }
   .event-skull-parade {
@@ -570,7 +561,7 @@
   .event-skull-parade__slot img { --skull-visual-scale:1.34; }
   .focus-return-control {
     right:12px;
-    top:var(--mobile-top-stack-height);
+    top:var(--visible-top);
   }
 }
 
@@ -602,8 +593,6 @@
 }
 
 @media (max-width: 767px) {
-  html { scroll-padding-top:calc(var(--mobile-top-stack-height) + 8px); }
-  [data-context-section], [data-focus-item] { scroll-margin-top:calc(var(--mobile-top-stack-height) + 8px); }
   .site-top-stack { top:0; }
   .admin-bar .site-top-stack { top:46px; }
 }
@@ -621,7 +610,7 @@
   padding:0;
   width:100%;
 }
-.calendar-page--jaikki { padding-top:32px; }
+.calendar-page--jaikki { padding:var(--content-top) var(--content-inline) 12px; }
 .calendar-page--jaikki .jaikki-calendar-root { margin:0 auto; max-width:1180px; }
 .calendar-page--jaikki .jaikki-calendar {
   background:var(--surface);
@@ -640,13 +629,13 @@
 @media (max-width: 767px) {
   .calendar-page--jaikki {
     box-sizing:border-box;
-    height:var(--viewport-content-height);
+    height:var(--visible-height);
     min-height:0;
     overflow:visible;
     padding:12px 14px;
   }
   .calendar-page--jaikki .jaikki-calendar-root { min-height:100%; width:100%; }
-  .profile-page { height:var(--viewport-content-height); min-height:0; padding-bottom:0; }
+  .profile-page { height:var(--visible-height); min-height:0; padding-bottom:0; }
   .profile-page__layout { gap:8px; grid-template-rows:minmax(0,1fr) minmax(0,1fr); height:100%; }
   .profile-page__image { border-radius:20px; height:auto; min-height:0; }
   .profile-page__image img { object-position:center 32%; }
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.5/assets/js/theme.js
@@ -38,11 +38,61 @@
 
   function syncWebAppMode() {
     root.classList.toggle('is-web-app', webAppQuery.matches || window.navigator.standalone === true);
-  }
-
-  function stickyOffset() {
+    window.requestAnimationFrame(syncVisibleMetrics);
+  }
+
+  function isRendered(element) {
+    if (!element) return false;
+    var style = window.getComputedStyle(element);
+    return style.display !== 'none' && style.visibility !== 'hidden' && element.getClientRects().length > 0;
+  }
+
+  function visibleBounds() {
     var stack = document.getElementById('site-top-stack');
-    return stack && window.getComputedStyle(stack).position === 'sticky' ? stack.getBoundingClientRect().height : 0;
+    var top = isRendered(stack) ? Math.max(0, stack.getBoundingClientRect().bottom) : 0;
+    var bottom = window.innerHeight;
+    var candidates = [document.querySelector('[data-context-slot]'), document.getElementById('smf-mobile-dock'), document.querySelector('.site-footer')];
+    candidates.forEach(function (element) {
+      if (!isRendered(element)) return;
+      var rect = element.getBoundingClientRect();
+      if (rect.top > top && rect.top < bottom) bottom = rect.top;
+    });
+    return { top: top, bottom: Math.max(top, bottom), height: Math.max(0, bottom - top) };
+  }
+
+  function syncVisibleMetrics() {
+    var bounds = visibleBounds();
+    root.style.setProperty('--visible-top', Math.round(bounds.top) + 'px');
+    root.style.setProperty('--visible-bottom', Math.round(bounds.bottom) + 'px');
+    root.style.setProperty('--visible-height', Math.round(bounds.height) + 'px');
+    return bounds;
+  }
+
+  function setupVisibleMetrics() {
+    var scheduled = false;
+    function schedule() {
+      if (scheduled) return;
+      scheduled = true;
+      window.requestAnimationFrame(function () {
+        scheduled = false;
+        syncVisibleMetrics();
+      });
+    }
+    schedule();
+    window.addEventListener('resize', schedule, { passive: true });
+    window.addEventListener('orientationchange', schedule, { passive: true });
+    if (window.visualViewport) window.visualViewport.addEventListener('resize', schedule, { passive: true });
+    var observer = 'ResizeObserver' in window ? new ResizeObserver(schedule) : null;
+    [document.getElementById('site-top-stack'), document.querySelector('[data-context-slot]'), document.getElementById('smf-mobile-dock'), document.querySelector('.site-footer')].forEach(function (element) {
+      if (observer && element) observer.observe(element);
+    });
+    if (document.fonts && document.fonts.ready) document.fonts.ready.then(schedule);
+    return function () {
+      window.removeEventListener('resize', schedule);
+      window.removeEventListener('orientationchange', schedule);
+      if (window.visualViewport) window.visualViewport.removeEventListener('resize', schedule);
+      if (observer) observer.disconnect();
+    };
   }
 
   function contextAnchor(target) {
@@ -57,9 +107,12 @@
     var anchor = contextAnchor(target);
     if (!anchor) return;
     window.requestAnimationFrame(function () {
-      if (activation !== targetActivation || !document.documentElement.contains(anchor)) return;
-      var destination = window.scrollY + anchor.getBoundingClientRect().top - stickyOffset();
-      window.scrollTo({ top: Math.max(0, Math.round(destination)), left: 0, behavior: 'auto' });
+      window.requestAnimationFrame(function () {
+        if (activation !== targetActivation || !document.documentElement.contains(anchor)) return;
+        var bounds = syncVisibleMetrics();
+        var destination = window.scrollY + anchor.getBoundingClientRect().top - bounds.top - 8;
+        window.scrollTo({ top: Math.max(0, Math.round(destination)), left: 0, behavior: 'auto' });
+      });
     });
   }
 
@@ -91,6 +144,7 @@
     var value = html || '';
     if (slot) slot.innerHTML = value;
     root.setAttribute('data-has-context', value.trim() ? 'true' : 'false');
+    window.requestAnimationFrame(syncVisibleMetrics);
   }
 
   function visibleContext() {
@@ -285,16 +339,28 @@
       });
     });
 
-    if ('IntersectionObserver' in window) {
-      var observer = new IntersectionObserver(function (entries) {
-        var visible = entries.filter(function (entry) { return entry.isIntersecting; }).sort(function (a, b) {
-          return a.boundingClientRect.top - b.boundingClientRect.top;
-        })[0];
-        if (visible) select(visible.target.getAttribute('data-context-key'));
-      }, { rootMargin: '-' + Math.round(stickyOffset() + 8) + 'px 0px -70% 0px', threshold: 0 });
-      sections.forEach(function (section) { observer.observe(section); });
-      registerViewCleanup(function () { observer.disconnect(); });
-    }
+    var sectionSelectionScheduled = false;
+    function syncSectionSelection() {
+      sectionSelectionScheduled = false;
+      var top = visibleBounds().top + 9;
+      var active = sections[0];
+      sections.forEach(function (section) {
+        var anchor = contextAnchor(section);
+        if (anchor && anchor.getBoundingClientRect().top <= top) active = section;
+      });
+      if (active) select(active.getAttribute('data-context-key'));
+    }
+    function scheduleSectionSelection() {
+      if (sectionSelectionScheduled) return;
+      sectionSelectionScheduled = true;
+      window.requestAnimationFrame(syncSectionSelection);
+    }
+    window.addEventListener('scroll', scheduleSectionSelection, { passive: true });
+    window.addEventListener('resize', scheduleSectionSelection, { passive: true });
+    registerViewCleanup(function () {
+      window.removeEventListener('scroll', scheduleSectionSelection);
+      window.removeEventListener('resize', scheduleSectionSelection);
+    });
 
     registerViewCleanup(listen(window, 'popstate', function () {
       if (document.querySelector('.focus-page.is-focus')) return;
@@ -498,7 +564,7 @@
       page.classList.add('is-focus');
       replaceLocation(archiveUrl() + '#focus-' + type + '-' + itemId, { hoPartial: true, hoListFocus: true });
       document.title = link.textContent.trim() + ' – ' + archiveTitle;
-      window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
+      activateTarget(stage);
       loadDetails(activeClone, type, itemId);
     }
 
@@ -561,6 +627,7 @@
 
   function initializeView(options) {
     var settings = options || {};
+    syncVisibleMetrics();
     setupTabs();
     setupLinkThanks();
     setupContextNavigation();
@@ -845,6 +912,7 @@
 
   syncWebAppMode();
   if (webAppQuery.addEventListener) webAppQuery.addEventListener('change', syncWebAppMode);
+  setupVisibleMetrics();
   initializeView();
   setupViewNavigation();
   revealInitialView();
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.5/docs/BETA1-CHANGELOG.md
@@ -132,3 +132,12 @@
 - 共通の`activateTarget()`が、実際のstickyヘッダー高を一度だけ差し引いて位置を決定。
 - 年号クリック、URLハッシュ、ブラウザ履歴復帰を同じアンカー処理へ統合。
 
+
+
+## 0.16.0-beta.5
+
+- Unified all viewport-bound layout against the actually rendered header, context navigation, footer, and mobile dock.
+- Removed fixed header-stack and viewport-height arithmetic from Profile, Calendar, Tetris, anchor navigation, and focus UI.
+- Year anchors now align to the current visible content top, including the WordPress admin bar and responsive context menu position.
+- Replaced fixed IntersectionObserver margins with one requestAnimationFrame-throttled section tracker using the same live bounds.
+- Removed calendar/Tetris page-specific top compensation and legacy CSS scroll offsets.
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.5/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.4' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.5' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.5/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.4
+Version: 0.16.0-beta.5
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

共通境界へ移行した直後で、各ページに残る旧CSSが干渉した。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.5.zip


再利用すべき判断基準

  • 新構造へ移行したら、競合する旧CSS・旧JS・互換処理・固定高を削除する。
  • 同じ役割の処理は一つの関数・一つの状態・一つのDOMに集約する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.4

当時の改修目的

年号移動を正式な年見出しアンカーへ統一し、固定ヘッダー下への潜り込みを防ぐ。

実装した変更

・Opus/Works/Scheduleの年号移動を、各年セクションの見出しを正式なアンカーとして統一。

・`scrollIntoView()`による固定ヘッダー下への潜り込みを廃止。

・共通の`activateTarget()`が、実際のstickyヘッダー高を一度だけ差し引いて位置を決定。

・年号クリック、URLハッシュ、ブラウザ履歴復帰を同じアンカー処理へ統合。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.5以降でも撤去されず残った仕組みは継続採用とみなす。

activateTargetでstickyヘッダー高を差し引き、クリック・hash・履歴復帰を統合。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.5 の要望・修正内容と照合して確定。

可視領域の上端・下端計算がページごとにまだ不統一。

次版で実際に求められたこと:Profile/Calendar/Tetris/アンカー/Focusを、実際に描画された上下境界から算出する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, inc/view-shell.php, style.css

changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.4/assets/js/theme.js
@@ -45,12 +45,21 @@
     return stack && window.getComputedStyle(stack).position === 'sticky' ? stack.getBoundingClientRect().height : 0;
   }
 
+  function contextAnchor(target) {
+    if (!target) return null;
+    return target.matches('[data-context-anchor]')
+      ? target
+      : (target.querySelector('[data-context-anchor]') || target);
+  }
+
   function activateTarget(target) {
     var activation = ++targetActivation;
-    if (!target) return;
+    var anchor = contextAnchor(target);
+    if (!anchor) return;
     window.requestAnimationFrame(function () {
-      if (activation !== targetActivation || !document.documentElement.contains(target)) return;
-      target.scrollIntoView({ block: 'start', behavior: 'auto' });
+      if (activation !== targetActivation || !document.documentElement.contains(anchor)) return;
+      var destination = window.scrollY + anchor.getBoundingClientRect().top - stickyOffset();
+      window.scrollTo({ top: Math.max(0, Math.round(destination)), left: 0, behavior: 'auto' });
     });
   }
 
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.4/docs/BETA1-CHANGELOG.md
@@ -125,3 +125,10 @@
 - Limited scrolling code to explicit hash navigation and stored-list-position restoration.
 - Changed calendar integration to use the plugin's documented `data-current-date` and `data-jcs-*` attributes instead of text heuristics.
 - Removed obsolete layout variables from the Tetris integration.
+## 0.16.0-beta.4
+
+- Opus/Works/Scheduleの年号移動を、各年セクションの見出しを正式なアンカーとして統一。
+- `scrollIntoView()`による固定ヘッダー下への潜り込みを廃止。
+- 共通の`activateTarget()`が、実際のstickyヘッダー高を一度だけ差し引いて位置を決定。
+- 年号クリック、URLハッシュ、ブラウザ履歴復帰を同じアンカー処理へ統合。
+
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.4/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.3' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.4' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedinc/view-shell.php
--- previous/inc/view-shell.php
+++ 0.16.0-beta.4/inc/view-shell.php
@@ -172,7 +172,7 @@
 	ob_start();
 	?>
 	<div class="content-shell opus-page focus-page" data-focus-page data-view-root="opus" data-view-list-url="<?php echo esc_url( ho_view_url( 'opus' ) ); ?>"<?php if ( $initial_id ) : ?> data-initial-focus-type="work" data-initial-focus-id="<?php echo esc_attr( $initial_id ); ?>"<?php endif; ?>><h1 class="screen-reader-text">Opus</h1><div class="module-archive work-list" data-focus-list>
-	<?php if ( $query->have_posts() ) : $shown_year = ''; while ( $query->have_posts() ) : $query->the_post(); $source = ho_work_value( 'released' ) ?: ho_work_value( 'composed' ); preg_match( '/(?:19|20)\d{2}/', (string) $source, $match ); $year = isset( $match[0] ) ? $match[0] : 'その他'; if ( $year !== $shown_year ) : if ( $shown_year ) : ?></section><?php endif; $shown_year = $year; ?><section class="module-section year-section work-year" id="opus-<?php echo esc_attr( $year ); ?>" data-context-section data-context-key="<?php echo esc_attr( $year ); ?>"><h2 class="year-section__title"><?php echo esc_html( $year ); ?></h2><?php endif; ho_work_row(); endwhile; if ( $shown_year ) : ?></section><?php endif; wp_reset_postdata(); else : ?><p>公開中の作品はありません。</p><?php endif; ?>
+	<?php if ( $query->have_posts() ) : $shown_year = ''; while ( $query->have_posts() ) : $query->the_post(); $source = ho_work_value( 'released' ) ?: ho_work_value( 'composed' ); preg_match( '/(?:19|20)\d{2}/', (string) $source, $match ); $year = isset( $match[0] ) ? $match[0] : 'その他'; if ( $year !== $shown_year ) : if ( $shown_year ) : ?></section><?php endif; $shown_year = $year; ?><section class="module-section year-section work-year" id="opus-<?php echo esc_attr( $year ); ?>" data-context-section data-context-key="<?php echo esc_attr( $year ); ?>"><h2 class="year-section__title" data-context-anchor><?php echo esc_html( $year ); ?></h2><?php endif; ho_work_row(); endwhile; if ( $shown_year ) : ?></section><?php endif; wp_reset_postdata(); else : ?><p>公開中の作品はありません。</p><?php endif; ?>
 	</div></div>
 	<?php
 	return trim( ob_get_clean() );
@@ -183,7 +183,7 @@
 	ob_start();
 	?>
 	<div class="content-shell schedule-page focus-page" data-focus-page data-view-root="schedule" data-view-list-url="<?php echo esc_url( ho_view_url( 'schedule' ) ); ?>"<?php if ( $initial_id ) : ?> data-initial-focus-type="event" data-initial-focus-id="<?php echo esc_attr( $initial_id ); ?>"<?php endif; ?>><h1 class="screen-reader-text">Schedule</h1><div class="module-archive event-list" data-focus-list>
-	<?php if ( $query->have_posts() ) : $shown_year = ''; while ( $query->have_posts() ) : $query->the_post(); $year = substr( (string) ho_event_value( 'date' ), 0, 4 ); if ( $year !== $shown_year ) : if ( $shown_year ) : ?></section><?php endif; $shown_year = $year; ?><section class="module-section year-section event-year" id="schedule-<?php echo esc_attr( $year ); ?>" data-context-section data-context-key="<?php echo esc_attr( $year ); ?>"><h2 class="year-section__title"><?php echo esc_html( $year ); ?></h2><?php endif; ho_event_card(); endwhile; if ( $shown_year ) : ?></section><?php endif; wp_reset_postdata(); else : ?><p>公開中のスケジュールはありません。</p><?php endif; ?>
+	<?php if ( $query->have_posts() ) : $shown_year = ''; while ( $query->have_posts() ) : $query->the_post(); $year = substr( (string) ho_event_value( 'date' ), 0, 4 ); if ( $year !== $shown_year ) : if ( $shown_year ) : ?></section><?php endif; $shown_year = $year; ?><section class="module-section year-section event-year" id="schedule-<?php echo esc_attr( $year ); ?>" data-context-section data-context-key="<?php echo esc_attr( $year ); ?>"><h2 class="year-section__title" data-context-anchor><?php echo esc_html( $year ); ?></h2><?php endif; ho_event_card(); endwhile; if ( $shown_year ) : ?></section><?php endif; wp_reset_postdata(); else : ?><p>公開中のスケジュールはありません。</p><?php endif; ?>
 	</div></div>
 	<?php
 	return trim( ob_get_clean() );
@@ -205,7 +205,7 @@
 	ob_start();
 	?>
 	<article class="content-shell works-page focus-page" data-focus-page data-view-root="works" data-view-list-url="<?php echo esc_url( ho_view_url( 'works' ) ); ?>"<?php if ( $initial_id ) : ?> data-initial-focus-type="credit" data-initial-focus-id="<?php echo esc_attr( $initial_id ); ?>"<?php endif; ?>><h1 class="screen-reader-text">Works</h1><div class="module-archive works-list" data-focus-list>
-	<?php $shown_year = ''; foreach ( $query->posts as $credit ) : $year = get_post_meta( $credit->ID, '_ho_credit_year', true ) ?: 'Other'; if ( $shown_year !== $year ) : if ( $shown_year ) : ?></section><?php endif; $shown_year = $year; ?><section class="module-section year-section works-year" id="works-<?php echo esc_attr( $year ); ?>" data-context-section data-context-key="<?php echo esc_attr( $year ); ?>"><h2 class="year-section__title"><?php echo esc_html( $year ); ?></h2><?php endif; ho_credit_row( $credit->ID ); endforeach; if ( $shown_year ) : ?></section><?php endif; wp_reset_postdata(); ?>
+	<?php $shown_year = ''; foreach ( $query->posts as $credit ) : $year = get_post_meta( $credit->ID, '_ho_credit_year', true ) ?: 'Other'; if ( $shown_year !== $year ) : if ( $shown_year ) : ?></section><?php endif; $shown_year = $year; ?><section class="module-section year-section works-year" id="works-<?php echo esc_attr( $year ); ?>" data-context-section data-context-key="<?php echo esc_attr( $year ); ?>"><h2 class="year-section__title" data-context-anchor><?php echo esc_html( $year ); ?></h2><?php endif; ho_credit_row( $credit->ID ); endforeach; if ( $shown_year ) : ?></section><?php endif; wp_reset_postdata(); ?>
 	</div></article>
 	<?php
 	return trim( ob_get_clean() );
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.4/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.3
+Version: 0.16.0-beta.4
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

ヘッダー高だけを基準にし、フッターやモバイルdockを含む実表示領域全体は未統合だった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.4.zip


再利用すべき判断基準

  • 成功判定はPC/スマートフォンの実機確認後に行い、未確認を「成功」と記録しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.3

当時の改修目的

補正用padding、PC/スマホ二重ナビDOM、フォーカスDOM移動復元を廃止し、単一構造にする。

実装した変更

・Rebuilt the top layout as one sticky document-flow stack; removed content-offset padding and measured header constants.

・Replaced duplicate desktop/mobile context navigation DOM with one responsive context slot.

・Rebuilt single-module focus mode to render a clone while keeping the archive DOM untouched.

・Removed legacy focus-transition classes, DOM move/restore calculations, and scaled animation timing helpers.

・Limited scrolling code to explicit hash navigation and stored-list-position restoration.

・Changed calendar integration to use the plugin's documented `data-current-date` and `data-jcs-*` attributes instead of text heuristics.

・Removed obsolete layout variables from the Tetris integration.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.4以降でも撤去されず残った仕組みは継続採用とみなす。

stickyな通常フロー、単一context slot、clone式focus、旧遷移クラス・移動復元計算削除、Calendar属性参照へ変更。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.4 の要望・修正内容と照合して確定。

年号アンカーが固定ヘッダー下へ潜る問題が残った。

次版で実際に求められたこと:年号移動を正式な年見出しアンカーへ統一し、固定ヘッダー下への潜り込みを防ぐ。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/tetris.css, assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, footer.php, functions.php, header.php, style.css

changedassets/css/tetris.css
--- previous/assets/css/tetris.css
+++ 0.16.0-beta.3/assets/css/tetris.css
@@ -60,7 +60,7 @@
       display:grid;
       height:100%;
       margin:0 auto;
-      width:min(1100px,calc((100dvh - var(--layout-top) - var(--footer-height) - 80px)/2 + 250px),calc(100vw - 20px));
+      width:min(1100px,calc((var(--viewport-content-height) - 80px)/2 + 250px),calc(100vw - 20px));
     }
     #tetris-doodle-root .row{ display:grid; grid-template-columns:minmax(0,1fr); height:100%; min-height:0; align-items:stretch; }
 
changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.3/assets/css/theme.css
@@ -13,10 +13,13 @@
   --ink: #202124;
   --muted: #868686;
   --page: 1280px;
-  --site-header-height: 212.40625px;
-  --desktop-context-height: 52px;
+  --brand-height: 120px;
+  --ticker-height: 35px;
+  --primary-nav-height: 58px;
+  --context-height: 52px;
   --mobile-context-height: 56px;
-  --layout-top: calc(var(--site-header-height) + var(--desktop-context-height));
+  --desktop-top-stack-height: calc(var(--brand-height) + var(--ticker-height) + var(--primary-nav-height) + var(--context-height));
+  --mobile-top-stack-height: 149px;
   --content-edge-gap: 20px;
   --page-top-clearance: var(--content-edge-gap);
   --content-top: 12px;
@@ -32,7 +35,7 @@
   --mobile-nav-height: 48px;
   --mobile-credit-height: 16px;
   --mobile-dock-height: 64px;
-  --viewport-content-top: var(--layout-top);
+  --viewport-content-top: var(--desktop-top-stack-height);
   --viewport-content-bottom: calc(100dvh - var(--footer-height));
   --viewport-content-height: calc(var(--viewport-content-bottom) - var(--viewport-content-top));
   --view-fade-duration: .86s;
@@ -49,7 +52,7 @@
 }
 
 * { box-sizing: border-box; }
-html { -webkit-tap-highlight-color:transparent; scroll-padding-top:calc(var(--layout-top) + var(--page-top-clearance)); }
+html { -webkit-tap-highlight-color:transparent; scroll-padding-top:calc(var(--desktop-top-stack-height) + var(--page-top-clearance)); }
 body {
   background: var(--surface);
   color: var(--ink);
@@ -72,7 +75,7 @@
   overflow:hidden;
   position:fixed;
   right:0;
-  top:calc(var(--layout-top) + var(--page-top-clearance));
+  top:calc(var(--desktop-top-stack-height) + var(--page-top-clearance));
   touch-action:none;
 }
 a { color: inherit; }
@@ -82,23 +85,21 @@
 .skip-link:focus { top:8px; }
 .screen-reader-text { clip:rect(1px,1px,1px,1px); clip-path:inset(50%); height:1px; overflow:hidden; position:absolute!important; white-space:nowrap; width:1px; }
 
-.site-header {
-  background: var(--surface);
+.site-top-stack {
+  background:var(--surface);
   box-shadow:0 7px 18px rgba(163,177,198,.34);
-  left: 0;
-  position: fixed;
-  right: 0;
-  top: 0;
-  z-index: 9990;
-}
-.admin-bar .site-header { top: 32px; }
-.site-main { padding-top:calc(var(--layout-top) + var(--page-top-clearance)); }
-[data-context-section], [data-focus-item] { scroll-margin-top:calc(var(--layout-top) + 8px); }
-.desktop-context-slot { height:var(--desktop-context-height); overflow:visible; }
-.desktop-context-slot:empty { height:0; }
-.mobile-context-slot { display:none; }
-html[data-has-context="false"] { --desktop-context-height:0px; }
-.brand { background:var(--surface); height:119.40625px; position:relative; }
+  position:sticky;
+  top:0;
+  z-index:9990;
+}
+.admin-bar .site-top-stack { top:32px; }
+.site-header { background:var(--surface); }
+.site-main { min-height:1px; }
+[data-context-section], [data-focus-item] { scroll-margin-top:calc(var(--desktop-top-stack-height) + 8px); }
+.context-slot { height:var(--context-height); overflow:visible; }
+.context-slot:empty { height:0; }
+html[data-has-context="false"] { --context-height:0px; --desktop-top-stack-height:calc(var(--brand-height) + var(--ticker-height) + var(--primary-nav-height)); }
+.brand { background:var(--surface); height:var(--brand-height); position:relative; }
 .brand::after { background:rgba(117,128,146,.48); content:""; height:1px; left:10px; position:absolute; right:10px; top:79px; }
 .brand__name, .brand__role {
   font-family: "Archivo Black", "Arial Black", sans-serif;
@@ -112,7 +113,7 @@
   white-space: nowrap;
 }
 .brand__name { font-size:60px; height:60px; line-height:60px; top:19px; }
-.brand__role { color:#000; font-size:36px; height:50.40625px; line-height:50.4px; top:65px; }
+.brand__role { color:#000; font-size:36px; height:50px; line-height:50px; top:65px; }
 .brand__name-text,.brand__role-text { display:inline-flex; width:max-content; }
 .brand__name-character { display:inline-block; margin-right:-3px; }
 .brand__role-character { display:inline-block; margin-right:-2px; }
@@ -148,8 +149,7 @@
 @keyframes nav-glow { 0%,100% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 0 0 var(--glow); } 50% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 16px 5px var(--glow); } }
 
 
-.desktop-context-slot .context-nav { height:var(--desktop-context-height); padding-block:6px; }
-.desktop-context-slot .context-nav { box-shadow:0 7px 18px rgba(163,177,198,.34); }
+.context-slot .context-nav { height:var(--context-height); padding-block:6px; }
 
 .context-nav { --context-accent:var(--green); align-items:center; background:linear-gradient(var(--surface),rgba(237,241,247,.82)); display:grid; gap:10px; grid-template-columns:100px minmax(0,1fr) 100px; height:40px; overflow:visible; padding:0 12px; white-space:nowrap; }
 .context-nav--schedule { --context-accent:var(--yellow); }
@@ -256,9 +256,9 @@
 .event-artists__item { display:block; min-width:0; white-space:nowrap; }
 .event-artists__detail--many { gap:3px 16px; grid-template-columns:repeat(2,minmax(0,1fr)); }
 .event-artists__detail--many .event-artists__item--wide { grid-column:1 / -1; }
-.focus-page.is-focus .event-card.is-picked .event-artists__list { display:none; }
-.focus-page.is-focus .event-card.is-picked .event-artists__detail { display:block; }
-.focus-page.is-focus .event-card.is-picked .event-artists__detail--many { display:grid; }
+.focus-page.is-focus .focus-stage .event-card.is-picked .event-artists__list { display:none; }
+.focus-page.is-focus .focus-stage .event-card.is-picked .event-artists__detail { display:block; }
+.focus-page.is-focus .focus-stage .event-card.is-picked .event-artists__detail--many { display:grid; }
 .detail-list,.work-row__details { display:grid; gap:3px 20px; grid-template-columns:172px minmax(0,1fr); margin:0; }
 .detail-list dt,.detail-list dd,.work-row__details dt,.work-row__details dd { margin:0; }
 .detail-list dd,.work-row__details dd { background:rgba(245,248,251,.48); border:1px solid rgba(255,255,255,.58); border-radius:11px; box-shadow:inset 1px 1px 3px rgba(143,158,184,.2),inset -1px -1px 3px rgba(255,255,255,.72); font-weight:700; line-height:1.45; padding:3px 9px; }
@@ -330,8 +330,6 @@
   .profile-page .language-tabs { min-height:0; }
   .profile-page .language-tabs__panel { height:100%; overflow:auto; overscroll-behavior:contain; }
 }
-.focus-page.is-focus .event-card.is-picked.has-focus-details { overflow:visible; z-index:20; }
-.focus-page.is-focus .event-card.is-picked.has-focus-details .focus-item__extras-inner { overflow:visible; }
 .event-skull-parade { align-items:end; display:grid; gap:clamp(4px,1.5vw,18px); grid-template-columns:repeat(var(--skull-count),minmax(0,1fr)); isolation:isolate; margin:0 auto; max-width:var(--skull-parade-max-width); min-height:clamp(92px,14vw,170px); overflow:visible; padding:12px clamp(6px,2vw,20px) 6px; position:relative; width:100%; }
 .focus-parade-layer { bottom:calc(100dvh - var(--viewport-content-bottom) + 8px); left:50%; max-width:calc(100vw - 28px); pointer-events:none; position:fixed; transform:translateX(-50%); width:min(calc(100vw - 28px),var(--skull-parade-max-width)); z-index:9988; }
 .focus-parade-layer:empty { display:none; }
@@ -373,7 +371,7 @@
 .event-focus__note { background:rgba(245,248,251,.48); border:1px solid rgba(255,255,255,.58); border-radius:11px; box-shadow:inset 1px 1px 3px rgba(143,158,184,.2),inset -1px -1px 3px rgba(255,255,255,.72); font-size:16px; font-weight:700; line-height:1.45; padding:12px 14px; }
 .event-focus__note p { margin:0 0 1em; }
 .event-focus__note p:last-child { margin-bottom:0; }
-.focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--layout-top); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
+.focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--desktop-top-stack-height); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
 .focus-return-control svg { fill:none; height:28px; overflow:visible; stroke:currentColor; stroke-linecap:round; stroke-linejoin:round; stroke-width:3.4; width:28px; }
 .focus-page.is-focus .focus-return-control { opacity:1; pointer-events:auto; transform:translateY(0); }
 .focus-return-control:active { box-shadow:var(--neu-inset); transform:translateY(1px); }
@@ -385,12 +383,8 @@
 .focus-stage[hidden] { display:none; }
 .focus-page.is-focus [data-focus-list] { display:none; }
 .focus-stage > [data-focus-item] { margin:0; }
-.focus-page [data-focus-item].is-focus-moving { pointer-events:none; will-change:transform; z-index:30; }
-.focus-page.is-focus-closing [data-focus-item].is-picked { pointer-events:none; }
-.focus-page .module-section,.focus-page .year-section__title,.focus-page [data-focus-item] { transition:opacity var(--view-fade-duration) ease-in-out; }
-.focus-page:is(.is-focus-leaving,.is-focus-returning) .module-section:not(.is-picked-section),
-.focus-page:is(.is-focus-leaving,.is-focus-returning) .is-picked-section > .year-section__title,
-.focus-page:is(.is-focus-leaving,.is-focus-returning) .is-picked-section > [data-focus-item]:not(.is-picked) { opacity:0; pointer-events:none; }
+.focus-stage .is-picked.has-focus-details { overflow:visible; position:relative; z-index:20; }
+.focus-stage .is-picked.has-focus-details .focus-item__extras-inner { overflow:visible; }
 .focus-panel__back { margin:0; }
 .prose { font-size:17px; }
 .prose p { margin:0 0 1.2em; }
@@ -434,7 +428,7 @@
   overflow:hidden;
   position:fixed;
   right:0;
-  top:calc(var(--layout-top) + var(--page-top-clearance));
+  top:calc(var(--desktop-top-stack-height) + var(--page-top-clearance));
 }
 .home-stage__album-link { align-items:center; color:var(--red); display:flex; flex-direction:column; font-weight:900; left:20%; position:absolute; text-decoration:none; top:20px; width:154px; z-index:2; }
 .home-stage__album-link img { animation:home-skull 2.4s ease-in-out infinite; width:148px; }
@@ -467,7 +461,7 @@
 @media (min-width: 768px) {
   .link-row__button { font-size:clamp(14px,1.25vw,16px); white-space:nowrap; }
   .home-stage {
-    --home-head-position-unit:min(7.708333vw,calc((100dvh - var(--layout-top) - var(--footer-height) - 64px) / 5));
+    --home-head-position-unit:min(7.708333vw,calc((100dvh - var(--desktop-top-stack-height) - var(--footer-height) - 64px) / 5));
     --home-head-size:clamp(224px,15.416667vw,296px);
     background-position:center center;
     background-size:cover;
@@ -494,12 +488,11 @@
 }
 
 @media (max-width: 767px) {
-  :root { --site-header-height:149px; --desktop-context-height:0px; --layout-top:var(--site-header-height); --page-top-clearance:8px; --viewport-content-top:var(--layout-top); --viewport-content-bottom:calc(100dvh - var(--mobile-dock-height) - var(--mobile-context-height)); --viewport-content-height:calc(var(--viewport-content-bottom) - var(--viewport-content-top)); --content-top:4px; --content-inline:14px; --content-bottom:calc(var(--mobile-dock-height) + var(--mobile-context-height) + 6px); --module-gap:22px; --module-section-gap:28px; --module-padding:14px; --module-radius:20px; --record-shell-inline:14px; }
+  :root { --page-top-clearance:8px; --viewport-content-top:var(--mobile-top-stack-height); --viewport-content-bottom:calc(100dvh - var(--mobile-dock-height) - var(--mobile-context-height)); --viewport-content-height:calc(var(--viewport-content-bottom) - var(--viewport-content-top)); --content-top:4px; --content-inline:14px; --content-bottom:calc(var(--mobile-dock-height) + var(--mobile-context-height) + 6px); --module-gap:22px; --module-section-gap:28px; --module-padding:14px; --module-radius:20px; --record-shell-inline:14px; }
   body { padding-bottom:calc(var(--mobile-dock-height) + var(--mobile-context-height)); }
   html[data-has-context="false"] body { padding-bottom:var(--mobile-dock-height); }
-  .site-main { padding-top:calc(var(--layout-top) + var(--page-top-clearance)); }
   html[data-current-view="tetris"] .tetris-page { bottom:var(--mobile-dock-height); }
-  .admin-bar .site-header { top:46px; }
+  .admin-bar .site-top-stack { top:46px; }
   .brand { height:79px; }
   .brand::after { top:52px; }
   .brand__name { font-size:32px; height:32px; line-height:32px; top:19px; }
@@ -508,8 +501,7 @@
   .news-ticker { height:32px; }
   .site-nav { gap:20px; height:38px; padding:5px 15px; }
   .site-nav__link { border-radius:500px; font-family:"Archivo Black",sans-serif; font-size:14px; height:28px; padding:0 3px; }
-  .desktop-context-slot { display:none; }
-  .context-nav { display:none; }
+
   .content-shell { padding:var(--content-top) var(--content-inline) var(--content-bottom); }
   .page-title,.calendar-link { font-size:28px; }
   .event-card { display:block; }
@@ -537,8 +529,8 @@
   .site-footer { display:none; }
   .mobile-dock { bottom:0; display:block; left:0; position:fixed; right:0; z-index:9999; }
   :root.is-web-app .mobile-dock { background:rgba(119,128,143,.94); padding-bottom:env(safe-area-inset-bottom, 0px); }
-  .mobile-context-slot { bottom:var(--mobile-dock-height); display:block; height:var(--mobile-context-height); left:0; position:fixed; right:0; z-index:9998; }
-  .mobile-context-slot:empty { display:none; }
+  .context-slot { bottom:var(--mobile-dock-height); height:var(--mobile-context-height); left:0; position:fixed; right:0; z-index:9998; }
+  .context-slot:empty { display:none; }
   .context-nav { background:rgba(237,241,247,.97); bottom:auto; box-shadow:0 -5px 14px rgba(163,177,198,.5); display:grid; gap:0; grid-template-columns:20% minmax(0,60%) 20%; height:56px; left:auto; overflow:hidden; padding:0; position:relative; right:auto; z-index:auto; }
   .context-nav__section-link { align-items:center; align-self:stretch; background:linear-gradient(90deg,#fff,var(--surface)); border-radius:16px; display:flex; justify-content:center; padding:0 5px; position:relative; z-index:2; }
   .context-nav__section-link:last-child { background:linear-gradient(90deg,var(--surface),#fff); }
@@ -563,7 +555,7 @@
   html:not([data-current-view="home"]) .home-dog,html:not([data-current-view="home"]) .home-runner-track { display:none; }
   .home-dog,.home-runner-track { bottom:var(--mobile-dock-height); }
   :root.is-web-app[data-current-view="home"] .home-dog { display:block !important; opacity:1; visibility:visible; }
-  .admin-bar .home-stage { top:calc(var(--layout-top) + var(--page-top-clearance)); }
+  .admin-bar .home-stage { top:calc(var(--mobile-top-stack-height) + var(--page-top-clearance)); }
   .focus-parade-layer {
     bottom:calc(100dvh - var(--viewport-content-bottom) + 8px);
     max-width:calc(100vw - 16px);
@@ -578,7 +570,7 @@
   .event-skull-parade__slot img { --skull-visual-scale:1.34; }
   .focus-return-control {
     right:12px;
-    top:var(--layout-top);
+    top:var(--mobile-top-stack-height);
   }
 }
 
@@ -609,6 +601,13 @@
   .keep-phrase { white-space:normal; }
 }
 
+@media (max-width: 767px) {
+  html { scroll-padding-top:calc(var(--mobile-top-stack-height) + 8px); }
+  [data-context-section], [data-focus-item] { scroll-margin-top:calc(var(--mobile-top-stack-height) + 8px); }
+  .site-top-stack { top:0; }
+  .admin-bar .site-top-stack { top:46px; }
+}
+
 
 .calendar-dissolve-overlay {
   overflow:hidden;
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.3/assets/js/theme.js
@@ -6,13 +6,9 @@
   var moduleDetailCache = new Map();
   var targetActivation = 0;
   var lastLinkThanksAnimation = -1;
-  var motionTimeScale = 2;
   var webAppQuery = window.matchMedia('(display-mode: standalone), (display-mode: fullscreen), (display-mode: minimal-ui)');
   var viewFadeDuration = 860;
 
-  function motionTime(milliseconds) {
-    return milliseconds * motionTimeScale;
-  }
 
   if ('scrollRestoration' in window.history) window.history.scrollRestoration = 'manual';
 
@@ -44,29 +40,17 @@
     root.classList.toggle('is-web-app', webAppQuery.matches || window.navigator.standalone === true);
   }
 
-  function contentTop() {
-    return parseFloat(window.getComputedStyle(root).getPropertyValue('--layout-top')) || 0;
-  }
-
-  function scrollElementToContentTop(target) {
-    if (!target) return;
-    var destination = window.scrollY + target.getBoundingClientRect().top - contentTop() - 8;
-    window.scrollTo({ top: Math.max(0, destination), left: 0, behavior: 'auto' });
-  }
-
-  function scrollTargetBelowHeader(target) {
-    scrollElementToContentTop(target);
+  function stickyOffset() {
+    var stack = document.getElementById('site-top-stack');
+    return stack && window.getComputedStyle(stack).position === 'sticky' ? stack.getBoundingClientRect().height : 0;
   }
 
   function activateTarget(target) {
     var activation = ++targetActivation;
     if (!target) return;
-    if (window.getComputedStyle(target).position === 'fixed') {
-      window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
-      return;
-    }
     window.requestAnimationFrame(function () {
-      if (activation === targetActivation && document.documentElement.contains(target)) scrollElementToContentTop(target);
+      if (activation !== targetActivation || !document.documentElement.contains(target)) return;
+      target.scrollIntoView({ block: 'start', behavior: 'auto' });
     });
   }
 
@@ -79,25 +63,30 @@
     activateTarget(document.getElementById(hash));
   }
 
+  function contextSlot() {
+    return document.querySelector('[data-context-slot]');
+  }
+
   function contextSlots() {
-    return Array.from(document.querySelectorAll('[data-context-slot]'));
+    var slot = contextSlot();
+    return slot ? [slot] : [];
   }
 
   function contextHtml() {
-    var slot = contextSlots()[0];
+    var slot = contextSlot();
     return slot ? slot.innerHTML : '';
   }
 
   function setContextHtml(html) {
+    var slot = contextSlot();
     var value = html || '';
-    contextSlots().forEach(function (slot) { slot.innerHTML = value; });
+    if (slot) slot.innerHTML = value;
     root.setAttribute('data-has-context', value.trim() ? 'true' : 'false');
   }
 
   function visibleContext() {
-    return contextSlots().map(function (slot) { return slot.firstElementChild; }).find(function (node) {
-      return node && window.getComputedStyle(node).display !== 'none';
-    }) || null;
+    var slot = contextSlot();
+    return slot ? slot.firstElementChild : null;
   }
 
   function setupTabs() {
@@ -153,34 +142,10 @@
   }
 
   function calendarDateFromRoot(calendarRoot) {
-    var candidates = [
-      calendarRoot.getAttribute('data-current-date'),
-      calendarRoot.getAttribute('data-date'),
-      calendarRoot.getAttribute('data-month'),
-      calendarRoot.textContent
-    ].filter(Boolean);
-    for (var index = 0; index < candidates.length; index += 1) {
-      var match = String(candidates[index]).match(/((?:19|20)\d{2})\D{0,6}(1[0-2]|0?[1-9])/);
-      if (match) return { year: Number(match[1]), month: Number(match[2]) };
-    }
-    var now = new Date();
-    return { year: now.getFullYear(), month: now.getMonth() + 1 };
-  }
-
-  function calendarNavButtons(calendarRoot) {
-    var buttons = Array.from(calendarRoot.querySelectorAll('.jaikki-calendar__nav, button, [role="button"]'));
-    var previous = null;
-    var next = null;
-    buttons.forEach(function (button) {
-      var label = [button.getAttribute('aria-label'), button.getAttribute('title'), button.textContent].filter(Boolean).join(' ').toLowerCase();
-      if (!previous && /(prev|previous|前|戻|‹|←)/.test(label)) previous = button;
-      if (!next && /(next|次|進|›|→)/.test(label)) next = button;
-    });
-    if ((!previous || !next) && buttons.length >= 2) {
-      previous = previous || buttons[0];
-      next = next || buttons[buttons.length - 1];
-    }
-    return { previous: previous, next: next };
+    var value = calendarRoot.getAttribute('data-current-date') || '';
+    var match = value.match(/^((?:19|20)\d{2})-(1[0-2]|0[1-9])$/);
+    if (!match) return null;
+    return { year: Number(match[1]), month: Number(match[2]) };
   }
 
   function navigateCalendarTo(year, month) {
@@ -195,7 +160,7 @@
     calendarRoot.dispatchEvent(event);
     if (event.defaultPrevented) return true;
 
-    var directTarget = calendarRoot.querySelector('[data-year="' + targetYear + '"][data-month="' + targetMonth + '"], [data-date="' + targetYear + '-' + String(targetMonth).padStart(2, '0') + '"]');
+    var directTarget = calendarRoot.querySelector('[data-jcs-year="' + targetYear + '"][data-jcs-month="' + targetMonth + '"]');
     if (directTarget) {
       directTarget.click();
       return true;
@@ -229,6 +194,7 @@
 
     function shiftMonth(offset) {
       var current = calendarDateFromRoot(calendarRoot);
+      if (!current) return;
       var target = new Date(current.year, current.month - 1 + offset, 1);
       navigateCalendarTo(target.getFullYear(), target.getMonth());
     }
@@ -316,7 +282,7 @@
           return a.boundingClientRect.top - b.boundingClientRect.top;
         })[0];
         if (visible) select(visible.target.getAttribute('data-context-key'));
-      }, { rootMargin: '-' + Math.round(contentTop() + 8) + 'px 0px -70% 0px', threshold: 0 });
+      }, { rootMargin: '-' + Math.round(stickyOffset() + 8) + 'px 0px -70% 0px', threshold: 0 });
       sections.forEach(function (section) { observer.observe(section); });
       registerViewCleanup(function () { observer.disconnect(); });
     }
@@ -353,8 +319,8 @@
         skull.classList.add('event-skull-motion--' + Math.floor(Math.random() * 10));
         skull.style.setProperty('--skull-from-x', (fromRight ? distance : -distance) + 'px');
         skull.style.setProperty('--skull-spin', (fromRight ? -turns : turns) + 'deg');
-        skull.style.setProperty('--skull-delay', ((index * 0.09 + Math.random() * 0.22) * motionTimeScale).toFixed(2) + 's');
-        skull.style.setProperty('--skull-duration', ((1.25 + Math.random() * 1.05) * motionTimeScale).toFixed(2) + 's');
+        skull.style.setProperty('--skull-delay', (index * 0.18 + Math.random() * 0.44).toFixed(2) + 's');
+        skull.style.setProperty('--skull-duration', (2.5 + Math.random() * 2.1).toFixed(2) + 's');
       });
       window.requestAnimationFrame(function () { parade.classList.add('is-rolling'); });
     });
@@ -389,39 +355,31 @@
     if (!page) return;
     var list = page.querySelector('[data-focus-list]');
     if (!list) return;
+
     var stage = document.createElement('div');
     stage.className = 'focus-stage';
     stage.setAttribute('data-focus-stage', '');
     stage.hidden = true;
     page.insertBefore(stage, list);
-    var supportsFloatingReturn = ['opus', 'works', 'schedule'].indexOf(page.getAttribute('data-view-root')) !== -1;
-    var returnControl = null;
-    if (supportsFloatingReturn) {
-      returnControl = document.createElement('button');
-      returnControl.className = 'focus-return-control';
-      returnControl.type = 'button';
-      returnControl.setAttribute('aria-label', '一覧へ戻る');
-      returnControl.setAttribute('data-focus-close', '');
-      returnControl.innerHTML = '<svg viewBox="0 0 32 32" aria-hidden="true" focusable="false"><path d="M25 5v8c0 5.8-3.7 9-9.5 9H7"/><path d="M13 15.5 6.5 22 13 28.5"/></svg>';
-      page.appendChild(returnControl);
-    }
+
+    var returnControl = document.createElement('button');
+    returnControl.className = 'focus-return-control';
+    returnControl.type = 'button';
+    returnControl.setAttribute('aria-label', '一覧へ戻る');
+    returnControl.setAttribute('data-focus-close', '');
+    returnControl.innerHTML = '<svg viewBox="0 0 32 32" aria-hidden="true" focusable="false"><path d="M25 5v8c0 5.8-3.7 9-9.5 9H7"/><path d="M13 15.5 6.5 22 13 28.5"/></svg>';
+    page.appendChild(returnControl);
+
     var paradeLayer = document.createElement('div');
     paradeLayer.className = 'focus-parade-layer';
     paradeLayer.setAttribute('aria-hidden', 'true');
     document.body.appendChild(paradeLayer);
-    var activeItem = null;
-    var activeSection = null;
-    var originParent = null;
-    var originNextSibling = null;
-    var listUrl = window.location.href;
+
+    var activeSource = null;
+    var activeClone = null;
+    var listUrl = archiveUrl();
     var archiveTitle = document.title;
     var savedListScroll = 0;
-    var transitionId = 0;
-    var reduceFocusMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
-    var onArtistResize = function () { if (activeItem) layoutEventArtists(activeItem); };
-    window.addEventListener('resize', onArtistResize, { passive: true });
-    registerViewCleanup(function () { window.removeEventListener('resize', onArtistResize); });
-
 
     function clearParadeLayer() {
       resetEventSkullParades(paradeLayer);
@@ -437,45 +395,52 @@
       setupEventSkullParades(paradeLayer);
     }
 
+    function archiveUrl() {
+      var listBase = page.getAttribute('data-view-list-url');
+      if (listBase) return listBase;
+      var url = new URL(window.location.href);
+      url.searchParams.delete('focus');
+      url.searchParams.delete('item');
+      url.searchParams.delete('event');
+      url.hash = '';
+      return url.href;
+    }
+
+    function sanitizeClone(clone) {
+      clone.removeAttribute('id');
+      clone.querySelectorAll('[id]').forEach(function (node) { node.removeAttribute('id'); });
+      clone.querySelectorAll('[data-focus-link]').forEach(function (link) {
+        link.removeAttribute('data-focus-link');
+        link.setAttribute('tabindex', '-1');
+      });
+      return clone;
+    }
+
     function loadDetails(item, type, itemId) {
       var slot = item.querySelector('[data-focus-details]');
       if (!slot || !window.fetch) return;
       var cacheKey = type + ':' + itemId;
-      var reveal = function (html) {
-        if (!document.documentElement.contains(item)) return;
-        if (slot.getAttribute('data-details-loaded') !== 'true') {
-          var details = document.createElement('div');
-          details.className = 'focus-item__extras-inner';
-          details.innerHTML = html || '';
-          var paradeTemplate = details.querySelector('[data-event-skull-parade]');
-          if (paradeTemplate) slot.setAttribute('data-parade-html', paradeTemplate.outerHTML);
-          renderParadeFromDetails(details);
-          slot.replaceChildren(details);
-          slot.setAttribute('data-details-loaded', 'true');
-        } else {
-          clearParadeLayer();
-          var paradeHtml = slot.getAttribute('data-parade-html');
-          if (paradeHtml) {
-            var holder = document.createElement('div');
-            holder.innerHTML = paradeHtml;
-            renderParadeFromDetails(holder);
-          }
-        }
+
+      function reveal(html) {
+        if (activeClone !== item || !document.documentElement.contains(item)) return;
+        var details = document.createElement('div');
+        details.className = 'focus-item__extras-inner';
+        details.innerHTML = html || '';
+        renderParadeFromDetails(details);
+        slot.replaceChildren(details);
         slot.removeAttribute('aria-busy');
         item.classList.remove('is-expanding');
-        window.requestAnimationFrame(function () {
-          if (activeItem !== item || page.classList.contains('is-focus-closing')) return;
-          item.classList.add('has-focus-details');
-          layoutEventArtists(item);
-
-        });
-      };
+        item.classList.add('has-focus-details');
+        layoutEventArtists(item);
+      }
+
       item.classList.add('is-expanding');
       slot.setAttribute('aria-busy', 'true');
       if (moduleDetailCache.has(cacheKey)) {
         reveal(moduleDetailCache.get(cacheKey));
         return;
       }
+
       var endpoint = window.location.origin + '/wp-json/ho/v1/details/' + encodeURIComponent(type) + '/' + encodeURIComponent(itemId);
       window.fetch(endpoint, { credentials: 'same-origin' }).then(function (response) {
         if (!response.ok) throw new Error('Module details request failed');
@@ -485,76 +450,47 @@
         moduleDetailCache.set(cacheKey, html);
         reveal(html);
       }).catch(function () {
-        if (!document.documentElement.contains(item)) return;
+        if (activeClone !== item) return;
         slot.removeAttribute('aria-busy');
         item.classList.remove('is-expanding');
       });
     }
 
-    function archiveUrl() {
-      var listBase = page.getAttribute('data-view-list-url');
-      if (listBase) return listBase;
-      var url = new URL(window.location.href);
-      url.searchParams.delete('focus');
-      url.searchParams.delete('item');
-      url.searchParams.delete('event');
-      url.hash = '';
-      return url.href;
-    }
-
     function closeItem() {
-      if (!activeItem || page.classList.contains('is-focus-closing')) return;
-      var restoredItem = activeItem;
-      var restoredSection = activeSection;
-      page.classList.add('is-focus-closing');
+      if (!activeClone) return;
       clearParadeLayer();
-      restoredItem.classList.remove('is-expanding', 'has-focus-details');
-
-      originParent.insertBefore(restoredItem, originNextSibling);
+      stage.replaceChildren();
       stage.hidden = true;
-      page.classList.remove('is-focus', 'is-focus-leaving', 'is-focus-closing', 'is-focus-returning');
+      list.hidden = false;
+      page.classList.remove('is-focus');
+      if (activeSource) activeSource.removeAttribute('aria-current');
+      activeSource = null;
+      activeClone = null;
+      document.title = archiveTitle;
       window.scrollTo({ top: savedListScroll, left: 0, behavior: 'auto' });
-
-      activeItem = null;
-      activeSection = null;
-      originParent = null;
-      originNextSibling = null;
-      document.title = archiveTitle;
-      restoredItem.classList.remove('is-picked', 'is-focus-moving');
-      restoredItem.removeAttribute('aria-current');
-      if (restoredSection) restoredSection.classList.remove('is-picked-section');
-    }
-
-    function openItem(item, link, initial) {
-      if (!item || !link || activeItem) return;
-      activeItem = item;
+    }
+
+    function openItem(source, link, initial) {
+      if (!source || !link || activeClone) return;
+      var type = source.getAttribute('data-focus-type');
+      var itemId = source.getAttribute('data-focus-id');
+      if (!type || !itemId) return;
+
+      activeSource = source;
+      activeSource.setAttribute('aria-current', 'true');
+      activeClone = sanitizeClone(source.cloneNode(true));
+      activeClone.classList.add('is-picked');
       savedListScroll = initial ? 0 : window.scrollY;
-      listUrl = archiveUrl() + '#' + item.id;
-      var type = item.getAttribute('data-focus-type');
-      var itemId = item.getAttribute('data-focus-id');
-      originParent = item.parentNode;
-      originNextSibling = item.nextSibling;
-      activeSection = item.closest('.module-section');
-      if (activeSection) activeSection.classList.add('is-picked-section');
-      item.classList.add('is-picked');
-      item.setAttribute('aria-current', 'true');
+      listUrl = archiveUrl() + (source.id ? '#' + source.id : '');
+
+      stage.replaceChildren(activeClone);
+      stage.hidden = false;
+      list.hidden = true;
+      page.classList.add('is-focus');
       replaceLocation(archiveUrl() + '#focus-' + type + '-' + itemId, { hoPartial: true, hoListFocus: true });
       document.title = link.textContent.trim() + ' – ' + archiveTitle;
-      var thisTransition = ++transitionId;
-      var finish = function () {
-        if (thisTransition !== transitionId || activeItem !== item) return;
-        stage.hidden = false;
-        stage.appendChild(item);
-        page.classList.remove('is-focus-leaving');
-        page.classList.add('is-focus');
-        window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
-        loadDetails(item, type, itemId);
-      };
-      if (initial || reduceFocusMotion) finish();
-      else {
-        page.classList.add('is-focus-leaving');
-        window.setTimeout(finish, motionTime(190));
-      }
+      window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
+      loadDetails(activeClone, type, itemId);
     }
 
     var onFocusClick = function (event) {
@@ -573,30 +509,28 @@
     };
     page.addEventListener('click', onFocusClick);
 
+    var onResize = function () { if (activeClone) layoutEventArtists(activeClone); };
+    window.addEventListener('resize', onResize, { passive: true });
+
     registerViewCleanup(listen(window, 'popstate', function () {
-      if (activeItem && !/^#focus-(event|work|credit)-[0-9]+$/.test(window.location.hash) && !new URL(window.location.href).searchParams.get('focus')) closeItem();
+      if (activeClone && !/^#focus-(event|work|credit)-[0-9]+$/.test(window.location.hash) && !new URL(window.location.href).searchParams.get('focus')) closeItem();
     }));
-    registerViewCleanup(function () {
-      transitionId += 1;
-      page.removeEventListener('click', onFocusClick);
-      if (activeItem && originParent) {
-        originParent.insertBefore(activeItem, originNextSibling);
-        activeItem.classList.remove('is-picked', 'is-expanding', 'has-focus-details', 'is-focus-moving');
-        activeItem.removeAttribute('aria-current');
-        if (activeSection) activeSection.classList.remove('is-picked-section');
-      }
-      clearParadeLayer();
-      if (returnControl) returnControl.remove();
-      paradeLayer.remove();
-      stage.remove();
-      page.classList.remove('is-focus', 'is-focus-leaving', 'is-focus-closing', 'is-focus-returning');
-    });
-
     registerViewCleanup(listen(document, 'ho:resetfocus', function () {
-      if (!activeItem) return;
+      if (!activeClone) return;
       replaceLocation(archiveUrl(), { hoPartial: true });
       closeItem();
     }));
+    registerViewCleanup(function () {
+      page.removeEventListener('click', onFocusClick);
+      window.removeEventListener('resize', onResize);
+      clearParadeLayer();
+      paradeLayer.remove();
+      returnControl.remove();
+      stage.remove();
+      list.hidden = false;
+      page.classList.remove('is-focus');
+      if (activeSource) activeSource.removeAttribute('aria-current');
+    });
 
     var initialUrl = new URL(window.location.href);
     var focusHash = initialUrl.hash.match(/^#focus-(event|work|credit)-([0-9]+)$/);
@@ -768,7 +702,7 @@
       });
     }
 
-    function captureView(view, main, contextSlot) {
+    function captureView(view, main) {
       var fragment = document.createDocumentFragment();
       while (main.firstChild) fragment.appendChild(main.firstChild);
       viewStates.set(view, { fragment: fragment, contextHtml: contextHtml(), title: document.title });
@@ -789,7 +723,6 @@
       targetActivation += 1;
       navigating = true;
       var main = document.querySelector('.site-main');
-      var contextSlot = contextSlots()[0];
       var fromView = currentView();
       var calendarDissolve = fromView === 'calendar' && targetView === 'calendar';
       var oldCalendarPage = calendarDissolve ? document.querySelector('.calendar-page') : null;
@@ -803,7 +736,7 @@
         var result = results[0];
         disposeView();
         if (fromView === targetView) main.replaceChildren();
-        else captureView(fromView, main, contextSlot);
+        else captureView(fromView, main);
         var contextHtml = '';
         var title = '';
         var assets = null;
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.3/docs/BETA1-CHANGELOG.md
@@ -115,3 +115,13 @@
 - Calendarの同一ページ内履歴(前月・次月・年移動)をブラウザバックで復元。
 - 重複していたProfile文脈ナビ条件を削除。
 - サービスアカウント方式の companion plugin に対応。
+
+## 0.16.0-beta.3
+
+- Rebuilt the top layout as one sticky document-flow stack; removed content-offset padding and measured header constants.
+- Replaced duplicate desktop/mobile context navigation DOM with one responsive context slot.
+- Rebuilt single-module focus mode to render a clone while keeping the archive DOM untouched.
+- Removed legacy focus-transition classes, DOM move/restore calculations, and scaled animation timing helpers.
+- Limited scrolling code to explicit hash navigation and stored-list-position restoration.
+- Changed calendar integration to use the plugin's documented `data-current-date` and `data-jcs-*` attributes instead of text heuristics.
+- Removed obsolete layout variables from the Tetris integration.
changedfooter.php
--- previous/footer.php
+++ 0.16.0-beta.3/footer.php
@@ -6,7 +6,6 @@
  */
 ?>
 </main>
-<div class="mobile-context-slot" data-context-slot="mobile"><?php echo ho_view_context_html( ho_current_view() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
 <img class="home-dog" src="<?php echo esc_url( get_template_directory_uri() . '/assets/img/8686_dog.webp' ); ?>" alt="" aria-hidden="true">
 <div class="home-runner-track" aria-hidden="true"><a href="<?php echo esc_url( ho_view_url( 'calendar' ) ); ?>" data-view-link="calendar" tabindex="-1"><img src="<?php echo esc_url( get_template_directory_uri() . '/assets/img/8686_skull-run.webp' ); ?>" alt=""></a></div>
 <footer class="site-footer">&copy;Skeleton Music Factory, Japan</footer>
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.3/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.2' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.3' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedheader.php
--- previous/header.php
+++ 0.16.0-beta.3/header.php
@@ -20,6 +20,7 @@
 <body <?php body_class( 'ho-site' ); ?>>
 <?php wp_body_open(); ?>
 <a class="skip-link" href="#main-content">本文へ移動</a>
+<div class="site-top-stack" id="site-top-stack">
 <header class="site-header" id="site-header">
 	<div class="brand">
 		<?php $brand_name = get_theme_mod( 'ho_brand_name', 'Hiroki Ohnuma' ); $brand_role = get_theme_mod( 'ho_role', 'composer' ); ?>
@@ -29,6 +30,7 @@
 	</div>
 	<?php ho_recent_posts_ticker(); ?>
 	<?php ho_render_primary_nav(); ?>
-	<div class="desktop-context-slot" data-context-slot="desktop"><?php echo $context_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
 </header>
+	<div class="context-slot" data-context-slot><?php echo $context_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
+</div>
 <main class="site-main" id="main-content">
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.3/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.2
+Version: 0.16.0-beta.3
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

DOM構造は整理されたが、スクロール位置決めがscrollIntoView依存だった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.3.zip


再利用すべき判断基準

  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.2

当時の改修目的

beta.1のヘッダー/ページ専用ナビ/可視領域の不具合を修正する。

実装した変更

・Removed automatic first-content scrolling on ordinary page loads. Pages now start at document top, so the fixed header stack and the main-content padding define the start position exactly once.

・Replaced the CSS `:has()` context-height switch with an explicit server-rendered `data-has-context` state.

・Focus view now stores the list scroll position, opens at the top of the correctly padded main area, and restores the saved list position directly on Back. Coordinate recalculation against moved modules was removed.

・Removed global smooth scrolling so layout restoration and focus transitions do not produce unintended intermediate motion.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.3以降でも撤去されず残った仕組みは継続採用とみなす。

header.php、theme.js/cssを中心に修正。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.3 の要望・修正内容と照合して確定。

DOM重複や座標計算が残り、beta.3で再構築。

次版で実際に求められたこと:補正用padding、PC/スマホ二重ナビDOM、フォーカスDOM移動復元を廃止し、単一構造にする。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, header.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.2/assets/css/theme.css
@@ -49,7 +49,7 @@
 }
 
 * { box-sizing: border-box; }
-html { -webkit-tap-highlight-color:transparent; scroll-behavior:smooth; scroll-padding-top:calc(var(--layout-top) + var(--page-top-clearance)); }
+html { -webkit-tap-highlight-color:transparent; scroll-padding-top:calc(var(--layout-top) + var(--page-top-clearance)); }
 body {
   background: var(--surface);
   color: var(--ink);
@@ -97,7 +97,7 @@
 .desktop-context-slot { height:var(--desktop-context-height); overflow:visible; }
 .desktop-context-slot:empty { height:0; }
 .mobile-context-slot { display:none; }
-html:not(:has(.desktop-context-slot > *)) { --desktop-context-height:0px; }
+html[data-has-context="false"] { --desktop-context-height:0px; }
 .brand { background:var(--surface); height:119.40625px; position:relative; }
 .brand::after { background:rgba(117,128,146,.48); content:""; height:1px; left:10px; position:absolute; right:10px; top:79px; }
 .brand__name, .brand__role {
@@ -234,6 +234,7 @@
 .module-grid { display:grid; gap:var(--module-gap); }
 .record-shell,.module-archive,.focus-stage { margin-inline:auto; max-width:var(--record-shell); padding-inline:var(--record-shell-inline); width:100%; }
 .module-archive,.focus-stage { padding-bottom:28px; padding-top:10px; }
+.focus-stage { scroll-margin-top:0; }
 .module-section { display:grid; gap:var(--module-gap); }
 .module-section + .module-section { margin-top:var(--module-section-gap); }
 
@@ -495,7 +496,7 @@
 @media (max-width: 767px) {
   :root { --site-header-height:149px; --desktop-context-height:0px; --layout-top:var(--site-header-height); --page-top-clearance:8px; --viewport-content-top:var(--layout-top); --viewport-content-bottom:calc(100dvh - var(--mobile-dock-height) - var(--mobile-context-height)); --viewport-content-height:calc(var(--viewport-content-bottom) - var(--viewport-content-top)); --content-top:4px; --content-inline:14px; --content-bottom:calc(var(--mobile-dock-height) + var(--mobile-context-height) + 6px); --module-gap:22px; --module-section-gap:28px; --module-padding:14px; --module-radius:20px; --record-shell-inline:14px; }
   body { padding-bottom:calc(var(--mobile-dock-height) + var(--mobile-context-height)); }
-  html:not(:has(.mobile-context-slot > *)) body { padding-bottom:var(--mobile-dock-height); }
+  html[data-has-context="false"] body { padding-bottom:var(--mobile-dock-height); }
   .site-main { padding-top:calc(var(--layout-top) + var(--page-top-clearance)); }
   html[data-current-view="tetris"] .tetris-page { bottom:var(--mobile-dock-height); }
   .admin-bar .site-header { top:46px; }
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.2/assets/js/theme.js
@@ -72,8 +72,11 @@
 
   function activateLocationTarget() {
     var hash = window.location.hash ? decodeURIComponent(window.location.hash.slice(1)) : '';
-    var target = hash ? document.getElementById(hash) : document.querySelector('.site-main > :first-child');
-    activateTarget(target);
+    if (!hash) {
+      window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
+      return;
+    }
+    activateTarget(document.getElementById(hash));
   }
 
   function contextSlots() {
@@ -86,7 +89,9 @@
   }
 
   function setContextHtml(html) {
-    contextSlots().forEach(function (slot) { slot.innerHTML = html || ''; });
+    var value = html || '';
+    contextSlots().forEach(function (slot) { slot.innerHTML = value; });
+    root.setAttribute('data-has-context', value.trim() ? 'true' : 'false');
   }
 
   function visibleContext() {
@@ -410,6 +415,7 @@
     var originNextSibling = null;
     var listUrl = window.location.href;
     var archiveTitle = document.title;
+    var savedListScroll = 0;
     var transitionId = 0;
     var reduceFocusMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
     var onArtistResize = function () { if (activeItem) layoutEventArtists(activeItem); };
@@ -507,7 +513,7 @@
       originParent.insertBefore(restoredItem, originNextSibling);
       stage.hidden = true;
       page.classList.remove('is-focus', 'is-focus-leaving', 'is-focus-closing', 'is-focus-returning');
-      scrollElementToContentTop(restoredItem);
+      window.scrollTo({ top: savedListScroll, left: 0, behavior: 'auto' });
 
       activeItem = null;
       activeSection = null;
@@ -522,6 +528,7 @@
     function openItem(item, link, initial) {
       if (!item || !link || activeItem) return;
       activeItem = item;
+      savedListScroll = initial ? 0 : window.scrollY;
       listUrl = archiveUrl() + '#' + item.id;
       var type = item.getAttribute('data-focus-type');
       var itemId = item.getAttribute('data-focus-id');
@@ -540,12 +547,7 @@
         stage.appendChild(item);
         page.classList.remove('is-focus-leaving');
         page.classList.add('is-focus');
-        if (initial || reduceFocusMotion) {
-          loadDetails(item, type, itemId);
-          activateTarget(item);
-          return;
-        }
-        scrollElementToContentTop(item);
+        window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
         loadDetails(item, type, itemId);
       };
       if (initial || reduceFocusMotion) finish();
@@ -622,7 +624,10 @@
     setupCalendarInteraction();
     setupEventSkullParades();
     setupListFocus();
-    if (!settings.preserveScroll && !document.querySelector('.focus-page.is-focus')) activateLocationTarget();
+    if (!settings.preserveScroll && !document.querySelector('.focus-page.is-focus')) {
+      if (window.location.hash) activateLocationTarget();
+      else window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
+    }
   }
 
   function setupViewNavigation() {
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.2/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,10 @@
+
+## 0.16.0-beta.2
+
+- Removed automatic first-content scrolling on ordinary page loads. Pages now start at document top, so the fixed header stack and the main-content padding define the start position exactly once.
+- Replaced the CSS `:has()` context-height switch with an explicit server-rendered `data-has-context` state.
+- Focus view now stores the list scroll position, opens at the top of the correctly padded main area, and restores the saved list position directly on Back. Coordinate recalculation against moved modules was removed.
+- Removed global smooth scrolling so layout restoration and focus transitions do not produce unintended intermediate motion.
 ## 0.16.0
 
 - PC and mobile context navigation now have explicit structural slots generated from one shared renderer.
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.2/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.16.0-beta.1' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.2' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedheader.php
--- previous/header.php
+++ 0.16.0-beta.2/header.php
@@ -5,8 +5,10 @@
  * @package Hiroki_Ohnuma
  */
 $current_view = ho_current_view();
+$context_html = ho_view_context_html( $current_view );
+$has_context = '' !== trim( $context_html );
 ?><!doctype html>
-<html <?php language_attributes(); ?> data-current-view="<?php echo esc_attr( $current_view ); ?>">
+<html <?php language_attributes(); ?> data-current-view="<?php echo esc_attr( $current_view ); ?>" data-has-context="<?php echo $has_context ? 'true' : 'false'; ?>">
 <head>
 	<meta charset="<?php bloginfo( 'charset' ); ?>">
 	<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
@@ -27,6 +29,6 @@
 	</div>
 	<?php ho_recent_posts_ticker(); ?>
 	<?php ho_render_primary_nav(); ?>
-	<div class="desktop-context-slot" data-context-slot="desktop"><?php echo ho_view_context_html( $current_view ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
+	<div class="desktop-context-slot" data-context-slot="desktop"><?php echo $context_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
 </header>
 <main class="site-main" id="main-content">
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.2/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.16.0-beta.1
+Version: 0.16.0-beta.2
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

既存構造の修正に留まり、PC/モバイル別DOMとフォーカス移動復元が複雑だった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.2.zip


再利用すべき判断基準

  • 成功判定はPC/スマートフォンの実機確認後に行い、未確認を「成功」と記録しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.16.0-beta.1

当時の改修目的

0.15系の補正積み重ねを廃止し、ヘッダー・フッター・可視領域を大きく再設計する。

実装した変更

・PC and mobile context navigation now have explicit structural slots generated from one shared renderer.

・Removed runtime header/footer geometry measurement and the related CSS-variable overrides.

・Unified all content positioning around one CSS layout token (`--layout-top`).

・Unified hash, module-open, and module-return scrolling through one lightweight function.

・Removed FLIP movement and delayed double-correction from single-module return.

・Removed duplicate CSS rules left by incremental revisions.

・Preserved year-strip left alignment for overflowing navigation.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.2以降でも撤去されず残った仕組みは継続採用とみなす。

Tetris CSS、共通CSS/JS、header/footerをまとめて再構成。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.2 の要望・修正内容と照合して確定。

後続ベータで多数の位置・高さ・モバイルフッター問題が発覚。

次版で実際に求められたこと:beta.1のヘッダー/ページ専用ナビ/可視領域の不具合を修正する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/tetris.css, assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, footer.php, functions.php, header.php, style.css

changedassets/css/tetris.css
--- previous/assets/css/tetris.css
+++ 0.16.0-beta.1/assets/css/tetris.css
@@ -60,7 +60,7 @@
       display:grid;
       height:100%;
       margin:0 auto;
-      width:min(1100px,calc((100dvh - var(--header-edge) - var(--footer-height) - 80px)/2 + 250px),calc(100vw - 20px));
+      width:min(1100px,calc((100dvh - var(--layout-top) - var(--footer-height) - 80px)/2 + 250px),calc(100vw - 20px));
     }
     #tetris-doodle-root .row{ display:grid; grid-template-columns:minmax(0,1fr); height:100%; min-height:0; align-items:stretch; }
 
changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.16.0-beta.1/assets/css/theme.css
@@ -13,13 +13,10 @@
   --ink: #202124;
   --muted: #868686;
   --page: 1280px;
-  --header-height: 212.40625px;
-  --header-top: 0px;
-  --header-edge: 212.40625px;
-  --context-edge: 212.40625px;
-  --context-nav-height: 40px;
-  --context-shadow-clearance: 12px;
-  --context-stack-height: calc(var(--context-nav-height) + var(--context-shadow-clearance));
+  --site-header-height: 212.40625px;
+  --desktop-context-height: 52px;
+  --mobile-context-height: 56px;
+  --layout-top: calc(var(--site-header-height) + var(--desktop-context-height));
   --content-edge-gap: 20px;
   --page-top-clearance: var(--content-edge-gap);
   --content-top: 12px;
@@ -35,12 +32,8 @@
   --mobile-nav-height: 48px;
   --mobile-credit-height: 16px;
   --mobile-dock-height: 64px;
-  --footer-rendered-height: 54px;
-  --mobile-dock-rendered-height: 0px;
-  --page-footer-height: 0px;
-  --viewport-bottom-chrome: 54px;
-  --viewport-content-top: var(--header-edge);
-  --viewport-content-bottom: calc(100dvh - var(--viewport-bottom-chrome));
+  --viewport-content-top: var(--layout-top);
+  --viewport-content-bottom: calc(100dvh - var(--footer-height));
   --viewport-content-height: calc(var(--viewport-content-bottom) - var(--viewport-content-top));
   --view-fade-duration: .86s;
   --skull-parade-max-width: calc(var(--record-shell) - (var(--record-shell-inline) * 2) - (var(--module-padding) * 2));
@@ -56,7 +49,7 @@
 }
 
 * { box-sizing: border-box; }
-html { -webkit-tap-highlight-color:transparent; scroll-behavior:smooth; scroll-padding-top:calc(var(--header-edge) + var(--page-top-clearance)); }
+html { -webkit-tap-highlight-color:transparent; scroll-behavior:smooth; scroll-padding-top:calc(var(--layout-top) + var(--page-top-clearance)); }
 body {
   background: var(--surface);
   color: var(--ink);
@@ -79,7 +72,7 @@
   overflow:hidden;
   position:fixed;
   right:0;
-  top:calc(var(--header-edge) + var(--page-top-clearance));
+  top:calc(var(--layout-top) + var(--page-top-clearance));
   touch-action:none;
 }
 a { color: inherit; }
@@ -99,9 +92,12 @@
   z-index: 9990;
 }
 .admin-bar .site-header { top: 32px; }
-@media (min-width:768px) {
-  html[data-has-context="true"] .site-header { box-shadow:none; }
-}
+.site-main { padding-top:calc(var(--layout-top) + var(--page-top-clearance)); }
+[data-context-section], [data-focus-item] { scroll-margin-top:calc(var(--layout-top) + 8px); }
+.desktop-context-slot { height:var(--desktop-context-height); overflow:visible; }
+.desktop-context-slot:empty { height:0; }
+.mobile-context-slot { display:none; }
+html:not(:has(.desktop-context-slot > *)) { --desktop-context-height:0px; }
 .brand { background:var(--surface); height:119.40625px; position:relative; }
 .brand::after { background:rgba(117,128,146,.48); content:""; height:1px; left:10px; position:absolute; right:10px; top:79px; }
 .brand__name, .brand__role {
@@ -152,11 +148,10 @@
 @keyframes nav-glow { 0%,100% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 0 0 var(--glow); } 50% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 16px 5px var(--glow); } }
 
 
-.page-footer { height:var(--context-stack-height); left:0; overflow:visible; padding:0 0 var(--context-shadow-clearance); position:fixed; right:0; top:var(--header-edge); z-index:9992; }
-.page-footer:empty { display:none; }
-.page-footer .context-nav { height:var(--context-nav-height); }
-
-.context-nav { --context-accent:var(--green); align-items:center; background:linear-gradient(var(--surface),rgba(237,241,247,.82)); display:grid; gap:10px; grid-template-columns:100px minmax(0,1fr) 100px; height:40px; overflow:hidden; padding:0 12px; white-space:nowrap; }
+.desktop-context-slot .context-nav { height:var(--desktop-context-height); padding-block:6px; }
+.desktop-context-slot .context-nav { box-shadow:0 7px 18px rgba(163,177,198,.34); }
+
+.context-nav { --context-accent:var(--green); align-items:center; background:linear-gradient(var(--surface),rgba(237,241,247,.82)); display:grid; gap:10px; grid-template-columns:100px minmax(0,1fr) 100px; height:40px; overflow:visible; padding:0 12px; white-space:nowrap; }
 .context-nav--schedule { --context-accent:var(--yellow); }
 .context-nav--works { --context-accent:var(--blue); }
 .context-nav.context-nav--profile { --context-accent:var(--blue); grid-template-columns:minmax(0,1fr); }
@@ -201,17 +196,10 @@
 @keyframes link-thanks-cascade { 0%,62%,100%{opacity:1;transform:translateY(0)} 72%{opacity:.72;transform:translateY(3px)} 84%{opacity:1;transform:translateY(-3px)} 92%{transform:translateY(0)} }
 @keyframes link-thanks-heartbeat { 0%,64%,100%{transform:scale(1)} 70%{transform:scale(1.035)} 76%{transform:scale(.995)} 82%{transform:scale(1.055)} 90%{transform:scale(1)} }
 
-.site-main { min-height:calc(100vh - var(--footer-height)); padding-top:calc(var(--header-edge) + var(--page-top-clearance)); }
-@media (min-width:768px) {
-  html[data-has-context="true"] .site-main { padding-top:calc(var(--header-edge) + var(--context-stack-height) + var(--page-top-clearance)); }
-  html[data-has-context="true"] { scroll-padding-top:calc(var(--header-edge) + var(--context-stack-height) + var(--page-top-clearance)); }
-  .page-footer { filter:drop-shadow(0 7px 10px rgba(143,158,184,.28)); }
-  .context-nav { overflow:visible; }
-  .context-nav__years { overflow-x:auto; overflow-y:visible; }
-}
+.site-main { min-height:calc(100vh - var(--footer-height)); }
 html[data-view-transitioning="true"] .site-main,html[data-view-transitioning="true"] .context-nav { will-change:opacity; }
 .ho-js[data-current-view="home"]:not(.is-view-ready) .site-main,
-.ho-js[data-current-view="home"]:not(.is-view-ready) [data-page-footer] > * { opacity:0; }
+.ho-js[data-current-view="home"]:not(.is-view-ready) [data-context-slot] > * { opacity:0; }
 .content-shell { margin:0 auto; max-width:var(--page); padding:var(--content-top) var(--content-inline) var(--content-bottom); }
 .page-title { font-size:32px; margin:0 0 22px; }
 .page-title--profile { color:var(--blue); }
@@ -369,8 +357,7 @@
 @keyframes skull-ricochet-in { 0%{opacity:0;transform:translateX(var(--skull-from-x)) translateY(-32vh) rotate(var(--skull-spin))} 14%{opacity:1} 38%{transform:translateX(calc(var(--skull-from-x) * .48)) translateY(10px) rotate(120deg) scale(1.12,.88)} 58%{transform:translateX(calc(var(--skull-from-x) * .22)) translateY(-96px) rotate(-55deg)} 80%{transform:translateX(0) translateY(0) rotate(18deg)} 92%{transform:translateY(-16px) rotate(-7deg)} 100%{opacity:1;transform:none} }
 @keyframes skull-pendulum-in { 0%{opacity:0;transform:translateX(var(--skull-from-x)) translateY(-18px) rotate(65deg)} 15%{opacity:1} 44%{transform:translateX(calc(var(--skull-from-x) * .5)) translateY(-74px) rotate(-42deg)} 68%{transform:translateX(calc(var(--skull-from-x) * .18)) translateY(-20px) rotate(28deg)} 86%{transform:translateY(-9px) rotate(-12deg)} 100%{opacity:1;transform:none} }
 @keyframes skull-skip-in { 0%{opacity:0;transform:translateX(var(--skull-from-x)) translateY(0) rotate(18deg)} 10%{opacity:1} 25%{transform:translateX(calc(var(--skull-from-x) * .72)) translateY(-26px) rotate(-12deg)} 43%{transform:translateX(calc(var(--skull-from-x) * .52)) translateY(0) scale(1.1,.9)} 60%{transform:translateX(calc(var(--skull-from-x) * .32)) translateY(-52px) rotate(16deg)} 78%{transform:translateX(calc(var(--skull-from-x) * .12)) translateY(0) scale(1.08,.92)} 90%{transform:translateY(-17px)} 100%{opacity:1;transform:none} }
-.focus-item__extras { grid-column:1 / -1; margin:0; }
-.focus-item__extras { display:grid; grid-template-rows:0fr; opacity:0; transition:grid-template-rows .84s cubic-bezier(.18,.82,.24,1),opacity .44s ease; visibility:hidden; }
+.focus-item__extras { grid-column:1 / -1; margin:0; display:grid; grid-template-rows:0fr; opacity:0; transition:grid-template-rows .84s cubic-bezier(.18,.82,.24,1),opacity .44s ease; visibility:hidden; }
 .focus-item__extras-inner { display:grid; gap:var(--module-gap); min-height:0; overflow:hidden; }
 .event-focus { --event-focus-gap:var(--module-gap); display:grid; gap:var(--event-focus-gap); }
 .event-focus__media { margin:0; }
@@ -385,7 +372,7 @@
 .event-focus__note { background:rgba(245,248,251,.48); border:1px solid rgba(255,255,255,.58); border-radius:11px; box-shadow:inset 1px 1px 3px rgba(143,158,184,.2),inset -1px -1px 3px rgba(255,255,255,.72); font-size:16px; font-weight:700; line-height:1.45; padding:12px 14px; }
 .event-focus__note p { margin:0 0 1em; }
 .event-focus__note p:last-child { margin-bottom:0; }
-.focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--context-edge); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
+.focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--layout-top); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
 .focus-return-control svg { fill:none; height:28px; overflow:visible; stroke:currentColor; stroke-linecap:round; stroke-linejoin:round; stroke-width:3.4; width:28px; }
 .focus-page.is-focus .focus-return-control { opacity:1; pointer-events:auto; transform:translateY(0); }
 .focus-return-control:active { box-shadow:var(--neu-inset); transform:translateY(1px); }
@@ -446,7 +433,7 @@
   overflow:hidden;
   position:fixed;
   right:0;
-  top:calc(var(--header-edge) + var(--page-top-clearance));
+  top:calc(var(--layout-top) + var(--page-top-clearance));
 }
 .home-stage__album-link { align-items:center; color:var(--red); display:flex; flex-direction:column; font-weight:900; left:20%; position:absolute; text-decoration:none; top:20px; width:154px; z-index:2; }
 .home-stage__album-link img { animation:home-skull 2.4s ease-in-out infinite; width:148px; }
@@ -479,7 +466,7 @@
 @media (min-width: 768px) {
   .link-row__button { font-size:clamp(14px,1.25vw,16px); white-space:nowrap; }
   .home-stage {
-    --home-head-position-unit:min(7.708333vw,calc((100dvh - var(--header-edge) - var(--footer-height) - 64px) / 5));
+    --home-head-position-unit:min(7.708333vw,calc((100dvh - var(--layout-top) - var(--footer-height) - 64px) / 5));
     --home-head-size:clamp(224px,15.416667vw,296px);
     background-position:center center;
     background-size:cover;
@@ -506,9 +493,10 @@
 }
 
 @media (max-width: 767px) {
-  :root { --header-height:149px; --header-edge:149px; --page-top-clearance:8px; --content-top:4px; --content-inline:14px; --content-bottom:calc(var(--mobile-dock-height) + 62px); --module-gap:22px; --module-section-gap:28px; --module-padding:14px; --module-radius:20px; --record-shell-inline:14px; }
-  body { padding-bottom:var(--mobile-dock-height); }
-  .site-main { padding-top:calc(var(--header-edge) + var(--page-top-clearance)); }
+  :root { --site-header-height:149px; --desktop-context-height:0px; --layout-top:var(--site-header-height); --page-top-clearance:8px; --viewport-content-top:var(--layout-top); --viewport-content-bottom:calc(100dvh - var(--mobile-dock-height) - var(--mobile-context-height)); --viewport-content-height:calc(var(--viewport-content-bottom) - var(--viewport-content-top)); --content-top:4px; --content-inline:14px; --content-bottom:calc(var(--mobile-dock-height) + var(--mobile-context-height) + 6px); --module-gap:22px; --module-section-gap:28px; --module-padding:14px; --module-radius:20px; --record-shell-inline:14px; }
+  body { padding-bottom:calc(var(--mobile-dock-height) + var(--mobile-context-height)); }
+  html:not(:has(.mobile-context-slot > *)) body { padding-bottom:var(--mobile-dock-height); }
+  .site-main { padding-top:calc(var(--layout-top) + var(--page-top-clearance)); }
   html[data-current-view="tetris"] .tetris-page { bottom:var(--mobile-dock-height); }
   .admin-bar .site-header { top:46px; }
   .brand { height:79px; }
@@ -519,6 +507,7 @@
   .news-ticker { height:32px; }
   .site-nav { gap:20px; height:38px; padding:5px 15px; }
   .site-nav__link { border-radius:500px; font-family:"Archivo Black",sans-serif; font-size:14px; height:28px; padding:0 3px; }
+  .desktop-context-slot { display:none; }
   .context-nav { display:none; }
   .content-shell { padding:var(--content-top) var(--content-inline) var(--content-bottom); }
   .page-title,.calendar-link { font-size:28px; }
@@ -547,7 +536,8 @@
   .site-footer { display:none; }
   .mobile-dock { bottom:0; display:block; left:0; position:fixed; right:0; z-index:9999; }
   :root.is-web-app .mobile-dock { background:rgba(119,128,143,.94); padding-bottom:env(safe-area-inset-bottom, 0px); }
-  .page-footer { bottom:var(--mobile-dock-height); height:56px; padding:0; top:auto; }
+  .mobile-context-slot { bottom:var(--mobile-dock-height); display:block; height:var(--mobile-context-height); left:0; position:fixed; right:0; z-index:9998; }
+  .mobile-context-slot:empty { display:none; }
   .context-nav { background:rgba(237,241,247,.97); bottom:auto; box-shadow:0 -5px 14px rgba(163,177,198,.5); display:grid; gap:0; grid-template-columns:20% minmax(0,60%) 20%; height:56px; left:auto; overflow:hidden; padding:0; position:relative; right:auto; z-index:auto; }
   .context-nav__section-link { align-items:center; align-self:stretch; background:linear-gradient(90deg,#fff,var(--surface)); border-radius:16px; display:flex; justify-content:center; padding:0 5px; position:relative; z-index:2; }
   .context-nav__section-link:last-child { background:linear-gradient(90deg,var(--surface),#fff); }
@@ -572,7 +562,7 @@
   html:not([data-current-view="home"]) .home-dog,html:not([data-current-view="home"]) .home-runner-track { display:none; }
   .home-dog,.home-runner-track { bottom:var(--mobile-dock-height); }
   :root.is-web-app[data-current-view="home"] .home-dog { display:block !important; opacity:1; visibility:visible; }
-  .admin-bar .home-stage { top:calc(var(--header-edge) + var(--page-top-clearance)); }
+  .admin-bar .home-stage { top:calc(var(--layout-top) + var(--page-top-clearance)); }
   .focus-parade-layer {
     bottom:calc(100dvh - var(--viewport-content-bottom) + 8px);
     max-width:calc(100vw - 16px);
@@ -587,7 +577,7 @@
   .event-skull-parade__slot img { --skull-visual-scale:1.34; }
   .focus-return-control {
     right:12px;
-    top:var(--header-edge);
+    top:var(--layout-top);
   }
 }
 
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.16.0-beta.1/assets/js/theme.js
@@ -44,108 +44,55 @@
     root.classList.toggle('is-web-app', webAppQuery.matches || window.navigator.standalone === true);
   }
 
-  function elementHeight(element) {
-    if (!element || window.getComputedStyle(element).display === 'none') return 0;
-    return Math.max(0, element.getBoundingClientRect().height);
-  }
-
-  function visibleRect(element) {
-    if (!element || window.getComputedStyle(element).display === 'none') return null;
-    var rect = element.getBoundingClientRect();
-    return rect.height > 0 && rect.width > 0 ? rect : null;
-  }
-
-  function syncLayoutMetrics() {
-    var header = document.getElementById('site-header');
-    var footer = document.querySelector('.site-footer');
-    var mobileDock = document.getElementById('smf-mobile-dock');
-    var pageFooter = document.querySelector('[data-page-footer]');
-    var context = pageFooter ? pageFooter.querySelector('.context-nav') : null;
-    var headerRect = visibleRect(header) || { top: 0, bottom: 0, height: 0 };
-    var footerRect = visibleRect(footer);
-    var mobileDockRect = visibleRect(mobileDock);
-    var contextRect = visibleRect(context);
-    var pageFooterRect = visibleRect(pageFooter);
-    var edgeGap = parseFloat(window.getComputedStyle(root).getPropertyValue('--content-edge-gap')) || 0;
-    var desktopContext = window.matchMedia('(min-width: 768px)').matches && pageFooterRect && contextRect;
-    var contextEdge = desktopContext ? pageFooterRect.bottom : headerRect.bottom;
-    var contentTop = Math.max(0, contextEdge + edgeGap);
-    var primaryFooterRect = mobileDockRect || footerRect;
-    var contentBoundary = desktopContext ? primaryFooterRect : (pageFooterRect || primaryFooterRect);
-    var contentBottom = contentBoundary ? Math.max(contentTop, Math.min(window.innerHeight, contentBoundary.top - edgeGap)) : Math.max(contentTop, window.innerHeight - edgeGap);
-    root.classList.toggle('has-page-context', Boolean(pageFooterRect && contextRect));
-    var bottomChrome = Math.max(0, window.innerHeight - contentBottom);
-    var contentHeight = Math.max(0, contentBottom - contentTop);
-
-    root.style.setProperty('--header-height', Math.max(0, headerRect.height).toFixed(2) + 'px');
-    root.style.setProperty('--header-top', Math.max(0, headerRect.top).toFixed(2) + 'px');
-    root.style.setProperty('--header-edge', Math.max(0, headerRect.bottom).toFixed(2) + 'px');
-    root.style.setProperty('--context-edge', Math.max(0, contextEdge).toFixed(2) + 'px');
-    root.style.setProperty('--footer-rendered-height', elementHeight(footer).toFixed(2) + 'px');
-    root.style.setProperty('--mobile-dock-rendered-height', elementHeight(mobileDock).toFixed(2) + 'px');
-    root.style.setProperty('--page-footer-height', elementHeight(pageFooter).toFixed(2) + 'px');
-    root.style.setProperty('--viewport-content-top', contentTop.toFixed(2) + 'px');
-    root.style.setProperty('--viewport-content-bottom', contentBottom.toFixed(2) + 'px');
-    root.style.setProperty('--viewport-bottom-chrome', bottomChrome.toFixed(2) + 'px');
-    root.style.setProperty('--viewport-content-height', contentHeight.toFixed(2) + 'px');
-  }
-
-  function setupLayoutMetrics() {
-    syncWebAppMode();
-    syncLayoutMetrics();
-    if (webAppQuery.addEventListener) webAppQuery.addEventListener('change', function () {
-      syncWebAppMode();
-      syncLayoutMetrics();
-    });
-    window.addEventListener('resize', syncLayoutMetrics, { passive: true });
-    window.addEventListener('orientationchange', syncLayoutMetrics);
-    window.addEventListener('pageshow', syncLayoutMetrics);
-    if ('ResizeObserver' in window) {
-      var observer = new ResizeObserver(syncLayoutMetrics);
-      ['site-header', 'smf-mobile-dock'].forEach(function (id) {
-        var element = document.getElementById(id);
-        if (element) observer.observe(element);
-      });
-      var footer = document.querySelector('.site-footer');
-      if (footer) observer.observe(footer);
-      var pageFooter = document.querySelector('[data-page-footer]');
-      if (pageFooter) observer.observe(pageFooter);
-    }
-    if (document.fonts && document.fonts.ready) document.fonts.ready.then(syncLayoutMetrics);
+  function contentTop() {
+    return parseFloat(window.getComputedStyle(root).getPropertyValue('--layout-top')) || 0;
+  }
+
+  function scrollElementToContentTop(target) {
+    if (!target) return;
+    var destination = window.scrollY + target.getBoundingClientRect().top - contentTop() - 8;
+    window.scrollTo({ top: Math.max(0, destination), left: 0, behavior: 'auto' });
   }
 
   function scrollTargetBelowHeader(target) {
-    syncLayoutMetrics();
-    var header = document.getElementById('site-header');
-    if (!header) return;
-    var destination = window.scrollY + target.getBoundingClientRect().top - header.getBoundingClientRect().bottom - 8;
-    var previousBehavior = root.style.scrollBehavior;
-    root.style.scrollBehavior = 'auto';
-    window.scrollTo(0, Math.max(0, destination));
-    root.style.scrollBehavior = previousBehavior;
+    scrollElementToContentTop(target);
   }
 
   function activateTarget(target) {
     var activation = ++targetActivation;
     if (!target) return;
     if (window.getComputedStyle(target).position === 'fixed') {
-      window.scrollTo(0, 0);
+      window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
       return;
     }
-    var activate = function () {
-      if (activation !== targetActivation || !document.documentElement.contains(target)) return;
-      window.requestAnimationFrame(function () {
-        if (activation === targetActivation && document.documentElement.contains(target)) scrollTargetBelowHeader(target);
-      });
-    };
-    if (document.fonts && document.fonts.ready) document.fonts.ready.then(activate);
-    else activate();
+    window.requestAnimationFrame(function () {
+      if (activation === targetActivation && document.documentElement.contains(target)) scrollElementToContentTop(target);
+    });
   }
 
   function activateLocationTarget() {
     var hash = window.location.hash ? decodeURIComponent(window.location.hash.slice(1)) : '';
     var target = hash ? document.getElementById(hash) : document.querySelector('.site-main > :first-child');
     activateTarget(target);
+  }
+
+  function contextSlots() {
+    return Array.from(document.querySelectorAll('[data-context-slot]'));
+  }
+
+  function contextHtml() {
+    var slot = contextSlots()[0];
+    return slot ? slot.innerHTML : '';
+  }
+
+  function setContextHtml(html) {
+    contextSlots().forEach(function (slot) { slot.innerHTML = html || ''; });
+  }
+
+  function visibleContext() {
+    return contextSlots().map(function (slot) { return slot.firstElementChild; }).find(function (node) {
+      return node && window.getComputedStyle(node).display !== 'none';
+    }) || null;
   }
 
   function setupTabs() {
@@ -314,32 +261,31 @@
   }
 
   function setupContextNavigation() {
-    var nav = document.querySelector('.context-nav');
+    var navs = contextSlots().map(function (slot) { return slot.querySelector('.context-nav'); }).filter(Boolean);
     var sections = Array.from(document.querySelectorAll('[data-context-section]'));
-    if (!nav) return;
-    alignContextNavigation(nav);
-    var links = Array.from(nav.querySelectorAll('[data-context-key]'));
-    if (!sections.length) {
-      if (root.getAttribute('data-current-view') === 'calendar') {
-        var requestedYear = new URL(window.location.href).searchParams.get('calendar_year');
-        if (requestedYear) {
-          links.forEach(function (link) {
-            if (link.getAttribute('data-context-key') === requestedYear) link.setAttribute('aria-current', 'page');
-            else link.removeAttribute('aria-current');
-          });
-          alignContextNavigation(nav);
-        }
-        setupCalendarYearNavigation(nav, links);
-      }
-      return;
-    }
+    if (!navs.length) return;
+    var links = navs.reduce(function (all, nav) {
+      alignContextNavigation(nav);
+      return all.concat(Array.from(nav.querySelectorAll('[data-context-key]')));
+    }, []);
 
     function select(key) {
       links.forEach(function (link) {
         if (link.getAttribute('data-context-key') === key) link.setAttribute('aria-current', 'page');
         else link.removeAttribute('aria-current');
       });
-      alignContextNavigation(nav);
+      navs.forEach(alignContextNavigation);
+    }
+
+    if (!sections.length) {
+      if (root.getAttribute('data-current-view') === 'calendar') {
+        var requestedYear = new URL(window.location.href).searchParams.get('calendar_year');
+        if (requestedYear) select(requestedYear);
+        navs.forEach(function (nav) {
+          setupCalendarYearNavigation(nav, Array.from(nav.querySelectorAll('[data-context-key]')));
+        });
+      }
+      return;
     }
 
     function targetForLocation() {
@@ -349,26 +295,23 @@
 
     links.forEach(function (link) {
       link.addEventListener('click', function (event) {
-        var target = sections.find(function (section) {
-          return section.getAttribute('data-context-key') === link.getAttribute('data-context-key');
-        });
+        var key = link.getAttribute('data-context-key');
+        var target = sections.find(function (section) { return section.getAttribute('data-context-key') === key; });
         if (!target) return;
         event.preventDefault();
-        select(link.getAttribute('data-context-key'));
+        select(key);
         replaceLocation(link.href, window.history.state);
         activateTarget(target);
       });
     });
 
     if ('IntersectionObserver' in window) {
-      var header = document.getElementById('site-header');
-      var topMargin = Math.round((header ? header.getBoundingClientRect().bottom : 0) + 8);
       var observer = new IntersectionObserver(function (entries) {
         var visible = entries.filter(function (entry) { return entry.isIntersecting; }).sort(function (a, b) {
           return a.boundingClientRect.top - b.boundingClientRect.top;
         })[0];
         if (visible) select(visible.target.getAttribute('data-context-key'));
-      }, { rootMargin: '-' + topMargin + 'px 0px -70% 0px', threshold: 0 });
+      }, { rootMargin: '-' + Math.round(contentTop() + 8) + 'px 0px -70% 0px', threshold: 0 });
       sections.forEach(function (section) { observer.observe(section); });
       registerViewCleanup(function () { observer.disconnect(); });
     }
@@ -472,24 +415,6 @@
     var onArtistResize = function () { if (activeItem) layoutEventArtists(activeItem); };
     window.addEventListener('resize', onArtistResize, { passive: true });
     registerViewCleanup(function () { window.removeEventListener('resize', onArtistResize); });
-
-    function animateMovedItem(item, firstRect) {
-      var lastRect = item.getBoundingClientRect();
-      var deltaX = firstRect.left - lastRect.left;
-      var deltaY = firstRect.top - lastRect.top;
-      if (reduceFocusMotion || !item.animate || (Math.abs(deltaX) < 1 && Math.abs(deltaY) < 1)) return Promise.resolve();
-      var distance = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY));
-      var duration = motionTime(Math.min(620, Math.max(380, 350 + (distance * .24))));
-      item.classList.add('is-focus-moving');
-      var animation = item.animate([
-        { transform: 'translate3d(' + deltaX + 'px,' + deltaY + 'px,0)' },
-        { transform: 'translate3d(0,0,0)' }
-      ], { duration: duration, easing: 'cubic-bezier(.16,1,.3,1)', fill: 'both' });
-      return animation.finished.catch(function () {}).then(function () {
-        animation.cancel();
-        item.classList.remove('is-focus-moving');
-      });
-    }
 
 
     function clearParadeLayer() {
@@ -573,46 +498,25 @@
 
     function closeItem() {
       if (!activeItem || page.classList.contains('is-focus-closing')) return;
-      var thisTransition = ++transitionId;
-      targetActivation += 1;
       var restoredItem = activeItem;
       var restoredSection = activeSection;
-      var hadDetails = restoredItem.classList.contains('has-focus-details');
       page.classList.add('is-focus-closing');
       clearParadeLayer();
       restoredItem.classList.remove('is-expanding', 'has-focus-details');
-      var restore = function () {
-        if (thisTransition !== transitionId || activeItem !== restoredItem) return;
-        page.classList.add('is-focus-returning');
-        originParent.insertBefore(restoredItem, originNextSibling);
-        stage.hidden = true;
-        page.classList.remove('is-focus', 'is-focus-leaving', 'is-focus-closing');
-
-        syncLayoutMetrics();
-        var headerBoundary = parseFloat(window.getComputedStyle(root).getPropertyValue('--context-edge')) || 0;
-        var destination = window.scrollY + restoredItem.getBoundingClientRect().top - headerBoundary - 8;
-        var previousBehavior = root.style.scrollBehavior;
-        root.style.scrollBehavior = 'auto';
-        window.scrollTo(0, Math.max(0, destination));
-        root.style.scrollBehavior = previousBehavior;
-
-        window.requestAnimationFrame(function () {
-          if (thisTransition !== transitionId || activeItem !== restoredItem) return;
-          page.classList.remove('is-focus-returning');
-          activeItem = null;
-          activeSection = null;
-          originParent = null;
-          originNextSibling = null;
-          document.title = archiveTitle;
-          window.setTimeout(function () {
-            restoredItem.classList.remove('is-picked');
-            restoredItem.removeAttribute('aria-current');
-            if (restoredSection) restoredSection.classList.remove('is-picked-section');
-          }, motionTime(220));
-        });
-      };
-      if (reduceFocusMotion || !hadDetails) restore();
-      else window.setTimeout(restore, motionTime(430));
+
+      originParent.insertBefore(restoredItem, originNextSibling);
+      stage.hidden = true;
+      page.classList.remove('is-focus', 'is-focus-leaving', 'is-focus-closing', 'is-focus-returning');
+      scrollElementToContentTop(restoredItem);
+
+      activeItem = null;
+      activeSection = null;
+      originParent = null;
+      originNextSibling = null;
+      document.title = archiveTitle;
+      restoredItem.classList.remove('is-picked', 'is-focus-moving');
+      restoredItem.removeAttribute('aria-current');
+      if (restoredSection) restoredSection.classList.remove('is-picked-section');
     }
 
     function openItem(item, link, initial) {
@@ -632,7 +536,6 @@
       var thisTransition = ++transitionId;
       var finish = function () {
         if (thisTransition !== transitionId || activeItem !== item) return;
-        var firstRect = item.getBoundingClientRect();
         stage.hidden = false;
         stage.appendChild(item);
         page.classList.remove('is-focus-leaving');
@@ -642,11 +545,8 @@
           activateTarget(item);
           return;
         }
-        scrollTargetBelowHeader(item);
-        animateMovedItem(item, firstRect).then(function () {
-          if (thisTransition !== transitionId || activeItem !== item) return;
-          loadDetails(item, type, itemId);
-        });
+        scrollElementToContentTop(item);
+        loadDetails(item, type, itemId);
       };
       if (initial || reduceFocusMotion) finish();
       else {
@@ -716,7 +616,6 @@
 
   function initializeView(options) {
     var settings = options || {};
-    syncLayoutMetrics();
     setupTabs();
     setupLinkThanks();
     setupContextNavigation();
@@ -782,7 +681,7 @@
     }
 
     function animateCalendarDissolve(main, oldCalendarPage) {
-      if (!oldCalendarPage || reduceMotion || !main.animate) return animateViewSurface(main, document.querySelector('[data-view-context] .context-nav'), true);
+      if (!oldCalendarPage || reduceMotion || !main.animate) return animateViewSurface(main, visibleContext(), true);
       var oldRect = oldCalendarPage.getBoundingClientRect();
       var overlay = document.createElement('div');
       overlay.className = 'calendar-dissolve-overlay';
@@ -867,7 +766,7 @@
     function captureView(view, main, contextSlot) {
       var fragment = document.createDocumentFragment();
       while (main.firstChild) fragment.appendChild(main.firstChild);
-      viewStates.set(view, { fragment: fragment, contextHtml: contextSlot.innerHTML, title: document.title });
+      viewStates.set(view, { fragment: fragment, contextHtml: contextHtml(), title: document.title });
     }
 
     function setPrimaryCurrent(view) {
@@ -885,14 +784,14 @@
       targetActivation += 1;
       navigating = true;
       var main = document.querySelector('.site-main');
-      var contextSlot = document.querySelector('[data-view-context]');
+      var contextSlot = contextSlots()[0];
       var fromView = currentView();
       var calendarDissolve = fromView === 'calendar' && targetView === 'calendar';
       var oldCalendarPage = calendarDissolve ? document.querySelector('.calendar-page') : null;
       if (oldCalendarPage) oldCalendarPage = oldCalendarPage.cloneNode(true);
       root.setAttribute('data-view-transitioning', 'true');
       var request = !forceRequest && viewStates.has(targetView) ? Promise.resolve({ state: viewStates.get(targetView) }) : requestView(targetView, url).then(function (payload) { return { payload: payload }; });
-      var oldContext = contextSlot ? contextSlot.firstElementChild : null;
+      var oldContext = visibleContext();
       var leaving = calendarDissolve ? Promise.resolve() : animateViewSurface(main, oldContext, false);
 
       return Promise.all([request, leaving]).then(function (results) {
@@ -913,17 +812,16 @@
           title = result.payload.title || document.title;
           assets = result.payload.assets;
         }
-        contextSlot.innerHTML = contextHtml;
+        setContextHtml(contextHtml);
         document.title = title;
         root.setAttribute('data-current-view', targetView);
-        root.setAttribute('data-has-context', ['profile', 'opus', 'works', 'schedule', 'calendar', 'link'].indexOf(targetView) !== -1 ? 'true' : 'false');
         setPrimaryCurrent(targetView);
         if (addHistory) pushLocation(url, { hoShell: true, view: targetView });
         visibleUrl = window.location.href;
         window.scrollTo(0, typeof restoredScroll === 'number' ? restoredScroll : 0);
         return loadAssets(assets).then(function () {
           initializeView({ preserveScroll: typeof restoredScroll === 'number' });
-          return calendarDissolve ? animateCalendarDissolve(main, oldCalendarPage) : animateViewSurface(main, contextSlot.firstElementChild, true);
+          return calendarDissolve ? animateCalendarDissolve(main, oldCalendarPage) : animateViewSurface(main, visibleContext(), true);
         });
       }).catch(function () {
         window.location.href = url;
@@ -986,7 +884,7 @@
 
   function revealInitialView() {
     var main = document.querySelector('.site-main');
-    var context = document.querySelector('[data-view-context] > *');
+    var context = visibleContext();
     var isHome = root.getAttribute('data-current-view') === 'home';
     if (!isHome || window.matchMedia('(prefers-reduced-motion: reduce)').matches || !main || !main.animate) {
       root.classList.add('is-view-ready');
@@ -998,7 +896,8 @@
     })).then(function () { root.classList.add('is-view-ready'); });
   }
 
-  setupLayoutMetrics();
+  syncWebAppMode();
+  if (webAppQuery.addEventListener) webAppQuery.addEventListener('change', syncWebAppMode);
   initializeView();
   setupViewNavigation();
   revealInitialView();
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.16.0-beta.1/docs/BETA1-CHANGELOG.md
@@ -1,10 +1,12 @@
-## 0.15.4
+## 0.16.0
 
-- PC版のページ専用ヘッダーを主ヘッダー直下へ隙間なく接続。
-- ページ専用ヘッダーの影のための余白を下側だけに統一し、主メニューとの間に生じていた溝を撤去。
-- 本文開始位置を、主ヘッダー+ページ専用ヘッダー全体の実測下端から一度だけ決める構造へ整理。
-- 初期表示前から正しい本文位置を確保するため、ページ種別をHTML属性として出力。Ajax遷移時も同属性を同期。
-- 旧来の固定値52pxと `:has()` による重複した本文位置補正を削除。
+- PC and mobile context navigation now have explicit structural slots generated from one shared renderer.
+- Removed runtime header/footer geometry measurement and the related CSS-variable overrides.
+- Unified all content positioning around one CSS layout token (`--layout-top`).
+- Unified hash, module-open, and module-return scrolling through one lightweight function.
+- Removed FLIP movement and delayed double-correction from single-module return.
+- Removed duplicate CSS rules left by incremental revisions.
+- Preserved year-strip left alignment for overflowing navigation.
 
 ## 0.15.3
 - PCのページ専用ヘッダー高をレイアウト基準へ統合し、本文が下へ潜る初期表示をCSSと計測処理の両方で解消。
changedfooter.php
--- previous/footer.php
+++ 0.16.0-beta.1/footer.php
@@ -6,7 +6,7 @@
  */
 ?>
 </main>
-<div class="page-footer" data-view-context data-page-footer><?php echo ho_view_context_html( ho_current_view() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
+<div class="mobile-context-slot" data-context-slot="mobile"><?php echo ho_view_context_html( ho_current_view() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
 <img class="home-dog" src="<?php echo esc_url( get_template_directory_uri() . '/assets/img/8686_dog.webp' ); ?>" alt="" aria-hidden="true">
 <div class="home-runner-track" aria-hidden="true"><a href="<?php echo esc_url( ho_view_url( 'calendar' ) ); ?>" data-view-link="calendar" tabindex="-1"><img src="<?php echo esc_url( get_template_directory_uri() . '/assets/img/8686_skull-run.webp' ); ?>" alt=""></a></div>
 <footer class="site-footer">&copy;Skeleton Music Factory, Japan</footer>
changedfunctions.php
--- previous/functions.php
+++ 0.16.0-beta.1/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.15.4-beta.1' );
+define( 'HO_THEME_VERSION', '0.16.0-beta.1' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedheader.php
--- previous/header.php
+++ 0.16.0-beta.1/header.php
@@ -5,9 +5,8 @@
  * @package Hiroki_Ohnuma
  */
 $current_view = ho_current_view();
-$has_context = in_array( $current_view, array( 'profile', 'opus', 'works', 'schedule', 'calendar', 'link' ), true );
 ?><!doctype html>
-<html <?php language_attributes(); ?> data-current-view="<?php echo esc_attr( $current_view ); ?>" data-has-context="<?php echo $has_context ? 'true' : 'false'; ?>">
+<html <?php language_attributes(); ?> data-current-view="<?php echo esc_attr( $current_view ); ?>">
 <head>
 	<meta charset="<?php bloginfo( 'charset' ); ?>">
 	<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
@@ -28,5 +27,6 @@
 	</div>
 	<?php ho_recent_posts_ticker(); ?>
 	<?php ho_render_primary_nav(); ?>
+	<div class="desktop-context-slot" data-context-slot="desktop"><?php echo ho_view_context_html( $current_view ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
 </header>
 <main class="site-main" id="main-content">
changedstyle.css
--- previous/style.css
+++ 0.16.0-beta.1/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.15.4-beta.1
+Version: 0.16.0-beta.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

大規模再設計の初版で、全ページ・全端末の実測条件を一度に満たせなかった。


対象ZIP
hiroki-ohnuma-beta1-0.16.0-beta.1.zip


再利用すべき判断基準

  • 同じ役割の処理は一つの関数・一つの状態・一つのDOMに集約する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.15.4

当時の改修目的

主ヘッダーとページ専用ヘッダーの積層、固定表示、モバイル構造を整理する。

実装した変更

・PC版のページ専用ヘッダーを主ヘッダー直下へ隙間なく接続。

・ページ専用ヘッダーの影のための余白を下側だけに統一し、主メニューとの間に生じていた溝を撤去。

・本文開始位置を、主ヘッダー+ページ専用ヘッダー全体の実測下端から一度だけ決める構造へ整理。

・初期表示前から正しい本文位置を確保するため、ページ種別をHTML属性として出力。Ajax遷移時も同属性を同期。

・旧来の固定値52pxと `:has()` による重複した本文位置補正を削除。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.16.0-beta.1以降でも撤去されず残った仕組みは継続採用とみなす。

header.phpを含む構造調整を実施。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.16.0-beta.1 の要望・修正内容と照合して確定。

空白をpaddingで補うような構造が残り、ユーザー要望の「実体としてそこにあるヘッダー」になり切らなかった。

次版で実際に求められたこと:0.15系の補正積み重ねを廃止し、ヘッダー・フッター・可視領域を大きく再設計する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, header.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.15.4/assets/css/theme.css
@@ -17,6 +17,9 @@
   --header-top: 0px;
   --header-edge: 212.40625px;
   --context-edge: 212.40625px;
+  --context-nav-height: 40px;
+  --context-shadow-clearance: 12px;
+  --context-stack-height: calc(var(--context-nav-height) + var(--context-shadow-clearance));
   --content-edge-gap: 20px;
   --page-top-clearance: var(--content-edge-gap);
   --content-top: 12px;
@@ -96,6 +99,9 @@
   z-index: 9990;
 }
 .admin-bar .site-header { top: 32px; }
+@media (min-width:768px) {
+  html[data-has-context="true"] .site-header { box-shadow:none; }
+}
 .brand { background:var(--surface); height:119.40625px; position:relative; }
 .brand::after { background:rgba(117,128,146,.48); content:""; height:1px; left:10px; position:absolute; right:10px; top:79px; }
 .brand__name, .brand__role {
@@ -146,9 +152,9 @@
 @keyframes nav-glow { 0%,100% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 0 0 var(--glow); } 50% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 16px 5px var(--glow); } }
 
 
-.page-footer { height:52px; left:0; overflow:visible; padding:6px 0; position:fixed; right:0; top:var(--header-edge); z-index:9992; }
+.page-footer { height:var(--context-stack-height); left:0; overflow:visible; padding:0 0 var(--context-shadow-clearance); position:fixed; right:0; top:var(--header-edge); z-index:9992; }
 .page-footer:empty { display:none; }
-.page-footer .context-nav { height:40px; }
+.page-footer .context-nav { height:var(--context-nav-height); }
 
 .context-nav { --context-accent:var(--green); align-items:center; background:linear-gradient(var(--surface),rgba(237,241,247,.82)); display:grid; gap:10px; grid-template-columns:100px minmax(0,1fr) 100px; height:40px; overflow:hidden; padding:0 12px; white-space:nowrap; }
 .context-nav--schedule { --context-accent:var(--yellow); }
@@ -195,10 +201,10 @@
 @keyframes link-thanks-cascade { 0%,62%,100%{opacity:1;transform:translateY(0)} 72%{opacity:.72;transform:translateY(3px)} 84%{opacity:1;transform:translateY(-3px)} 92%{transform:translateY(0)} }
 @keyframes link-thanks-heartbeat { 0%,64%,100%{transform:scale(1)} 70%{transform:scale(1.035)} 76%{transform:scale(.995)} 82%{transform:scale(1.055)} 90%{transform:scale(1)} }
 
-.site-main { min-height:calc(100vh - var(--footer-height)); padding-top:calc(var(--context-edge) + var(--page-top-clearance)); }
+.site-main { min-height:calc(100vh - var(--footer-height)); padding-top:calc(var(--header-edge) + var(--page-top-clearance)); }
 @media (min-width:768px) {
-  html:has([data-page-footer] .context-nav) .site-main { padding-top:calc(var(--header-edge) + 52px + var(--page-top-clearance)); }
-  html:has([data-page-footer] .context-nav) { scroll-padding-top:calc(var(--header-edge) + 52px + var(--page-top-clearance)); }
+  html[data-has-context="true"] .site-main { padding-top:calc(var(--header-edge) + var(--context-stack-height) + var(--page-top-clearance)); }
+  html[data-has-context="true"] { scroll-padding-top:calc(var(--header-edge) + var(--context-stack-height) + var(--page-top-clearance)); }
   .page-footer { filter:drop-shadow(0 7px 10px rgba(143,158,184,.28)); }
   .context-nav { overflow:visible; }
   .context-nav__years { overflow-x:auto; overflow-y:visible; }
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.15.4/assets/js/theme.js
@@ -68,7 +68,7 @@
     var pageFooterRect = visibleRect(pageFooter);
     var edgeGap = parseFloat(window.getComputedStyle(root).getPropertyValue('--content-edge-gap')) || 0;
     var desktopContext = window.matchMedia('(min-width: 768px)').matches && pageFooterRect && contextRect;
-    var contextEdge = desktopContext ? contextRect.bottom : headerRect.bottom;
+    var contextEdge = desktopContext ? pageFooterRect.bottom : headerRect.bottom;
     var contentTop = Math.max(0, contextEdge + edgeGap);
     var primaryFooterRect = mobileDockRect || footerRect;
     var contentBoundary = desktopContext ? primaryFooterRect : (pageFooterRect || primaryFooterRect);
@@ -916,6 +916,7 @@
         contextSlot.innerHTML = contextHtml;
         document.title = title;
         root.setAttribute('data-current-view', targetView);
+        root.setAttribute('data-has-context', ['profile', 'opus', 'works', 'schedule', 'calendar', 'link'].indexOf(targetView) !== -1 ? 'true' : 'false');
         setPrimaryCurrent(targetView);
         if (addHistory) pushLocation(url, { hoShell: true, view: targetView });
         visibleUrl = window.location.href;
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.15.4/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.15.4
+
+- PC版のページ専用ヘッダーを主ヘッダー直下へ隙間なく接続。
+- ページ専用ヘッダーの影のための余白を下側だけに統一し、主メニューとの間に生じていた溝を撤去。
+- 本文開始位置を、主ヘッダー+ページ専用ヘッダー全体の実測下端から一度だけ決める構造へ整理。
+- 初期表示前から正しい本文位置を確保するため、ページ種別をHTML属性として出力。Ajax遷移時も同属性を同期。
+- 旧来の固定値52pxと `:has()` による重複した本文位置補正を削除。
+
 ## 0.15.3
 - PCのページ専用ヘッダー高をレイアウト基準へ統合し、本文が下へ潜る初期表示をCSSと計測処理の両方で解消。
 - ページ専用ヘッダーにシャドウ用クリアランスを確保し、外周効果を切らずに表示。
changedfunctions.php
--- previous/functions.php
+++ 0.15.4/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.15.3-beta.1' );
+define( 'HO_THEME_VERSION', '0.15.4-beta.1' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedheader.php
--- previous/header.php
+++ 0.15.4/header.php
@@ -5,8 +5,9 @@
  * @package Hiroki_Ohnuma
  */
 $current_view = ho_current_view();
+$has_context = in_array( $current_view, array( 'profile', 'opus', 'works', 'schedule', 'calendar', 'link' ), true );
 ?><!doctype html>
-<html <?php language_attributes(); ?> data-current-view="<?php echo esc_attr( $current_view ); ?>">
+<html <?php language_attributes(); ?> data-current-view="<?php echo esc_attr( $current_view ); ?>" data-has-context="<?php echo $has_context ? 'true' : 'false'; ?>">
 <head>
 	<meta charset="<?php bloginfo( 'charset' ); ?>">
 	<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
changedstyle.css
--- previous/style.css
+++ 0.15.4/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.15.3-beta.1
+Version: 0.15.4-beta.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

固定配置とコンテンツ補正を併用したため。


対象ZIP
hiroki-ohnuma-beta1-0.15.4.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.15.3

当時の改修目的

PC/スマホのページメニュー、Calendar/Tetris/Profileの表示領域問題を修正する。

実装した変更

・PCのページ専用ヘッダー高をレイアウト基準へ統合し、本文が下へ潜る初期表示をCSSと計測処理の両方で解消。

・ページ専用ヘッダーにシャドウ用クリアランスを確保し、外周効果を切らずに表示。

・年号列は収まる場合のみ中央揃え、あふれる場合は最新/選択中の年を表示領域左端へ配置。

・Opus・Works・Scheduleで同じ年号整列処理を共有し、個別補正を追加しない構造へ整理。

・テーマのキャッシュ更新用バージョン定数を実際の配布版と一致。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.15.4以降でも撤去されず残った仕組みは継続採用とみなす。

テーマJS/CSSと読み込みバージョンを更新。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.15.4 の要望・修正内容と照合して確定。

Tetris上部余白、スマホフッター、スクロール領域の問題が残った。

次版で実際に求められたこと:主ヘッダーとページ専用ヘッダーの積層、固定表示、モバイル構造を整理する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.15.3/assets/css/theme.css
@@ -146,9 +146,9 @@
 @keyframes nav-glow { 0%,100% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 0 0 var(--glow); } 50% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 16px 5px var(--glow); } }
 
 
-.page-footer { height:40px; left:0; position:fixed; right:0; top:var(--header-edge); z-index:9992; }
+.page-footer { height:52px; left:0; overflow:visible; padding:6px 0; position:fixed; right:0; top:var(--header-edge); z-index:9992; }
 .page-footer:empty { display:none; }
-.page-footer .context-nav { height:100%; }
+.page-footer .context-nav { height:40px; }
 
 .context-nav { --context-accent:var(--green); align-items:center; background:linear-gradient(var(--surface),rgba(237,241,247,.82)); display:grid; gap:10px; grid-template-columns:100px minmax(0,1fr) 100px; height:40px; overflow:hidden; padding:0 12px; white-space:nowrap; }
 .context-nav--schedule { --context-accent:var(--yellow); }
@@ -159,7 +159,8 @@
 .context-nav--profile .context-nav__years button { width:100%; }
 .context-nav a,.context-nav button { border:0; border-radius:500px; color:#747c89; cursor:pointer; font-size:13px; font-weight:800; min-width:30px; padding:4px 10px; text-align:center; text-decoration:none; }
 .context-nav button { font-family:inherit; }
-.context-nav__years { align-items:center; display:flex; gap:9px; height:100%; justify-content:center; min-width:0; overflow:auto; padding:6px 8px; scrollbar-width:none; }
+.context-nav__years { align-items:center; display:flex; gap:9px; height:100%; justify-content:center; min-width:0; overflow-x:auto; overflow-y:visible; padding:6px 8px; scrollbar-width:none; }
+.context-nav__years.is-overflowing { justify-content:flex-start; }
 .context-nav__years::-webkit-scrollbar { display:none; }
 .context-nav__section-link { color:#747c89; }
 .context-nav__section-link.is-current { color:var(--context-accent); }
@@ -195,6 +196,13 @@
 @keyframes link-thanks-heartbeat { 0%,64%,100%{transform:scale(1)} 70%{transform:scale(1.035)} 76%{transform:scale(.995)} 82%{transform:scale(1.055)} 90%{transform:scale(1)} }
 
 .site-main { min-height:calc(100vh - var(--footer-height)); padding-top:calc(var(--context-edge) + var(--page-top-clearance)); }
+@media (min-width:768px) {
+  html:has([data-page-footer] .context-nav) .site-main { padding-top:calc(var(--header-edge) + 52px + var(--page-top-clearance)); }
+  html:has([data-page-footer] .context-nav) { scroll-padding-top:calc(var(--header-edge) + 52px + var(--page-top-clearance)); }
+  .page-footer { filter:drop-shadow(0 7px 10px rgba(143,158,184,.28)); }
+  .context-nav { overflow:visible; }
+  .context-nav__years { overflow-x:auto; overflow-y:visible; }
+}
 html[data-view-transitioning="true"] .site-main,html[data-view-transitioning="true"] .context-nav { will-change:opacity; }
 .ho-js[data-current-view="home"]:not(.is-view-ready) .site-main,
 .ho-js[data-current-view="home"]:not(.is-view-ready) [data-page-footer] > * { opacity:0; }
@@ -533,7 +541,7 @@
   .site-footer { display:none; }
   .mobile-dock { bottom:0; display:block; left:0; position:fixed; right:0; z-index:9999; }
   :root.is-web-app .mobile-dock { background:rgba(119,128,143,.94); padding-bottom:env(safe-area-inset-bottom, 0px); }
-  .page-footer { bottom:var(--mobile-dock-height); height:56px; top:auto; }
+  .page-footer { bottom:var(--mobile-dock-height); height:56px; padding:0; top:auto; }
   .context-nav { background:rgba(237,241,247,.97); bottom:auto; box-shadow:0 -5px 14px rgba(163,177,198,.5); display:grid; gap:0; grid-template-columns:20% minmax(0,60%) 20%; height:56px; left:auto; overflow:hidden; padding:0; position:relative; right:auto; z-index:auto; }
   .context-nav__section-link { align-items:center; align-self:stretch; background:linear-gradient(90deg,#fff,var(--surface)); border-radius:16px; display:flex; justify-content:center; padding:0 5px; position:relative; z-index:2; }
   .context-nav__section-link:last-child { background:linear-gradient(90deg,var(--surface),#fff); }
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.15.3/assets/js/theme.js
@@ -177,15 +177,27 @@
     message.setAttribute('data-link-thanks-animation', animations[choice]);
   }
 
-  function centerContextNavigation(nav) {
+  function alignContextNavigation(nav) {
     var current = nav && nav.querySelector('[aria-current="page"]');
     if (!current) return;
     var strip = nav.querySelector('.context-nav__years') || nav;
     if (nav.classList.contains('context-nav--profile')) {
+      strip.classList.remove('is-overflowing');
       strip.scrollLeft = 0;
       return;
     }
-    strip.scrollLeft = Math.max(0, current.offsetLeft - strip.offsetLeft - ((strip.clientWidth - current.offsetWidth) / 2));
+
+    var overflowing = strip.scrollWidth > strip.clientWidth + 1;
+    strip.classList.toggle('is-overflowing', overflowing);
+    if (!overflowing) {
+      strip.scrollLeft = 0;
+      return;
+    }
+
+    var stripRect = strip.getBoundingClientRect();
+    var currentRect = current.getBoundingClientRect();
+    var paddingLeft = parseFloat(window.getComputedStyle(strip).paddingLeft) || 0;
+    strip.scrollLeft = Math.max(0, strip.scrollLeft + currentRect.left - stripRect.left - paddingLeft);
   }
 
   function calendarDateFromRoot(calendarRoot) {
@@ -252,7 +264,7 @@
         event.preventDefault();
         links.forEach(function (item) { item.removeAttribute('aria-current'); });
         link.setAttribute('aria-current', 'page');
-        centerContextNavigation(nav);
+        alignContextNavigation(nav);
       };
       link.addEventListener('click', onClick);
       registerViewCleanup(function () { link.removeEventListener('click', onClick); });
@@ -305,7 +317,7 @@
     var nav = document.querySelector('.context-nav');
     var sections = Array.from(document.querySelectorAll('[data-context-section]'));
     if (!nav) return;
-    centerContextNavigation(nav);
+    alignContextNavigation(nav);
     var links = Array.from(nav.querySelectorAll('[data-context-key]'));
     if (!sections.length) {
       if (root.getAttribute('data-current-view') === 'calendar') {
@@ -315,7 +327,7 @@
             if (link.getAttribute('data-context-key') === requestedYear) link.setAttribute('aria-current', 'page');
             else link.removeAttribute('aria-current');
           });
-          centerContextNavigation(nav);
+          alignContextNavigation(nav);
         }
         setupCalendarYearNavigation(nav, links);
       }
@@ -327,7 +339,7 @@
         if (link.getAttribute('data-context-key') === key) link.setAttribute('aria-current', 'page');
         else link.removeAttribute('aria-current');
       });
-      centerContextNavigation(nav);
+      alignContextNavigation(nav);
     }
 
     function targetForLocation() {
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.15.3/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.15.3
+- PCのページ専用ヘッダー高をレイアウト基準へ統合し、本文が下へ潜る初期表示をCSSと計測処理の両方で解消。
+- ページ専用ヘッダーにシャドウ用クリアランスを確保し、外周効果を切らずに表示。
+- 年号列は収まる場合のみ中央揃え、あふれる場合は最新/選択中の年を表示領域左端へ配置。
+- Opus・Works・Scheduleで同じ年号整列処理を共有し、個別補正を追加しない構造へ整理。
+- テーマのキャッシュ更新用バージョン定数を実際の配布版と一致。
+
 ## 0.15.2
 - PCではページ専用ナビゲーションを主ヘッダー直下へ戻し、モバイルでは従来どおり下部へ配置。
 - PCの縦型Backボタンをページ専用ヘッダー下端から展開。
changedfunctions.php
--- previous/functions.php
+++ 0.15.3/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.14.5-beta.1' );
+define( 'HO_THEME_VERSION', '0.15.3-beta.1' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.15.3/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.15.2-beta.1
+Version: 0.15.3-beta.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

ページ別補正が増え、共通可視領域の基準が統一されていなかった。


対象ZIP
hiroki-ohnuma-beta1-0.15.3.zip


再利用すべき判断基準

  • 同じ役割の処理は一つの関数・一つの状態・一つのDOMに集約する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.15.2

当時の改修目的

モジュール表示、ページ専用ナビ、フォーカス遷移の細部を調整する。

実装した変更

・PCではページ専用ナビゲーションを主ヘッダー直下へ戻し、モバイルでは従来どおり下部へ配置。

・PCの縦型Backボタンをページ専用ヘッダー下端から展開。

・単一モジュール終了時の二重スクロール補正を廃止し、一覧復帰位置を一度だけ確定する方式へ整理。

・カレンダーのスマホ横スワイプとPC左右キーによる月移動を追加。

・カレンダー月切り替えを0.86秒の同時クロスディゾルブへ変更。

・同一ビュー再読込時の不要な状態保存を削除。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.15.3以降でも撤去されず残った仕組みは継続採用とみなす。

CSS/JS中心に表示と操作を改善。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.15.3 の要望・修正内容と照合して確定。

後続版でヘッダー構造そのものの再設計が必要になった。

次版で実際に求められたこと:PC/スマホのページメニュー、Calendar/Tetris/Profileの表示領域問題を修正する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.15.2/assets/css/theme.css
@@ -16,6 +16,7 @@
   --header-height: 212.40625px;
   --header-top: 0px;
   --header-edge: 212.40625px;
+  --context-edge: 212.40625px;
   --content-edge-gap: 20px;
   --page-top-clearance: var(--content-edge-gap);
   --content-top: 12px;
@@ -145,7 +146,7 @@
 @keyframes nav-glow { 0%,100% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 0 0 var(--glow); } 50% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 16px 5px var(--glow); } }
 
 
-.page-footer { bottom:var(--footer-height); height:40px; left:0; position:fixed; right:0; z-index:9992; }
+.page-footer { height:40px; left:0; position:fixed; right:0; top:var(--header-edge); z-index:9992; }
 .page-footer:empty { display:none; }
 .page-footer .context-nav { height:100%; }
 
@@ -193,7 +194,7 @@
 @keyframes link-thanks-cascade { 0%,62%,100%{opacity:1;transform:translateY(0)} 72%{opacity:.72;transform:translateY(3px)} 84%{opacity:1;transform:translateY(-3px)} 92%{transform:translateY(0)} }
 @keyframes link-thanks-heartbeat { 0%,64%,100%{transform:scale(1)} 70%{transform:scale(1.035)} 76%{transform:scale(.995)} 82%{transform:scale(1.055)} 90%{transform:scale(1)} }
 
-.site-main { min-height:calc(100vh - var(--footer-height)); padding-top:calc(var(--header-height) + var(--page-top-clearance)); }
+.site-main { min-height:calc(100vh - var(--footer-height)); padding-top:calc(var(--context-edge) + var(--page-top-clearance)); }
 html[data-view-transitioning="true"] .site-main,html[data-view-transitioning="true"] .context-nav { will-change:opacity; }
 .ho-js[data-current-view="home"]:not(.is-view-ready) .site-main,
 .ho-js[data-current-view="home"]:not(.is-view-ready) [data-page-footer] > * { opacity:0; }
@@ -370,7 +371,7 @@
 .event-focus__note { background:rgba(245,248,251,.48); border:1px solid rgba(255,255,255,.58); border-radius:11px; box-shadow:inset 1px 1px 3px rgba(143,158,184,.2),inset -1px -1px 3px rgba(255,255,255,.72); font-size:16px; font-weight:700; line-height:1.45; padding:12px 14px; }
 .event-focus__note p { margin:0 0 1em; }
 .event-focus__note p:last-child { margin-bottom:0; }
-.focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--header-edge); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
+.focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--context-edge); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
 .focus-return-control svg { fill:none; height:28px; overflow:visible; stroke:currentColor; stroke-linecap:round; stroke-linejoin:round; stroke-width:3.4; width:28px; }
 .focus-page.is-focus .focus-return-control { opacity:1; pointer-events:auto; transform:translateY(0); }
 .focus-return-control:active { box-shadow:var(--neu-inset); transform:translateY(1px); }
@@ -493,6 +494,7 @@
 @media (max-width: 767px) {
   :root { --header-height:149px; --header-edge:149px; --page-top-clearance:8px; --content-top:4px; --content-inline:14px; --content-bottom:calc(var(--mobile-dock-height) + 62px); --module-gap:22px; --module-section-gap:28px; --module-padding:14px; --module-radius:20px; --record-shell-inline:14px; }
   body { padding-bottom:var(--mobile-dock-height); }
+  .site-main { padding-top:calc(var(--header-edge) + var(--page-top-clearance)); }
   html[data-current-view="tetris"] .tetris-page { bottom:var(--mobile-dock-height); }
   .admin-bar .site-header { top:46px; }
   .brand { height:79px; }
@@ -531,7 +533,7 @@
   .site-footer { display:none; }
   .mobile-dock { bottom:0; display:block; left:0; position:fixed; right:0; z-index:9999; }
   :root.is-web-app .mobile-dock { background:rgba(119,128,143,.94); padding-bottom:env(safe-area-inset-bottom, 0px); }
-  .page-footer { bottom:var(--mobile-dock-height); height:56px; }
+  .page-footer { bottom:var(--mobile-dock-height); height:56px; top:auto; }
   .context-nav { background:rgba(237,241,247,.97); bottom:auto; box-shadow:0 -5px 14px rgba(163,177,198,.5); display:grid; gap:0; grid-template-columns:20% minmax(0,60%) 20%; height:56px; left:auto; overflow:hidden; padding:0; position:relative; right:auto; z-index:auto; }
   .context-nav__section-link { align-items:center; align-self:stretch; background:linear-gradient(90deg,#fff,var(--surface)); border-radius:16px; display:flex; justify-content:center; padding:0 5px; position:relative; z-index:2; }
   .context-nav__section-link:last-child { background:linear-gradient(90deg,var(--surface),#fff); }
@@ -602,6 +604,19 @@
   .keep-phrase { white-space:normal; }
 }
 
+
+.calendar-dissolve-overlay {
+  overflow:hidden;
+  pointer-events:none;
+  position:fixed;
+  z-index:9987;
+}
+.calendar-dissolve-overlay .calendar-page {
+  height:100%;
+  margin:0;
+  padding:0;
+  width:100%;
+}
 .calendar-page--jaikki { padding-top:32px; }
 .calendar-page--jaikki .jaikki-calendar-root { margin:0 auto; max-width:1180px; }
 .calendar-page--jaikki .jaikki-calendar {
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.15.2/assets/js/theme.js
@@ -67,16 +67,20 @@
     var contextRect = visibleRect(context);
     var pageFooterRect = visibleRect(pageFooter);
     var edgeGap = parseFloat(window.getComputedStyle(root).getPropertyValue('--content-edge-gap')) || 0;
-    var contentTop = Math.max(0, headerRect.bottom + edgeGap);
+    var desktopContext = window.matchMedia('(min-width: 768px)').matches && pageFooterRect && contextRect;
+    var contextEdge = desktopContext ? contextRect.bottom : headerRect.bottom;
+    var contentTop = Math.max(0, contextEdge + edgeGap);
     var primaryFooterRect = mobileDockRect || footerRect;
-    var contentBoundary = pageFooterRect || primaryFooterRect;
+    var contentBoundary = desktopContext ? primaryFooterRect : (pageFooterRect || primaryFooterRect);
     var contentBottom = contentBoundary ? Math.max(contentTop, Math.min(window.innerHeight, contentBoundary.top - edgeGap)) : Math.max(contentTop, window.innerHeight - edgeGap);
+    root.classList.toggle('has-page-context', Boolean(pageFooterRect && contextRect));
     var bottomChrome = Math.max(0, window.innerHeight - contentBottom);
     var contentHeight = Math.max(0, contentBottom - contentTop);
 
     root.style.setProperty('--header-height', Math.max(0, headerRect.height).toFixed(2) + 'px');
     root.style.setProperty('--header-top', Math.max(0, headerRect.top).toFixed(2) + 'px');
     root.style.setProperty('--header-edge', Math.max(0, headerRect.bottom).toFixed(2) + 'px');
+    root.style.setProperty('--context-edge', Math.max(0, contextEdge).toFixed(2) + 'px');
     root.style.setProperty('--footer-rendered-height', elementHeight(footer).toFixed(2) + 'px');
     root.style.setProperty('--mobile-dock-rendered-height', elementHeight(mobileDock).toFixed(2) + 'px');
     root.style.setProperty('--page-footer-height', elementHeight(pageFooter).toFixed(2) + 'px');
@@ -252,6 +256,48 @@
       };
       link.addEventListener('click', onClick);
       registerViewCleanup(function () { link.removeEventListener('click', onClick); });
+    });
+  }
+
+  function setupCalendarInteraction() {
+    var calendarRoot = document.querySelector('.jaikki-calendar-root');
+    if (!calendarRoot) return;
+
+    function shiftMonth(offset) {
+      var current = calendarDateFromRoot(calendarRoot);
+      var target = new Date(current.year, current.month - 1 + offset, 1);
+      navigateCalendarTo(target.getFullYear(), target.getMonth());
+    }
+
+    var touchStart = null;
+    var onTouchStart = function (event) {
+      if (event.touches.length !== 1) return;
+      touchStart = { x: event.touches[0].clientX, y: event.touches[0].clientY };
+    };
+    var onTouchEnd = function (event) {
+      if (!touchStart || !event.changedTouches.length) return;
+      var deltaX = event.changedTouches[0].clientX - touchStart.x;
+      var deltaY = event.changedTouches[0].clientY - touchStart.y;
+      touchStart = null;
+      if (Math.abs(deltaX) < 48 || Math.abs(deltaX) <= Math.abs(deltaY) * 1.25) return;
+      shiftMonth(deltaX < 0 ? 1 : -1);
+    };
+    var onKeyDown = function (event) {
+      if (root.getAttribute('data-current-view') !== 'calendar') return;
+      if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
+      if (event.target && event.target.closest('input,textarea,select,[contenteditable="true"]')) return;
+      if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
+      event.preventDefault();
+      shiftMonth(event.key === 'ArrowRight' ? 1 : -1);
+    };
+
+    calendarRoot.addEventListener('touchstart', onTouchStart, { passive: true });
+    calendarRoot.addEventListener('touchend', onTouchEnd, { passive: true });
+    window.addEventListener('keydown', onKeyDown);
+    registerViewCleanup(function () {
+      calendarRoot.removeEventListener('touchstart', onTouchStart);
+      calendarRoot.removeEventListener('touchend', onTouchEnd);
+      window.removeEventListener('keydown', onKeyDown);
     });
   }
 
@@ -433,33 +479,6 @@
       });
     }
 
-    function animateTargetBelowHeader(target) {
-      syncLayoutMetrics();
-      var header = document.getElementById('site-header');
-      if (!header) return Promise.resolve();
-      var startY = window.scrollY;
-      var destination = startY + target.getBoundingClientRect().top - header.getBoundingClientRect().bottom - 8;
-      var maxY = Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
-      destination = Math.max(0, Math.min(maxY, destination));
-      var delta = destination - startY;
-      if (reduceFocusMotion || Math.abs(delta) < 1) {
-        window.scrollTo(0, destination);
-        return Promise.resolve();
-      }
-      var duration = motionTime(Math.min(480, Math.max(280, 250 + (Math.abs(delta) * .32))));
-      return new Promise(function (resolve) {
-        var startedAt = null;
-        var step = function (time) {
-          if (startedAt === null) startedAt = time;
-          var progress = Math.min(1, (time - startedAt) / duration);
-          var eased = 1 - Math.pow(1 - progress, 4);
-          window.scrollTo(0, startY + (delta * eased));
-          if (progress < 1) window.requestAnimationFrame(step);
-          else resolve();
-        };
-        window.requestAnimationFrame(step);
-      });
-    }
 
     function clearParadeLayer() {
       resetEventSkullParades(paradeLayer);
@@ -552,18 +571,20 @@
       restoredItem.classList.remove('is-expanding', 'has-focus-details');
       var restore = function () {
         if (thisTransition !== transitionId || activeItem !== restoredItem) return;
-        var visualTop = restoredItem.getBoundingClientRect().top;
         page.classList.add('is-focus-returning');
         originParent.insertBefore(restoredItem, originNextSibling);
         stage.hidden = true;
         page.classList.remove('is-focus', 'is-focus-leaving', 'is-focus-closing');
-        var insertedTop = restoredItem.getBoundingClientRect().top;
+
+        syncLayoutMetrics();
+        var headerBoundary = parseFloat(window.getComputedStyle(root).getPropertyValue('--context-edge')) || 0;
+        var destination = window.scrollY + restoredItem.getBoundingClientRect().top - headerBoundary - 8;
         var previousBehavior = root.style.scrollBehavior;
         root.style.scrollBehavior = 'auto';
-        window.scrollTo(0, Math.max(0, window.scrollY + insertedTop - visualTop));
+        window.scrollTo(0, Math.max(0, destination));
         root.style.scrollBehavior = previousBehavior;
+
         window.requestAnimationFrame(function () {
-          animateTargetBelowHeader(restoredItem).then(function () {
           if (thisTransition !== transitionId || activeItem !== restoredItem) return;
           page.classList.remove('is-focus-returning');
           activeItem = null;
@@ -572,12 +593,10 @@
           originNextSibling = null;
           document.title = archiveTitle;
           window.setTimeout(function () {
-            if (activeItem === restoredItem) return;
             restoredItem.classList.remove('is-picked');
             restoredItem.removeAttribute('aria-current');
             if (restoredSection) restoredSection.classList.remove('is-picked-section');
           }, motionTime(220));
-          });
         });
       };
       if (reduceFocusMotion || !hadDetails) restore();
@@ -689,6 +708,7 @@
     setupTabs();
     setupLinkThanks();
     setupContextNavigation();
+    setupCalendarInteraction();
     setupEventSkullParades();
     setupListFocus();
     if (!settings.preserveScroll && !document.querySelector('.focus-page.is-focus')) activateLocationTarget();
@@ -747,6 +767,28 @@
       var animations = [animateOpacity(main, entering)];
       if (context) animations.push(animateOpacity(context, entering));
       return Promise.all(animations);
+    }
+
+    function animateCalendarDissolve(main, oldCalendarPage) {
+      if (!oldCalendarPage || reduceMotion || !main.animate) return animateViewSurface(main, document.querySelector('[data-view-context] .context-nav'), true);
+      var oldRect = oldCalendarPage.getBoundingClientRect();
+      var overlay = document.createElement('div');
+      overlay.className = 'calendar-dissolve-overlay';
+      overlay.style.left = oldRect.left + 'px';
+      overlay.style.top = oldRect.top + 'px';
+      overlay.style.width = oldRect.width + 'px';
+      overlay.style.height = oldRect.height + 'px';
+      overlay.appendChild(oldCalendarPage);
+      document.body.appendChild(overlay);
+      main.style.opacity = '0';
+      var incoming = main.animate([{ opacity: 0 }, { opacity: 1 }], { duration: viewFadeDuration, easing: 'linear', fill: 'both' });
+      var outgoing = overlay.animate([{ opacity: 1 }, { opacity: 0 }], { duration: viewFadeDuration, easing: 'linear', fill: 'both' });
+      return Promise.all([incoming.finished.catch(function () {}), outgoing.finished.catch(function () {})]).then(function () {
+        incoming.cancel();
+        outgoing.cancel();
+        main.style.removeProperty('opacity');
+        overlay.remove();
+      });
     }
 
     function loadStyles(urls) {
@@ -833,15 +875,19 @@
       var main = document.querySelector('.site-main');
       var contextSlot = document.querySelector('[data-view-context]');
       var fromView = currentView();
+      var calendarDissolve = fromView === 'calendar' && targetView === 'calendar';
+      var oldCalendarPage = calendarDissolve ? document.querySelector('.calendar-page') : null;
+      if (oldCalendarPage) oldCalendarPage = oldCalendarPage.cloneNode(true);
       root.setAttribute('data-view-transitioning', 'true');
       var request = !forceRequest && viewStates.has(targetView) ? Promise.resolve({ state: viewStates.get(targetView) }) : requestView(targetView, url).then(function (payload) { return { payload: payload }; });
       var oldContext = contextSlot ? contextSlot.firstElementChild : null;
-      var leaving = animateViewSurface(main, oldContext, false);
+      var leaving = calendarDissolve ? Promise.resolve() : animateViewSurface(main, oldContext, false);
 
       return Promise.all([request, leaving]).then(function (results) {
         var result = results[0];
         disposeView();
-        captureView(fromView, main, contextSlot);
+        if (fromView === targetView) main.replaceChildren();
+        else captureView(fromView, main, contextSlot);
         var contextHtml = '';
         var title = '';
         var assets = null;
@@ -864,7 +910,7 @@
         window.scrollTo(0, typeof restoredScroll === 'number' ? restoredScroll : 0);
         return loadAssets(assets).then(function () {
           initializeView({ preserveScroll: typeof restoredScroll === 'number' });
-          return animateViewSurface(main, contextSlot.firstElementChild, true);
+          return calendarDissolve ? animateCalendarDissolve(main, oldCalendarPage) : animateViewSurface(main, contextSlot.firstElementChild, true);
         });
       }).catch(function () {
         window.location.href = url;
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.15.2/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.15.2
+- PCではページ専用ナビゲーションを主ヘッダー直下へ戻し、モバイルでは従来どおり下部へ配置。
+- PCの縦型Backボタンをページ専用ヘッダー下端から展開。
+- 単一モジュール終了時の二重スクロール補正を廃止し、一覧復帰位置を一度だけ確定する方式へ整理。
+- カレンダーのスマホ横スワイプとPC左右キーによる月移動を追加。
+- カレンダー月切り替えを0.86秒の同時クロスディゾルブへ変更。
+- 同一ビュー再読込時の不要な状態保存を削除。
+
 
 ## 0.15.1
 
changedstyle.css
--- previous/style.css
+++ 0.15.2/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.15.1-beta.1
+Version: 0.15.2-beta.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

表示上の症状を修正したが、DOM構造と座標基準が複数存在した。


対象ZIP
hiroki-ohnuma-beta1-0.15.2.zip


再利用すべき判断基準

  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.15.1

当時の改修目的

0.15.0のWorks統合後の表示・遷移・モジュール差異を修正する。

実装した変更

・Calendar mobile clearance now preserves the neumorphic shadow.

・Calendar partial navigation now cooperates with the plugin navigation event.

・Works `Released / On-Air` label uses a shared compact crossfade label instead of widening the module.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.15.2以降でも撤去されず残った仕組みは継続採用とみなす。

Works/Opus/Scheduleの共通表示を追加調整。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.15.2 の要望・修正内容と照合して確定。

固定ヘッダー/フッターと可視領域の補正がまだ積み重なっていた。

次版で実際に求められたこと:モジュール表示、ページ専用ナビ、フォーカス遷移の細部を調整する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, inc/template-tags.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.15.1/assets/css/theme.css
@@ -287,6 +287,21 @@
 .works-row__details .detail-pill--lyric { color:#000080; }
 .works-row__notes { grid-column:1 / -1; }
 .works-label-short { display:none; }
+
+.works-air-label { display:grid; height:1em; line-height:1; overflow:hidden; place-items:center; position:relative; width:100%; }
+.works-air-label > span { animation:works-air-cycle 3.72s linear infinite; grid-area:1 / 1; opacity:0; }
+.works-air-label > span:first-child { animation-delay:0s; }
+.works-air-label > span:last-child { animation-delay:-1.86s; }
+@keyframes works-air-cycle {
+  0%,26.8817% { opacity:1; }
+  50%,76.8817% { opacity:0; }
+  100% { opacity:1; }
+}
+@media (prefers-reduced-motion: reduce) {
+  .works-air-label > span { animation:none; }
+  .works-air-label > span:first-child { opacity:1; }
+  .works-air-label > span:last-child { display:none; }
+}
 .work-row__details dd { min-height:29px; }
 .work-row__details small { display:block; font-size:10px; text-transform:uppercase; }
 
@@ -608,8 +623,8 @@
     box-sizing:border-box;
     height:var(--viewport-content-height);
     min-height:0;
-    overflow:auto;
-    padding:4px 8px;
+    overflow:visible;
+    padding:12px 14px;
   }
   .calendar-page--jaikki .jaikki-calendar-root { min-height:100%; width:100%; }
   .profile-page { height:var(--viewport-content-height); min-height:0; padding-bottom:0; }
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.15.1/assets/js/theme.js
@@ -878,6 +878,7 @@
     document.addEventListener('ho:locationchange', function (event) { visibleUrl = event.detail.url; });
     document.addEventListener('ho:calendar:direct', function (event) {
       if (!event.detail || !event.detail.url) return;
+      event.preventDefault();
       scrollPositions.set(visibleUrl, window.scrollY);
       loadView(event.detail.url, true, undefined, true);
     });
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.15.1/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,9 @@
+
+## 0.15.1
+
+- Calendar mobile clearance now preserves the neumorphic shadow.
+- Calendar partial navigation now cooperates with the plugin navigation event.
+- Works `Released / On-Air` label uses a shared compact crossfade label instead of widening the module.
 # 0.15.0
 
 - Works now uses the same shared record-module layout and dimensions as Opus and Schedule; bespoke Works sizing rules were removed.
changedinc/template-tags.php
--- previous/inc/template-tags.php
+++ 0.15.1/inc/template-tags.php
@@ -548,7 +548,7 @@
 		<a class="capsule work-row__name" data-focus-link href="<?php echo esc_url( ho_focus_url( 'credit', $post_id ) ); ?>"><?php echo esc_html( get_the_title( $post_id ) ); ?></a>
 		<dl class="work-row__details works-row__details">
 			<?php foreach ( ho_credit_fields() as $credit_key => $field ) : $field_value = get_post_meta( $post_id, '_ho_credit_' . $credit_key, true ); if ( ! trim( $field_value ) ) continue; ?>
-				<dt class="detail-pill detail-pill--<?php echo esc_attr( $field['class'] ); ?>"><span class="works-label-full"><?php echo esc_html( $field['label'] ); ?></span><span class="works-label-short"><?php echo esc_html( ho_works_short_label( $field['label'] ) ); ?></span></dt>
+				<dt class="detail-pill detail-pill--<?php echo esc_attr( $field['class'] ); ?>"<?php echo 'released' === $credit_key ? ' aria-label="Released / On-Air"' : ''; ?>><?php if ( 'released' === $credit_key ) : ?><span class="works-air-label" aria-hidden="true"><span>Released</span><span>On-Air</span></span><?php else : ?><span class="works-label-full"><?php echo esc_html( $field['label'] ); ?></span><span class="works-label-short"><?php echo esc_html( ho_works_short_label( $field['label'] ) ); ?></span><?php endif; ?></dt>
 				<dd><?php echo wp_kses( ho_lines( $field_value, in_array( $credit_key, array( 'music', 'lyric_music', 'lyric', 'arranger', 'arrange', 'musicians' ), true ) ), ho_phrase_html() ); ?></dd>
 			<?php endforeach; ?>
 		</dl>
changedstyle.css
--- previous/style.css
+++ 0.15.1/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.15.0-beta.1
+Version: 0.15.1-beta.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

既存の測定値・CSS変数・固定配置を維持した局所修正だった。


対象ZIP
hiroki-ohnuma-beta1-0.15.1.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.15.0

当時の改修目的

WorksをOpus/Scheduleと同じモジュール体系へ統合し、Released/On-Air表現とBack対応を加える。

実装した変更

・Works now uses the same shared record-module layout and dimensions as Opus and Schedule; bespoke Works sizing rules were removed.

・Works single-focus view now uses the shared floating Back control and returns to the originating item position in the Works archive.

・Focus navigation support is shared across Opus, Works, and Schedule without a Works-only controller.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.15.1以降でも撤去されず残った仕組みは継続採用とみなす。

Works専用レイアウト依存を減らし、共通モジュール/フォーカス/Backへ寄せた。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.15.1 の要望・修正内容と照合して確定。

ページ専用フッターと可視領域の関係に不整合が残り、0.15.xで継続修正。

次版で実際に求められたこと:0.15.0のWorks統合後の表示・遷移・モジュール差異を修正する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, inc/template-tags.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.15.0/assets/css/theme.css
@@ -274,9 +274,6 @@
 .year-section__title::after { background:linear-gradient(90deg,rgba(143,158,184,.58),rgba(255,255,255,.86)); box-shadow:0 1px 0 rgba(255,255,255,.75); content:""; flex:1 1 auto; height:1px; min-width:24px; }
 .work-row { display:grid; gap:28px; grid-template-columns:170px minmax(0,1fr); margin:0; }
 .work-row__name { align-self:start; background-color:var(--work-accent,#dd9933); text-align:center; }
-.works-row__name { background-color:var(--blue); border:0; margin:0; }
-.works-row__details .detail-pill { font-size:13px; overflow-wrap:anywhere; padding-left:6px; padding-right:6px; }
-.works-row__details .detail-pill--arrange { font-size:11.5px; }
 .works-row__details .detail-pill--title { color:#cc99ff; }
 .works-row__details .detail-pill--music { color:#3366ff; }
 .works-row__details .detail-pill--arrange { color:#00ccff; }
@@ -288,8 +285,7 @@
 .works-row__details .detail-pill--dedication { color:#008000; }
 .works-row__details .detail-pill--arranger { color:#800080; }
 .works-row__details .detail-pill--lyric { color:#000080; }
-.works-row__details dd { background:rgba(239,246,251,.58); }
-.works-row__notes { grid-column:2; }
+.works-row__notes { grid-column:1 / -1; }
 .works-label-short { display:none; }
 .work-row__details dd { min-height:29px; }
 .work-row__details small { display:block; font-size:10px; text-transform:uppercase; }
@@ -504,7 +500,7 @@
   .work-row { gap:10px; grid-template-columns:112px minmax(0,1fr); }
   .event-card__date { font-size:12px; margin:0 0 8px; padding:8px 12px; }
   .work-row__name { font-size:12px; padding:8px 5px; }
-  .opus-page .work-row__name { justify-self:start; max-width:100%; padding-left:16px; padding-right:16px; width:max-content; }
+  .focus-page .work-row__name { justify-self:start; max-width:100%; padding-left:16px; padding-right:16px; width:max-content; }
   .event-card__title { font-size:20px; }
   .detail-list { gap:3px 12px; grid-template-columns:58px minmax(0,1fr); }
   .work-row__details { gap:3px 7px; grid-template-columns:100px minmax(0,1fr); }
@@ -572,8 +568,7 @@
   .link-list { padding-left:8px; padding-right:8px; }
   .work-row { display:block; }
   .work-row__name { margin-bottom:12px; width:100%; }
-  .opus-page .work-row__name { width:max-content; }
-  .works-row__notes { grid-column:auto; }
+  .focus-page .work-row__name { width:max-content; }
   .detail-list,.work-row__details { gap:4px 7px; grid-template-columns:88px minmax(0,1fr); }
   .detail-pill { font-size:11px; }
   .detail-list dd,.work-row__details dd { font-size:12px; overflow-wrap:anywhere; }
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.15.0/assets/js/theme.js
@@ -388,7 +388,7 @@
     stage.setAttribute('data-focus-stage', '');
     stage.hidden = true;
     page.insertBefore(stage, list);
-    var supportsFloatingReturn = ['opus', 'schedule'].indexOf(page.getAttribute('data-view-root')) !== -1;
+    var supportsFloatingReturn = ['opus', 'works', 'schedule'].indexOf(page.getAttribute('data-view-root')) !== -1;
     var returnControl = null;
     if (supportsFloatingReturn) {
       returnControl = document.createElement('button');
@@ -890,7 +890,7 @@
       if (!targetView) return;
       if (targetView === currentView()) {
         event.preventDefault();
-        if (document.querySelector('.focus-page.is-focus') && ['opus', 'schedule'].indexOf(targetView) !== -1) {
+        if (document.querySelector('.focus-page.is-focus') && ['opus', 'works', 'schedule'].indexOf(targetView) !== -1) {
           document.dispatchEvent(new CustomEvent('ho:resetfocus'));
           return;
         }
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.15.0/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,9 @@
+# 0.15.0
+
+- Works now uses the same shared record-module layout and dimensions as Opus and Schedule; bespoke Works sizing rules were removed.
+- Works single-focus view now uses the shared floating Back control and returns to the originating item position in the Works archive.
+- Focus navigation support is shared across Opus, Works, and Schedule without a Works-only controller.
+
 ## 0.14.2-beta.1
 
 - Added the 0.86-second initial home fade-in using the shared transition duration.
changedinc/template-tags.php
--- previous/inc/template-tags.php
+++ 0.15.0/inc/template-tags.php
@@ -544,8 +544,8 @@
 function ho_credit_row( $post_id ) {
 	$post_id = absint( $post_id );
 	?>
-	<article class="module module--record work-row works-row" id="works-credit-<?php echo esc_attr( $post_id ); ?>" data-focus-item data-focus-type="credit" data-focus-id="<?php echo esc_attr( $post_id ); ?>">
-		<a class="capsule work-row__name works-row__name" data-focus-link href="<?php echo esc_url( ho_focus_url( 'credit', $post_id ) ); ?>"><?php echo esc_html( get_the_title( $post_id ) ); ?></a>
+	<article class="module module--record work-row" id="works-credit-<?php echo esc_attr( $post_id ); ?>" data-focus-item data-focus-type="credit" data-focus-id="<?php echo esc_attr( $post_id ); ?>" style="--work-accent:var(--blue)">
+		<a class="capsule work-row__name" data-focus-link href="<?php echo esc_url( ho_focus_url( 'credit', $post_id ) ); ?>"><?php echo esc_html( get_the_title( $post_id ) ); ?></a>
 		<dl class="work-row__details works-row__details">
 			<?php foreach ( ho_credit_fields() as $credit_key => $field ) : $field_value = get_post_meta( $post_id, '_ho_credit_' . $credit_key, true ); if ( ! trim( $field_value ) ) continue; ?>
 				<dt class="detail-pill detail-pill--<?php echo esc_attr( $field['class'] ); ?>"><span class="works-label-full"><?php echo esc_html( $field['label'] ); ?></span><span class="works-label-short"><?php echo esc_html( ho_works_short_label( $field['label'] ) ); ?></span></dt>
changedstyle.css
--- previous/style.css
+++ 0.15.0/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.14.5-beta.1
+Version: 0.15.0-beta.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

既存構造を保ったままWorksを統合したため、共通化と旧例外が併存した。


対象ZIP
hiroki-ohnuma-beta1-0.15.0.zip


再利用すべき判断基準

  • 新構造へ移行したら、競合する旧CSS・旧JS・互換処理・固定高を削除する。
  • 同じ役割の処理は一つの関数・一つの状態・一つのDOMに集約する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.14.5

当時の改修目的

Profile/Calendarの上下余白、Calendar年月遷移と履歴、companion plugin対応を整える。

実装した変更

・Profile/Calendarの上下余白を共通20pxとして統一。

・表示領域を「ヘッダー下端+20px〜ページ専用フッター上端-20px」で算出。

・Calendar内の年月リンクをテーマの画面遷移で直接再読込。

・Calendarの同一ページ内履歴(前月・次月・年移動)をブラウザバックで復元。

・重複していたProfile文脈ナビ条件を削除。

・サービスアカウント方式の companion plugin に対応。

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.15.0以降でも撤去されず残った仕組みは継続採用とみなす。

共通20px余白、Calendar同一ページ履歴、年月リンク再読込、重複条件削除、サービスアカウント方式対応。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.15.0 の要望・修正内容と照合して確定。

後にOAuth方式やヘッダー/フッター全体の再設計が必要になった。

次版で実際に求められたこと:WorksをOpus/Scheduleと同じモジュール体系へ統合し、Released/On-Air表現とBack対応を加える。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.14.5/assets/css/theme.css
@@ -16,7 +16,8 @@
   --header-height: 212.40625px;
   --header-top: 0px;
   --header-edge: 212.40625px;
-  --page-top-clearance: 8px;
+  --content-edge-gap: 20px;
+  --page-top-clearance: var(--content-edge-gap);
   --content-top: 12px;
   --content-inline: 24px;
   --content-bottom: 80px;
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.14.5/assets/js/theme.js
@@ -66,11 +66,11 @@
     var mobileDockRect = visibleRect(mobileDock);
     var contextRect = visibleRect(context);
     var pageFooterRect = visibleRect(pageFooter);
-    var pageClearance = parseFloat(window.getComputedStyle(root).getPropertyValue('--page-top-clearance')) || 0;
-    var contentTop = Math.max(0, headerRect.bottom + pageClearance);
+    var edgeGap = parseFloat(window.getComputedStyle(root).getPropertyValue('--content-edge-gap')) || 0;
+    var contentTop = Math.max(0, headerRect.bottom + edgeGap);
     var primaryFooterRect = mobileDockRect || footerRect;
     var contentBoundary = pageFooterRect || primaryFooterRect;
-    var contentBottom = contentBoundary ? Math.max(contentTop, Math.min(window.innerHeight, contentBoundary.top)) : window.innerHeight;
+    var contentBottom = contentBoundary ? Math.max(contentTop, Math.min(window.innerHeight, contentBoundary.top - edgeGap)) : Math.max(contentTop, window.innerHeight - edgeGap);
     var bottomChrome = Math.max(0, window.innerHeight - contentBottom);
     var contentHeight = Math.max(0, contentBottom - contentTop);
 
@@ -884,7 +884,7 @@
     document.addEventListener('click', function (event) {
       var link = event.target.closest('a[href]');
       if (!link || event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || link.target) return;
-      if (link.closest('.jaikki-calendar-root')) return;
+      if (link.closest('.jaikki-calendar-root') && !link.hasAttribute('data-ho-calendar-link')) return;
       var targetUrl = new URL(link.href, window.location.href);
       var targetView = targetUrl.origin === window.location.origin ? viewForUrl(targetUrl.href) : '';
       if (!targetView) return;
@@ -894,6 +894,11 @@
           document.dispatchEvent(new CustomEvent('ho:resetfocus'));
           return;
         }
+        if (targetView === 'calendar' && targetUrl.href !== window.location.href) {
+          scrollPositions.set(visibleUrl, window.scrollY);
+          loadView(targetUrl.href, true, undefined, true);
+          return;
+        }
         if (targetUrl.href !== window.location.href) pushLocation(targetUrl.href, { hoShell: true, view: targetView });
         if (targetUrl.hash) activateLocationTarget();
         return;
@@ -906,7 +911,12 @@
       var targetView = viewForUrl(window.location.href);
       if (!targetView) return;
       if (targetView === currentView()) {
+        if (targetView === 'calendar' && window.location.href !== visibleUrl) {
+          loadView(window.location.href, false, scrollPositions.get(window.location.href), true);
+          return;
+        }
         activateLocationTarget();
+        visibleUrl = window.location.href;
         return;
       }
       scrollPositions.set(visibleUrl, window.scrollY);
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.14.5/docs/BETA1-CHANGELOG.md
@@ -63,3 +63,11 @@
 - Removed obsolete skull parade move/restore code and legacy text Back output.
 - Rendered skull parades directly in the viewport presentation layer.
 - Removed competing Profile and Calendar height overrides.
+
+## 0.14.5-beta.1
+- Profile/Calendarの上下余白を共通20pxとして統一。
+- 表示領域を「ヘッダー下端+20px〜ページ専用フッター上端-20px」で算出。
+- Calendar内の年月リンクをテーマの画面遷移で直接再読込。
+- Calendarの同一ページ内履歴(前月・次月・年移動)をブラウザバックで復元。
+- 重複していたProfile文脈ナビ条件を削除。
+- サービスアカウント方式の companion plugin に対応。
changedfunctions.php
--- previous/functions.php
+++ 0.14.5/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.14.4-beta.1' );
+define( 'HO_THEME_VERSION', '0.14.5-beta.1' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedstyle.css
--- previous/style.css
+++ 0.14.5/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.14.4-beta.1
+Version: 0.14.5-beta.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

当時のプラグイン方式と固定フッター前提が、その後の要件拡張に合わなくなった。


対象ZIP
hiroki-ohnuma-beta1-0.14.5.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.14.4

当時の改修目的

ページ専用ナビをヘッダーから分離し、表示領域とガイコツ/Backの構造を整理する。

実装した変更

・Moved the page-specific context navigation out of the header into a dedicated page-footer region.

・Unified viewport measurement between the header bottom and page-footer top.

・Removed obsolete skull parade move/restore code and legacy text Back output.

・Rendered skull parades directly in the viewport presentation layer.

・Removed competing Profile and Calendar height overrides.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.14.5以降でも撤去されず残った仕組みは継続採用とみなす。

専用フッター領域へ移動、表示領域測定を統一、旧ガイコツ移動復元処理と文字Backを削除。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.14.5 の要望・修正内容と照合して確定。

Profile/Calendarの余白やCalendar履歴はまだ不十分で0.14.5へ継続。

次版で実際に求められたこと:Profile/Calendarの上下余白、Calendar年月遷移と履歴、companion plugin対応を整える。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, footer.php, functions.php, header.php, inc/template-tags.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.14.4/assets/css/theme.css
@@ -32,13 +32,13 @@
   --mobile-dock-height: 64px;
   --footer-rendered-height: 54px;
   --mobile-dock-rendered-height: 0px;
-  --context-nav-rendered-height: 0px;
+  --page-footer-height: 0px;
   --viewport-bottom-chrome: 54px;
   --viewport-content-top: var(--header-edge);
   --viewport-content-bottom: calc(100dvh - var(--viewport-bottom-chrome));
   --viewport-content-height: calc(var(--viewport-content-bottom) - var(--viewport-content-top));
   --view-fade-duration: .86s;
-  --skull-parade-max-width: 920px;
+  --skull-parade-max-width: calc(var(--record-shell) - (var(--record-shell-inline) * 2) - (var(--module-padding) * 2));
   --surface: #edf3f8;
   --surface-soft: #f5f8fb;
   --neu-dark: rgba(143, 158, 184, .4);
@@ -142,6 +142,11 @@
 .site-nav__link--link,.mobile-nav__link--link { background-color:var(--orange); --glow:rgba(255,72,0,1); }
 .site-nav__link[aria-current="page"] { animation:nav-glow 2s ease-in-out infinite; filter:saturate(1.12); }
 @keyframes nav-glow { 0%,100% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 0 0 var(--glow); } 50% { box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42),0 0 16px 5px var(--glow); } }
+
+
+.page-footer { bottom:var(--footer-height); height:40px; left:0; position:fixed; right:0; z-index:9992; }
+.page-footer:empty { display:none; }
+.page-footer .context-nav { height:100%; }
 
 .context-nav { --context-accent:var(--green); align-items:center; background:linear-gradient(var(--surface),rgba(237,241,247,.82)); display:grid; gap:10px; grid-template-columns:100px minmax(0,1fr) 100px; height:40px; overflow:hidden; padding:0 12px; white-space:nowrap; }
 .context-nav--schedule { --context-accent:var(--yellow); }
@@ -190,7 +195,7 @@
 .site-main { min-height:calc(100vh - var(--footer-height)); padding-top:calc(var(--header-height) + var(--page-top-clearance)); }
 html[data-view-transitioning="true"] .site-main,html[data-view-transitioning="true"] .context-nav { will-change:opacity; }
 .ho-js[data-current-view="home"]:not(.is-view-ready) .site-main,
-.ho-js[data-current-view="home"]:not(.is-view-ready) [data-view-context] > * { opacity:0; }
+.ho-js[data-current-view="home"]:not(.is-view-ready) [data-page-footer] > * { opacity:0; }
 .content-shell { margin:0 auto; max-width:var(--page); padding:var(--content-top) var(--content-inline) var(--content-bottom); }
 .page-title { font-size:32px; margin:0 0 22px; }
 .page-title--profile { color:var(--blue); }
@@ -290,7 +295,6 @@
 
 .single-detail { display:grid; gap:var(--module-gap); }
 .single-detail__content { margin:0; }
-.back-link { font-weight:800; text-decoration:none; }
 .action-button { background-color:#20242c; white-space:nowrap; }
 
 .language-tabs__buttons { display:flex; gap:12px; margin:-5px -10px 12px; overflow:auto; padding:10px 10px 16px; }
@@ -304,7 +308,7 @@
 .profile-page__image figcaption { background:rgba(35,40,49,.58); border-radius:500px; bottom:10px; color:#fff; font-size:10px; padding:3px 9px; position:absolute; right:12px; }
 
 @media (min-width:768px) and (min-height:650px) {
-  .profile-page { height:var(--viewport-content-height); min-height:0; padding-bottom:var(--content-top); }
+  .profile-page { box-sizing:border-box; height:var(--viewport-content-height); min-height:0; padding-bottom:0; }
   .profile-page__layout { grid-template-rows:repeat(2,minmax(0,1fr)); height:100%; }
   .profile-page__image { height:auto; min-height:0; }
   .profile-page .language-tabs { min-height:0; }
@@ -313,7 +317,7 @@
 .focus-page.is-focus .event-card.is-picked.has-focus-details { overflow:visible; z-index:20; }
 .focus-page.is-focus .event-card.is-picked.has-focus-details .focus-item__extras-inner { overflow:visible; }
 .event-skull-parade { align-items:end; display:grid; gap:clamp(4px,1.5vw,18px); grid-template-columns:repeat(var(--skull-count),minmax(0,1fr)); isolation:isolate; margin:0 auto; max-width:var(--skull-parade-max-width); min-height:clamp(92px,14vw,170px); overflow:visible; padding:12px clamp(6px,2vw,20px) 6px; position:relative; width:100%; }
-.focus-parade-layer { bottom:calc(var(--viewport-bottom-chrome) + 8px); left:50%; max-width:calc(100vw - 28px); pointer-events:none; position:fixed; transform:translateX(-50%); width:var(--focus-parade-width,var(--skull-parade-max-width)); z-index:9988; }
+.focus-parade-layer { bottom:calc(100dvh - var(--viewport-content-bottom) + 8px); left:50%; max-width:calc(100vw - 28px); pointer-events:none; position:fixed; transform:translateX(-50%); width:min(calc(100vw - 28px),var(--skull-parade-max-width)); z-index:9988; }
 .focus-parade-layer:empty { display:none; }
 .event-skull-parade__slot { align-self:end; display:block; justify-self:center; max-height:150px; max-width:150px; opacity:0; transform-origin:50% 100%; width:100%; }
 .event-skull-parade__slot img { --skull-visual-scale:1; display:block; transform:scale(var(--skull-visual-scale)); transform-origin:50% 100%; width:100%; }
@@ -338,7 +342,7 @@
 @keyframes skull-ricochet-in { 0%{opacity:0;transform:translateX(var(--skull-from-x)) translateY(-32vh) rotate(var(--skull-spin))} 14%{opacity:1} 38%{transform:translateX(calc(var(--skull-from-x) * .48)) translateY(10px) rotate(120deg) scale(1.12,.88)} 58%{transform:translateX(calc(var(--skull-from-x) * .22)) translateY(-96px) rotate(-55deg)} 80%{transform:translateX(0) translateY(0) rotate(18deg)} 92%{transform:translateY(-16px) rotate(-7deg)} 100%{opacity:1;transform:none} }
 @keyframes skull-pendulum-in { 0%{opacity:0;transform:translateX(var(--skull-from-x)) translateY(-18px) rotate(65deg)} 15%{opacity:1} 44%{transform:translateX(calc(var(--skull-from-x) * .5)) translateY(-74px) rotate(-42deg)} 68%{transform:translateX(calc(var(--skull-from-x) * .18)) translateY(-20px) rotate(28deg)} 86%{transform:translateY(-9px) rotate(-12deg)} 100%{opacity:1;transform:none} }
 @keyframes skull-skip-in { 0%{opacity:0;transform:translateX(var(--skull-from-x)) translateY(0) rotate(18deg)} 10%{opacity:1} 25%{transform:translateX(calc(var(--skull-from-x) * .72)) translateY(-26px) rotate(-12deg)} 43%{transform:translateX(calc(var(--skull-from-x) * .52)) translateY(0) scale(1.1,.9)} 60%{transform:translateX(calc(var(--skull-from-x) * .32)) translateY(-52px) rotate(16deg)} 78%{transform:translateX(calc(var(--skull-from-x) * .12)) translateY(0) scale(1.08,.92)} 90%{transform:translateY(-17px)} 100%{opacity:1;transform:none} }
-.focus-item__extras,.focus-item__back { grid-column:1 / -1; margin:0; }
+.focus-item__extras { grid-column:1 / -1; margin:0; }
 .focus-item__extras { display:grid; grid-template-rows:0fr; opacity:0; transition:grid-template-rows .84s cubic-bezier(.18,.82,.24,1),opacity .44s ease; visibility:hidden; }
 .focus-item__extras-inner { display:grid; gap:var(--module-gap); min-height:0; overflow:hidden; }
 .event-focus { --event-focus-gap:var(--module-gap); display:grid; gap:var(--event-focus-gap); }
@@ -354,8 +358,6 @@
 .event-focus__note { background:rgba(245,248,251,.48); border:1px solid rgba(255,255,255,.58); border-radius:11px; box-shadow:inset 1px 1px 3px rgba(143,158,184,.2),inset -1px -1px 3px rgba(255,255,255,.72); font-size:16px; font-weight:700; line-height:1.45; padding:12px 14px; }
 .event-focus__note p { margin:0 0 1em; }
 .event-focus__note p:last-child { margin-bottom:0; }
-.focus-item__back { display:none; }
-.works-page.is-focus [data-focus-item].is-picked > .focus-item__back { display:block; margin-top:var(--module-gap); }
 .focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--header-edge); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
 .focus-return-control svg { fill:none; height:28px; overflow:visible; stroke:currentColor; stroke-linecap:round; stroke-linejoin:round; stroke-width:3.4; width:28px; }
 .focus-page.is-focus .focus-return-control { opacity:1; pointer-events:auto; transform:translateY(0); }
@@ -391,7 +393,7 @@
 .release-card img { aspect-ratio:1; border-radius:18px; box-shadow:var(--neu-inset); display:block; object-fit:cover; width:100%; }
 .release-card strong { display:block; padding:9px; }
 
-.calendar-page { max-width:1300px; min-height:var(--viewport-content-height); }
+.calendar-page { box-sizing:border-box; height:var(--viewport-content-height); max-width:1300px; min-height:0; }
 
 .site-footer { align-items:center; background:rgba(119,128,143,.86); border-top:1px solid rgba(255,255,255,.55); box-shadow:0 -6px 15px rgba(163,177,198,.38); bottom:0; color:#fff; display:flex; font-size:14px; font-weight:600; height:var(--footer-height); justify-content:center; left:0; position:fixed; right:0; z-index:9991; }
 .mobile-dock { display:none; }
@@ -517,7 +519,8 @@
   .site-footer { display:none; }
   .mobile-dock { bottom:0; display:block; left:0; position:fixed; right:0; z-index:9999; }
   :root.is-web-app .mobile-dock { background:rgba(119,128,143,.94); padding-bottom:env(safe-area-inset-bottom, 0px); }
-  .context-nav { background:rgba(237,241,247,.97); bottom:var(--mobile-dock-height); box-shadow:0 -5px 14px rgba(163,177,198,.5); display:grid; gap:0; grid-template-columns:20% minmax(0,60%) 20%; height:56px; left:0; overflow:hidden; padding:0; position:fixed; right:0; z-index:9998; }
+  .page-footer { bottom:var(--mobile-dock-height); height:56px; }
+  .context-nav { background:rgba(237,241,247,.97); bottom:auto; box-shadow:0 -5px 14px rgba(163,177,198,.5); display:grid; gap:0; grid-template-columns:20% minmax(0,60%) 20%; height:56px; left:auto; overflow:hidden; padding:0; position:relative; right:auto; z-index:auto; }
   .context-nav__section-link { align-items:center; align-self:stretch; background:linear-gradient(90deg,#fff,var(--surface)); border-radius:16px; display:flex; justify-content:center; padding:0 5px; position:relative; z-index:2; }
   .context-nav__section-link:last-child { background:linear-gradient(90deg,var(--surface),#fff); }
   .context-nav__years { height:56px; justify-content:flex-start; mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%); min-width:0; padding:10px 12px; -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 15px,#000 calc(100% - 15px),transparent 100%); }
@@ -543,7 +546,7 @@
   :root.is-web-app[data-current-view="home"] .home-dog { display:block !important; opacity:1; visibility:visible; }
   .admin-bar .home-stage { top:calc(var(--header-edge) + var(--page-top-clearance)); }
   .focus-parade-layer {
-    bottom:calc(var(--viewport-bottom-chrome) + 8px);
+    bottom:calc(100dvh - var(--viewport-content-bottom) + 8px);
     max-width:calc(100vw - 16px);
   }
   .event-skull-parade {
@@ -613,8 +616,8 @@
     padding:4px 8px;
   }
   .calendar-page--jaikki .jaikki-calendar-root { min-height:100%; width:100%; }
-  .profile-page { min-height:var(--viewport-content-height); padding-bottom:4px; }
-  .profile-page__layout { gap:8px; grid-template-rows:minmax(0,1fr) minmax(0,1fr); height:var(--viewport-content-height); }
+  .profile-page { height:var(--viewport-content-height); min-height:0; padding-bottom:0; }
+  .profile-page__layout { gap:8px; grid-template-rows:minmax(0,1fr) minmax(0,1fr); height:100%; }
   .profile-page__image { border-radius:20px; height:auto; min-height:0; }
   .profile-page__image img { object-position:center 32%; }
   .profile-page .language-tabs { min-height:0; }
@@ -628,7 +631,3 @@
   *,*::before,*::after { animation-duration:.01ms !important; animation-iteration-count:1 !important; scroll-behavior:auto !important; }
 }
 
-@media (max-height: 620px) {
-  .profile-page__layout { height:auto; min-height:var(--viewport-content-height); }
-  .profile-page__image,.profile-page .language-tabs__panel { min-height:260px; }
-}
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.14.4/assets/js/theme.js
@@ -59,26 +59,18 @@
     var header = document.getElementById('site-header');
     var footer = document.querySelector('.site-footer');
     var mobileDock = document.getElementById('smf-mobile-dock');
-    var context = document.querySelector('[data-view-context] .context-nav');
+    var pageFooter = document.querySelector('[data-page-footer]');
+    var context = pageFooter ? pageFooter.querySelector('.context-nav') : null;
     var headerRect = visibleRect(header) || { top: 0, bottom: 0, height: 0 };
     var footerRect = visibleRect(footer);
     var mobileDockRect = visibleRect(mobileDock);
     var contextRect = visibleRect(context);
+    var pageFooterRect = visibleRect(pageFooter);
     var pageClearance = parseFloat(window.getComputedStyle(root).getPropertyValue('--page-top-clearance')) || 0;
     var contentTop = Math.max(0, headerRect.bottom + pageClearance);
-    var bottomChromeRects = [
-      { element: footer, rect: footerRect },
-      { element: mobileDock, rect: mobileDockRect },
-      { element: context, rect: contextRect }
-    ].filter(function (entry) {
-      if (!entry.rect || !entry.element) return false;
-      var style = window.getComputedStyle(entry.element);
-      return style.position === 'fixed' && entry.rect.bottom > window.innerHeight / 2;
-    });
-    var bottomEdges = bottomChromeRects.map(function (entry) {
-      return Math.max(0, Math.min(window.innerHeight, entry.rect.top));
-    });
-    var contentBottom = bottomEdges.length ? Math.min.apply(Math, bottomEdges) : window.innerHeight;
+    var primaryFooterRect = mobileDockRect || footerRect;
+    var contentBoundary = pageFooterRect || primaryFooterRect;
+    var contentBottom = contentBoundary ? Math.max(contentTop, Math.min(window.innerHeight, contentBoundary.top)) : window.innerHeight;
     var bottomChrome = Math.max(0, window.innerHeight - contentBottom);
     var contentHeight = Math.max(0, contentBottom - contentTop);
 
@@ -87,7 +79,7 @@
     root.style.setProperty('--header-edge', Math.max(0, headerRect.bottom).toFixed(2) + 'px');
     root.style.setProperty('--footer-rendered-height', elementHeight(footer).toFixed(2) + 'px');
     root.style.setProperty('--mobile-dock-rendered-height', elementHeight(mobileDock).toFixed(2) + 'px');
-    root.style.setProperty('--context-nav-rendered-height', elementHeight(context).toFixed(2) + 'px');
+    root.style.setProperty('--page-footer-height', elementHeight(pageFooter).toFixed(2) + 'px');
     root.style.setProperty('--viewport-content-top', contentTop.toFixed(2) + 'px');
     root.style.setProperty('--viewport-content-bottom', contentBottom.toFixed(2) + 'px');
     root.style.setProperty('--viewport-bottom-chrome', bottomChrome.toFixed(2) + 'px');
@@ -112,8 +104,8 @@
       });
       var footer = document.querySelector('.site-footer');
       if (footer) observer.observe(footer);
-      var contextSlot = document.querySelector('[data-view-context]');
-      if (contextSlot) observer.observe(contextSlot);
+      var pageFooter = document.querySelector('[data-page-footer]');
+      if (pageFooter) observer.observe(pageFooter);
     }
     if (document.fonts && document.fonts.ready) document.fonts.ready.then(syncLayoutMetrics);
   }
@@ -411,7 +403,6 @@
     paradeLayer.className = 'focus-parade-layer';
     paradeLayer.setAttribute('aria-hidden', 'true');
     document.body.appendChild(paradeLayer);
-    var paradeOrigins = new Map();
     var activeItem = null;
     var activeSection = null;
     var originParent = null;
@@ -420,7 +411,7 @@
     var archiveTitle = document.title;
     var transitionId = 0;
     var reduceFocusMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
-    var onArtistResize = function () { if (activeItem) { layoutEventArtists(activeItem); syncParadeWidth(); } };
+    var onArtistResize = function () { if (activeItem) layoutEventArtists(activeItem); };
     window.addEventListener('resize', onArtistResize, { passive: true });
     registerViewCleanup(function () { window.removeEventListener('resize', onArtistResize); });
 
@@ -470,35 +461,18 @@
       });
     }
 
-    function paradeContentWidth(item, parade) {
-      var rect = parade.getBoundingClientRect();
-      if (rect.width > 0) return rect.width;
-      var style = window.getComputedStyle(item);
-      return Math.max(0, item.getBoundingClientRect().width - (parseFloat(style.paddingLeft) || 0) - (parseFloat(style.paddingRight) || 0));
-    }
-
-    function syncParadeWidth() {
-      if (!activeItem) return;
-      var parade = paradeLayer.querySelector('[data-event-skull-parade]');
+    function clearParadeLayer() {
+      resetEventSkullParades(paradeLayer);
+      paradeLayer.replaceChildren();
+    }
+
+    function renderParadeFromDetails(container) {
+      clearParadeLayer();
+      var parade = container.querySelector('[data-event-skull-parade]');
       if (!parade) return;
-      paradeLayer.style.setProperty('--focus-parade-width', Math.min(window.innerWidth - 28, paradeContentWidth(activeItem, parade)).toFixed(2) + 'px');
-    }
-
-    function moveParadesToViewport(item) {
-      item.querySelectorAll('[data-event-skull-parade]').forEach(function (parade) {
-        if (!paradeOrigins.has(parade)) paradeOrigins.set(parade, { parent: parade.parentNode, next: parade.nextSibling });
-        var width = paradeContentWidth(item, parade);
-        paradeLayer.style.setProperty('--focus-parade-width', Math.min(window.innerWidth - 28, width).toFixed(2) + 'px');
-        paradeLayer.appendChild(parade);
-      });
-    }
-
-    function restoreParades() {
-      paradeOrigins.forEach(function (origin, parade) {
-        if (origin.parent && document.documentElement.contains(origin.parent)) origin.parent.insertBefore(parade, origin.next);
-      });
-      paradeOrigins.clear();
-      paradeLayer.style.removeProperty('--focus-parade-width');
+      parade.remove();
+      paradeLayer.appendChild(parade);
+      setupEventSkullParades(paradeLayer);
     }
 
     function loadDetails(item, type, itemId) {
@@ -508,8 +482,22 @@
       var reveal = function (html) {
         if (!document.documentElement.contains(item)) return;
         if (slot.getAttribute('data-details-loaded') !== 'true') {
-          slot.innerHTML = '<div class="focus-item__extras-inner">' + (html || '') + '</div>';
+          var details = document.createElement('div');
+          details.className = 'focus-item__extras-inner';
+          details.innerHTML = html || '';
+          var paradeTemplate = details.querySelector('[data-event-skull-parade]');
+          if (paradeTemplate) slot.setAttribute('data-parade-html', paradeTemplate.outerHTML);
+          renderParadeFromDetails(details);
+          slot.replaceChildren(details);
           slot.setAttribute('data-details-loaded', 'true');
+        } else {
+          clearParadeLayer();
+          var paradeHtml = slot.getAttribute('data-parade-html');
+          if (paradeHtml) {
+            var holder = document.createElement('div');
+            holder.innerHTML = paradeHtml;
+            renderParadeFromDetails(holder);
+          }
         }
         slot.removeAttribute('aria-busy');
         item.classList.remove('is-expanding');
@@ -517,8 +505,7 @@
           if (activeItem !== item || page.classList.contains('is-focus-closing')) return;
           item.classList.add('has-focus-details');
           layoutEventArtists(item);
-          moveParadesToViewport(item);
-          setupEventSkullParades(paradeLayer);
+
         });
       };
       item.classList.add('is-expanding');
@@ -561,8 +548,7 @@
       var restoredSection = activeSection;
       var hadDetails = restoredItem.classList.contains('has-focus-details');
       page.classList.add('is-focus-closing');
-      resetEventSkullParades(paradeLayer);
-      restoreParades();
+      clearParadeLayer();
       restoredItem.classList.remove('is-expanding', 'has-focus-details');
       var restore = function () {
         if (thisTransition !== transitionId || activeItem !== restoredItem) return;
@@ -666,7 +652,7 @@
         activeItem.removeAttribute('aria-current');
         if (activeSection) activeSection.classList.remove('is-picked-section');
       }
-      restoreParades();
+      clearParadeLayer();
       if (returnControl) returnControl.remove();
       paradeLayer.remove();
       stage.remove();
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.14.4/docs/BETA1-CHANGELOG.md
@@ -55,3 +55,11 @@
 - 公演概要とアイキャッチ画像の間隔を、展開時だけ共通モジュール間隔で明示
 - 横長画像、縦長画像の左右カラム、リンク、説明文などの主要ブロック間隔を同じレイアウト変数から参照
 - CSS/JavaScriptのキャッシュ更新用テーマバージョンを更新
+
+
+## 0.14.4-beta.1
+- Moved the page-specific context navigation out of the header into a dedicated page-footer region.
+- Unified viewport measurement between the header bottom and page-footer top.
+- Removed obsolete skull parade move/restore code and legacy text Back output.
+- Rendered skull parades directly in the viewport presentation layer.
+- Removed competing Profile and Calendar height overrides.
changedfooter.php
--- previous/footer.php
+++ 0.14.4/footer.php
@@ -6,6 +6,7 @@
  */
 ?>
 </main>
+<div class="page-footer" data-view-context data-page-footer><?php echo ho_view_context_html( ho_current_view() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
 <img class="home-dog" src="<?php echo esc_url( get_template_directory_uri() . '/assets/img/8686_dog.webp' ); ?>" alt="" aria-hidden="true">
 <div class="home-runner-track" aria-hidden="true"><a href="<?php echo esc_url( ho_view_url( 'calendar' ) ); ?>" data-view-link="calendar" tabindex="-1"><img src="<?php echo esc_url( get_template_directory_uri() . '/assets/img/8686_skull-run.webp' ); ?>" alt=""></a></div>
 <footer class="site-footer">&copy;Skeleton Music Factory, Japan</footer>
changedfunctions.php
--- previous/functions.php
+++ 0.14.4/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.14.3-beta.1' );
+define( 'HO_THEME_VERSION', '0.14.4-beta.1' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedheader.php
--- previous/header.php
+++ 0.14.4/header.php
@@ -27,6 +27,5 @@
 	</div>
 	<?php ho_recent_posts_ticker(); ?>
 	<?php ho_render_primary_nav(); ?>
-	<div class="view-context-slot" data-view-context><?php echo ho_view_context_html( $current_view ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></div>
 </header>
 <main class="site-main" id="main-content">
changedinc/template-tags.php
--- previous/inc/template-tags.php
+++ 0.14.4/inc/template-tags.php
@@ -384,17 +384,6 @@
 		),
 		ho_focus_archive_url( $type )
 	);
-}
-
-function ho_focus_back_link( $type, $post_id ) {
-	$anchors = array(
-		'event'  => 'schedule-event-',
-		'work'   => 'opus-work-',
-		'credit' => 'works-credit-',
-	);
-	if ( ! isset( $anchors[ $type ] ) ) return;
-	$back_url = ho_focus_archive_url( $type ) . '#' . $anchors[ $type ] . absint( $post_id );
-	?><p class="focus-item__back"><a class="back-link" data-focus-close href="<?php echo esc_url( $back_url ); ?>">Back</a></p><?php
 }
 
 function ho_event_feature_layout( $post_id ) {
@@ -565,7 +554,6 @@
 		</dl>
 		<?php $notes = get_post_meta( $post_id, '_ho_credit_notes', true ); if ( trim( $notes ) ) : ?><div class="prose works-row__notes"><?php echo wp_kses_post( wpautop( $notes ) ); ?></div><?php endif; ?>
 		<div class="focus-item__extras" data-focus-details aria-live="polite"></div>
-		<?php ho_focus_back_link( 'credit', $post_id ); ?>
 	</article>
 	<?php
 }
changedstyle.css
--- previous/style.css
+++ 0.14.4/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.14.3-beta.1
+Version: 0.14.4-beta.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

構造変更後にページ固有の余白・履歴処理が残ったため。


対象ZIP
hiroki-ohnuma-beta1-0.14.4.zip


再利用すべき判断基準

  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 新構造へ移行したら、競合する旧CSS・旧JS・互換処理・固定高を削除する。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.14.3

当時の改修目的

0.14.2で発生したヘッダー/一覧復帰/表示レイヤー周辺の不具合を調整する。

実装した変更

変更: assets/css/theme.css, assets/js/theme.js, functions.php, inc/view-shell.php, style.css

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.14.4以降でも撤去されず残った仕組みは継続採用とみなす。

CSS・JS・view-shellを中心に挙動を修正。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.14.4 の要望・修正内容と照合して確定。

根本構造はまだヘッダー内ページナビ方式のままで、後続の0.14.4で再構築された。

次版で実際に求められたこと:ページ専用ナビをヘッダーから分離し、表示領域とガイコツ/Backの構造を整理する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, functions.php, inc/view-shell.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.14.3/assets/css/theme.css
@@ -34,7 +34,9 @@
   --mobile-dock-rendered-height: 0px;
   --context-nav-rendered-height: 0px;
   --viewport-bottom-chrome: 54px;
-  --viewport-content-height: calc(100dvh - var(--header-edge) - var(--viewport-bottom-chrome));
+  --viewport-content-top: var(--header-edge);
+  --viewport-content-bottom: calc(100dvh - var(--viewport-bottom-chrome));
+  --viewport-content-height: calc(var(--viewport-content-bottom) - var(--viewport-content-top));
   --view-fade-duration: .86s;
   --skull-parade-max-width: 920px;
   --surface: #edf3f8;
@@ -302,7 +304,7 @@
 .profile-page__image figcaption { background:rgba(35,40,49,.58); border-radius:500px; bottom:10px; color:#fff; font-size:10px; padding:3px 9px; position:absolute; right:12px; }
 
 @media (min-width:768px) and (min-height:650px) {
-  .profile-page { height:calc(100dvh - var(--header-edge) - var(--page-top-clearance) - var(--footer-height)); padding-bottom:var(--content-top); }
+  .profile-page { height:var(--viewport-content-height); min-height:0; padding-bottom:var(--content-top); }
   .profile-page__layout { grid-template-rows:repeat(2,minmax(0,1fr)); height:100%; }
   .profile-page__image { height:auto; min-height:0; }
   .profile-page .language-tabs { min-height:0; }
@@ -311,7 +313,7 @@
 .focus-page.is-focus .event-card.is-picked.has-focus-details { overflow:visible; z-index:20; }
 .focus-page.is-focus .event-card.is-picked.has-focus-details .focus-item__extras-inner { overflow:visible; }
 .event-skull-parade { align-items:end; display:grid; gap:clamp(4px,1.5vw,18px); grid-template-columns:repeat(var(--skull-count),minmax(0,1fr)); isolation:isolate; margin:0 auto; max-width:var(--skull-parade-max-width); min-height:clamp(92px,14vw,170px); overflow:visible; padding:12px clamp(6px,2vw,20px) 6px; position:relative; width:100%; }
-.focus-parade-layer { bottom:calc(var(--viewport-bottom-chrome) + 8px); left:50%; max-width:calc(100vw - 28px); pointer-events:none; position:fixed; transform:translateX(-50%); width:var(--skull-parade-max-width); z-index:9988; }
+.focus-parade-layer { bottom:calc(var(--viewport-bottom-chrome) + 8px); left:50%; max-width:calc(100vw - 28px); pointer-events:none; position:fixed; transform:translateX(-50%); width:var(--focus-parade-width,var(--skull-parade-max-width)); z-index:9988; }
 .focus-parade-layer:empty { display:none; }
 .event-skull-parade__slot { align-self:end; display:block; justify-self:center; max-height:150px; max-width:150px; opacity:0; transform-origin:50% 100%; width:100%; }
 .event-skull-parade__slot img { --skull-visual-scale:1; display:block; transform:scale(var(--skull-visual-scale)); transform-origin:50% 100%; width:100%; }
@@ -355,10 +357,7 @@
 .focus-item__back { display:none; }
 .works-page.is-focus [data-focus-item].is-picked > .focus-item__back { display:block; margin-top:var(--module-gap); }
 .focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--header-edge); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
-.focus-return-control span { height:17px; position:relative; width:20px; }
-.focus-return-control span::before,.focus-return-control span::after { content:""; position:absolute; }
-.focus-return-control span::before { border-bottom:3px solid currentColor; border-left:3px solid currentColor; border-radius:0 0 0 3px; bottom:1px; height:11px; left:4px; width:12px; }
-.focus-return-control span::after { border-bottom:3px solid currentColor; border-left:3px solid currentColor; bottom:1px; height:7px; left:2px; transform:rotate(45deg); transform-origin:0 100%; width:7px; }
+.focus-return-control svg { fill:none; height:28px; overflow:visible; stroke:currentColor; stroke-linecap:round; stroke-linejoin:round; stroke-width:3.4; width:28px; }
 .focus-page.is-focus .focus-return-control { opacity:1; pointer-events:auto; transform:translateY(0); }
 .focus-return-control:active { box-shadow:var(--neu-inset); transform:translateY(1px); }
 .focus-return-control:focus-visible { outline:3px solid #fff; outline-offset:3px; }
@@ -608,7 +607,9 @@
 @media (max-width: 767px) {
   .calendar-page--jaikki {
     box-sizing:border-box;
-    min-height:var(--viewport-content-height);
+    height:var(--viewport-content-height);
+    min-height:0;
+    overflow:auto;
     padding:4px 8px;
   }
   .calendar-page--jaikki .jaikki-calendar-root { min-height:100%; width:100%; }
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.14.3/assets/js/theme.js
@@ -49,25 +49,49 @@
     return Math.max(0, element.getBoundingClientRect().height);
   }
 
+  function visibleRect(element) {
+    if (!element || window.getComputedStyle(element).display === 'none') return null;
+    var rect = element.getBoundingClientRect();
+    return rect.height > 0 && rect.width > 0 ? rect : null;
+  }
+
   function syncLayoutMetrics() {
     var header = document.getElementById('site-header');
     var footer = document.querySelector('.site-footer');
     var mobileDock = document.getElementById('smf-mobile-dock');
     var context = document.querySelector('[data-view-context] .context-nav');
-    var headerRect = header ? header.getBoundingClientRect() : { top: 0, bottom: 0, height: 0 };
-    var footerHeight = elementHeight(footer);
-    var mobileDockHeight = elementHeight(mobileDock);
-    var contextHeight = elementHeight(context);
-    var bottomChrome = footerHeight + mobileDockHeight + (window.innerWidth < 768 ? contextHeight : 0);
+    var headerRect = visibleRect(header) || { top: 0, bottom: 0, height: 0 };
+    var footerRect = visibleRect(footer);
+    var mobileDockRect = visibleRect(mobileDock);
+    var contextRect = visibleRect(context);
+    var pageClearance = parseFloat(window.getComputedStyle(root).getPropertyValue('--page-top-clearance')) || 0;
+    var contentTop = Math.max(0, headerRect.bottom + pageClearance);
+    var bottomChromeRects = [
+      { element: footer, rect: footerRect },
+      { element: mobileDock, rect: mobileDockRect },
+      { element: context, rect: contextRect }
+    ].filter(function (entry) {
+      if (!entry.rect || !entry.element) return false;
+      var style = window.getComputedStyle(entry.element);
+      return style.position === 'fixed' && entry.rect.bottom > window.innerHeight / 2;
+    });
+    var bottomEdges = bottomChromeRects.map(function (entry) {
+      return Math.max(0, Math.min(window.innerHeight, entry.rect.top));
+    });
+    var contentBottom = bottomEdges.length ? Math.min.apply(Math, bottomEdges) : window.innerHeight;
+    var bottomChrome = Math.max(0, window.innerHeight - contentBottom);
+    var contentHeight = Math.max(0, contentBottom - contentTop);
 
     root.style.setProperty('--header-height', Math.max(0, headerRect.height).toFixed(2) + 'px');
     root.style.setProperty('--header-top', Math.max(0, headerRect.top).toFixed(2) + 'px');
     root.style.setProperty('--header-edge', Math.max(0, headerRect.bottom).toFixed(2) + 'px');
-    root.style.setProperty('--footer-rendered-height', footerHeight.toFixed(2) + 'px');
-    root.style.setProperty('--mobile-dock-rendered-height', mobileDockHeight.toFixed(2) + 'px');
-    root.style.setProperty('--context-nav-rendered-height', contextHeight.toFixed(2) + 'px');
+    root.style.setProperty('--footer-rendered-height', elementHeight(footer).toFixed(2) + 'px');
+    root.style.setProperty('--mobile-dock-rendered-height', elementHeight(mobileDock).toFixed(2) + 'px');
+    root.style.setProperty('--context-nav-rendered-height', elementHeight(context).toFixed(2) + 'px');
+    root.style.setProperty('--viewport-content-top', contentTop.toFixed(2) + 'px');
+    root.style.setProperty('--viewport-content-bottom', contentBottom.toFixed(2) + 'px');
     root.style.setProperty('--viewport-bottom-chrome', bottomChrome.toFixed(2) + 'px');
-    root.style.setProperty('--viewport-content-height', Math.max(0, window.innerHeight - Math.max(0, headerRect.bottom) - bottomChrome).toFixed(2) + 'px');
+    root.style.setProperty('--viewport-content-height', contentHeight.toFixed(2) + 'px');
   }
 
   function setupLayoutMetrics() {
@@ -200,28 +224,27 @@
   }
 
   function navigateCalendarTo(year, month) {
+    var targetYear = Number(year);
+    var targetMonth = Number(month) + 1;
+    if (!targetYear || targetMonth < 1 || targetMonth > 12) return false;
     var calendarRoot = document.querySelector('.jaikki-calendar-root');
     if (!calendarRoot) return false;
-    var detail = { year: Number(year), month: Number(month) + 1 };
+
+    var detail = { year: targetYear, month: targetMonth };
     var event = new CustomEvent('ho:calendar:navigate', { detail: detail, cancelable: true });
     calendarRoot.dispatchEvent(event);
     if (event.defaultPrevented) return true;
 
-    var current = calendarDateFromRoot(calendarRoot);
-    var delta = ((detail.year - current.year) * 12) + (detail.month - current.month);
-    var direction = delta < 0 ? 'previous' : 'next';
-    if (delta === 0) return true;
-    var remaining = Math.min(240, Math.abs(delta));
-    var step = function () {
-      if (!remaining) return;
-      var activeRoot = document.querySelector('.jaikki-calendar-root');
-      var button = activeRoot ? calendarNavButtons(activeRoot)[direction] : null;
-      if (!button) return;
-      button.click();
-      remaining -= 1;
-      if (remaining) window.setTimeout(step, 55);
-    };
-    step();
+    var directTarget = calendarRoot.querySelector('[data-year="' + targetYear + '"][data-month="' + targetMonth + '"], [data-date="' + targetYear + '-' + String(targetMonth).padStart(2, '0') + '"]');
+    if (directTarget) {
+      directTarget.click();
+      return true;
+    }
+
+    var url = new URL(HOShell.views.calendar, window.location.href);
+    url.searchParams.set('calendar_year', String(targetYear));
+    url.searchParams.set('calendar_month', String(targetMonth));
+    document.dispatchEvent(new CustomEvent('ho:calendar:direct', { detail: { url: url.href, year: targetYear, month: targetMonth } }));
     return true;
   }
 
@@ -247,7 +270,17 @@
     centerContextNavigation(nav);
     var links = Array.from(nav.querySelectorAll('[data-context-key]'));
     if (!sections.length) {
-      if (root.getAttribute('data-current-view') === 'calendar') setupCalendarYearNavigation(nav, links);
+      if (root.getAttribute('data-current-view') === 'calendar') {
+        var requestedYear = new URL(window.location.href).searchParams.get('calendar_year');
+        if (requestedYear) {
+          links.forEach(function (link) {
+            if (link.getAttribute('data-context-key') === requestedYear) link.setAttribute('aria-current', 'page');
+            else link.removeAttribute('aria-current');
+          });
+          centerContextNavigation(nav);
+        }
+        setupCalendarYearNavigation(nav, links);
+      }
       return;
     }
 
@@ -371,7 +404,7 @@
       returnControl.type = 'button';
       returnControl.setAttribute('aria-label', '一覧へ戻る');
       returnControl.setAttribute('data-focus-close', '');
-      returnControl.innerHTML = '<span aria-hidden="true"></span>';
+      returnControl.innerHTML = '<svg viewBox="0 0 32 32" aria-hidden="true" focusable="false"><path d="M25 5v8c0 5.8-3.7 9-9.5 9H7"/><path d="M13 15.5 6.5 22 13 28.5"/></svg>';
       page.appendChild(returnControl);
     }
     var paradeLayer = document.createElement('div');
@@ -387,7 +420,7 @@
     var archiveTitle = document.title;
     var transitionId = 0;
     var reduceFocusMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
-    var onArtistResize = function () { if (activeItem) layoutEventArtists(activeItem); };
+    var onArtistResize = function () { if (activeItem) { layoutEventArtists(activeItem); syncParadeWidth(); } };
     window.addEventListener('resize', onArtistResize, { passive: true });
     registerViewCleanup(function () { window.removeEventListener('resize', onArtistResize); });
 
@@ -437,9 +470,25 @@
       });
     }
 
+    function paradeContentWidth(item, parade) {
+      var rect = parade.getBoundingClientRect();
+      if (rect.width > 0) return rect.width;
+      var style = window.getComputedStyle(item);
+      return Math.max(0, item.getBoundingClientRect().width - (parseFloat(style.paddingLeft) || 0) - (parseFloat(style.paddingRight) || 0));
+    }
+
+    function syncParadeWidth() {
+      if (!activeItem) return;
+      var parade = paradeLayer.querySelector('[data-event-skull-parade]');
+      if (!parade) return;
+      paradeLayer.style.setProperty('--focus-parade-width', Math.min(window.innerWidth - 28, paradeContentWidth(activeItem, parade)).toFixed(2) + 'px');
+    }
+
     function moveParadesToViewport(item) {
       item.querySelectorAll('[data-event-skull-parade]').forEach(function (parade) {
         if (!paradeOrigins.has(parade)) paradeOrigins.set(parade, { parent: parade.parentNode, next: parade.nextSibling });
+        var width = paradeContentWidth(item, parade);
+        paradeLayer.style.setProperty('--focus-parade-width', Math.min(window.innerWidth - 28, width).toFixed(2) + 'px');
         paradeLayer.appendChild(parade);
       });
     }
@@ -449,6 +498,7 @@
         if (origin.parent && document.documentElement.contains(origin.parent)) origin.parent.insertBefore(parade, origin.next);
       });
       paradeOrigins.clear();
+      paradeLayer.style.removeProperty('--focus-parade-width');
     }
 
     function loadDetails(item, type, itemId) {
@@ -758,14 +808,18 @@
       return loadScripts(assets && assets.scripts).then(function () { runInlineScripts(assets && assets.after); });
     }
 
-    function requestView(view) {
-      if (payloadCache.has(view)) return Promise.resolve(payloadCache.get(view));
-      return window.fetch(HOShell.viewEndpoint + encodeURIComponent(view), { credentials: 'same-origin' }).then(function (response) {
+    function requestView(view, url) {
+      var targetUrl = new URL(url || HOShell.views[view], window.location.href);
+      var cacheKey = view + targetUrl.search;
+      if (payloadCache.has(cacheKey)) return Promise.resolve(payloadCache.get(cacheKey));
+      var endpoint = new URL(HOShell.viewEndpoint + encodeURIComponent(view), window.location.href);
+      targetUrl.searchParams.forEach(function (value, key) { endpoint.searchParams.set(key, value); });
+      return window.fetch(endpoint.href, { credentials: 'same-origin' }).then(function (response) {
         if (!response.ok) throw new Error('View request failed');
         return response.json();
       }).then(function (payload) {
         if (!payload || payload.view !== view || typeof payload.html !== 'string') throw new Error('View data was invalid');
-        payloadCache.set(view, payload);
+        payloadCache.set(cacheKey, payload);
         return payload;
       });
     }
@@ -784,7 +838,7 @@
       });
     }
 
-    function loadView(url, addHistory, restoredScroll) {
+    function loadView(url, addHistory, restoredScroll, forceRequest) {
       if (navigating) return Promise.resolve();
       var targetView = viewForUrl(url);
       if (!targetView) return Promise.reject(new Error('Unknown view'));
@@ -794,7 +848,7 @@
       var contextSlot = document.querySelector('[data-view-context]');
       var fromView = currentView();
       root.setAttribute('data-view-transitioning', 'true');
-      var request = viewStates.has(targetView) ? Promise.resolve({ state: viewStates.get(targetView) }) : requestView(targetView).then(function (payload) { return { payload: payload }; });
+      var request = !forceRequest && viewStates.has(targetView) ? Promise.resolve({ state: viewStates.get(targetView) }) : requestView(targetView, url).then(function (payload) { return { payload: payload }; });
       var oldContext = contextSlot ? contextSlot.firstElementChild : null;
       var leaving = animateViewSurface(main, oldContext, false);
 
@@ -836,6 +890,11 @@
 
     replaceLocation(window.location.href, { hoShell: true, view: currentView() });
     document.addEventListener('ho:locationchange', function (event) { visibleUrl = event.detail.url; });
+    document.addEventListener('ho:calendar:direct', function (event) {
+      if (!event.detail || !event.detail.url) return;
+      scrollPositions.set(visibleUrl, window.scrollY);
+      loadView(event.detail.url, true, undefined, true);
+    });
     document.addEventListener('click', function (event) {
       var link = event.target.closest('a[href]');
       if (!link || event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || link.target) return;
changedfunctions.php
--- previous/functions.php
+++ 0.14.3/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.14.2-beta.1' );
+define( 'HO_THEME_VERSION', '0.14.3-beta.1' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedinc/view-shell.php
--- previous/inc/view-shell.php
+++ 0.14.3/inc/view-shell.php
@@ -211,10 +211,16 @@
 	return trim( ob_get_clean() );
 }
 
-function ho_calendar_view_html() {
-	ob_start();
-	?>
-	<div class="content-shell calendar-page calendar-page--jaikki" data-view-root="calendar"><?php if ( shortcode_exists( 'jaikki_calendar' ) ) : echo do_shortcode( '[jaikki_calendar]' ); else : ?><p class="module module--prose calendar-fallback">カレンダーを読み込めませんでした。<a href="<?php echo esc_url( ho_view_url( 'schedule' ) ); ?>">スケジュール一覧</a>をご覧ください。</p><?php endif; ?></div>
+function ho_calendar_view_html( $year = 0, $month = 0 ) {
+	$year = absint( $year ?: ( isset( $_GET['calendar_year'] ) ? wp_unslash( $_GET['calendar_year'] ) : 0 ) );
+	$month = absint( $month ?: ( isset( $_GET['calendar_month'] ) ? wp_unslash( $_GET['calendar_month'] ) : 0 ) );
+	$shortcode = '[jaikki_calendar]';
+	if ( $year >= 1900 && $year <= 2200 && $month >= 1 && $month <= 12 ) {
+		$shortcode = sprintf( '[jaikki_calendar year="%d" month="%d"]', $year, $month );
+	}
+	ob_start();
+	?>
+	<div class="content-shell calendar-page calendar-page--jaikki" data-view-root="calendar"><?php if ( shortcode_exists( 'jaikki_calendar' ) ) : echo do_shortcode( $shortcode ); else : ?><p class="module module--prose calendar-fallback">カレンダーを読み込めませんでした。<a href="<?php echo esc_url( ho_view_url( 'schedule' ) ); ?>">スケジュール一覧</a>をご覧ください。</p><?php endif; ?></div>
 	<?php
 	return trim( ob_get_clean() );
 }
@@ -309,7 +315,9 @@
 		'permission_callback' => '__return_true',
 		'callback' => function ( WP_REST_Request $request ) {
 			$view = sanitize_key( $request['view'] );
-			$html = ho_view_content_html( $view );
+			$year = absint( $request->get_param( 'calendar_year' ) );
+			$month = absint( $request->get_param( 'calendar_month' ) );
+			$html = 'calendar' === $view ? ho_calendar_view_html( $year, $month ) : ho_view_content_html( $view );
 			$assets = ho_view_assets();
 			if ( 'tetris' === $view ) {
 				$assets['styles'][] = get_template_directory_uri() . '/assets/css/tetris.css?ver=' . rawurlencode( HO_THEME_VERSION );
changedstyle.css
--- previous/style.css
+++ 0.14.3/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.14.2-beta.1
+Version: 0.14.3-beta.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

局所修正では表示領域計算とナビ配置の競合を解消できなかった。


対象ZIP
hiroki-ohnuma-beta1-0.14.3.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.14.2

当時の改修目的

共通ヘッダー変数、Opus/ScheduleのBack、同一主メニュー再押下、Calendar年移動、ガイコツ表示を整理する。

実装した変更

・Added the 0.86-second initial home fade-in using the shared transition duration.

・Added measured viewport chrome variables shared by Profile, Calendar, the floating return control, and skull parade placement.

・Added the shared Opus/Schedule vertical return capsule and unified list reset behavior.

・Restored Opus/Schedule list views when their active primary navigation item is selected again.

・Added Calendar year-button behavior that targets January only while Calendar is active; Schedule keeps its original year navigation.

・Excluded Calendar controls from the partial-page navigation interceptor.

・Moved focused skull parades to a fixed footer-adjacent layer while preserving their existing width and layer order.

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.14.3以降でも撤去されず残った仕組みは継続採用とみなす。

Backカプセル、一覧復帰、Calendar専用年移動、部分遷移除外、ガイコツの固定レイヤー化を実装。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.14.3 の要望・修正内容と照合して確定。

ページ専用ナビをヘッダー内に置く構造は後続版で撤回された。

次版で実際に求められたこと:0.14.2で発生したヘッダー/一覧復帰/表示レイヤー周辺の不具合を調整する。

実際の変更箇所・実装内容

変更ファイル

変更: assets/css/theme.css, assets/js/theme.js, docs/BETA1-CHANGELOG.md, functions.php, header.php, inc/template-tags.php, inc/view-shell.php, style.css

changedassets/css/theme.css
--- previous/assets/css/theme.css
+++ 0.14.2/assets/css/theme.css
@@ -30,6 +30,13 @@
   --mobile-nav-height: 48px;
   --mobile-credit-height: 16px;
   --mobile-dock-height: 64px;
+  --footer-rendered-height: 54px;
+  --mobile-dock-rendered-height: 0px;
+  --context-nav-rendered-height: 0px;
+  --viewport-bottom-chrome: 54px;
+  --viewport-content-height: calc(100dvh - var(--header-edge) - var(--viewport-bottom-chrome));
+  --view-fade-duration: .86s;
+  --skull-parade-max-width: 920px;
   --surface: #edf3f8;
   --surface-soft: #f5f8fb;
   --neu-dark: rgba(143, 158, 184, .4);
@@ -180,6 +187,8 @@
 
 .site-main { min-height:calc(100vh - var(--footer-height)); padding-top:calc(var(--header-height) + var(--page-top-clearance)); }
 html[data-view-transitioning="true"] .site-main,html[data-view-transitioning="true"] .context-nav { will-change:opacity; }
+.ho-js[data-current-view="home"]:not(.is-view-ready) .site-main,
+.ho-js[data-current-view="home"]:not(.is-view-ready) [data-view-context] > * { opacity:0; }
 .content-shell { margin:0 auto; max-width:var(--page); padding:var(--content-top) var(--content-inline) var(--content-bottom); }
 .page-title { font-size:32px; margin:0 0 22px; }
 .page-title--profile { color:var(--blue); }
@@ -301,7 +310,9 @@
 }
 .focus-page.is-focus .event-card.is-picked.has-focus-details { overflow:visible; z-index:20; }
 .focus-page.is-focus .event-card.is-picked.has-focus-details .focus-item__extras-inner { overflow:visible; }
-.event-skull-parade { align-items:end; display:grid; gap:clamp(4px,1.5vw,18px); grid-template-columns:repeat(var(--skull-count),minmax(0,1fr)); isolation:isolate; margin:8px auto 20px; max-width:min(920px,100%); min-height:clamp(92px,14vw,170px); overflow:visible; padding:12px clamp(6px,2vw,20px) 6px; position:relative; z-index:2; }
+.event-skull-parade { align-items:end; display:grid; gap:clamp(4px,1.5vw,18px); grid-template-columns:repeat(var(--skull-count),minmax(0,1fr)); isolation:isolate; margin:0 auto; max-width:var(--skull-parade-max-width); min-height:clamp(92px,14vw,170px); overflow:visible; padding:12px clamp(6px,2vw,20px) 6px; position:relative; width:100%; }
+.focus-parade-layer { bottom:calc(var(--viewport-bottom-chrome) + 8px); left:50%; max-width:calc(100vw - 28px); pointer-events:none; position:fixed; transform:translateX(-50%); width:var(--skull-parade-max-width); z-index:9988; }
+.focus-parade-layer:empty { display:none; }
 .event-skull-parade__slot { align-self:end; display:block; justify-self:center; max-height:150px; max-width:150px; opacity:0; transform-origin:50% 100%; width:100%; }
 .event-skull-parade__slot img { --skull-visual-scale:1; display:block; transform:scale(var(--skull-visual-scale)); transform-origin:50% 100%; width:100%; }
 .event-skull-parade.is-rolling .event-skull-parade__slot { animation-duration:var(--skull-duration); animation-fill-mode:both; animation-timing-function:cubic-bezier(.18,.82,.24,1); animation-delay:var(--skull-delay); }
@@ -342,18 +353,25 @@
 .event-focus__note p { margin:0 0 1em; }
 .event-focus__note p:last-child { margin-bottom:0; }
 .focus-item__back { display:none; }
+.works-page.is-focus [data-focus-item].is-picked > .focus-item__back { display:block; margin-top:var(--module-gap); }
+.focus-return-control { align-items:flex-end; background:var(--red); border:1px solid rgba(255,255,255,.5); border-radius:0 0 500px 500px; box-shadow:var(--neu-small),inset 0 1px 2px rgba(255,255,255,.42); color:#fff; cursor:pointer; display:flex; height:92px; justify-content:center; opacity:0; padding:0 0 18px; pointer-events:none; position:fixed; right:max(18px,calc((100vw - var(--record-shell)) / 2 + var(--record-shell-inline))); top:var(--header-edge); transform:translateY(-58px); transition:opacity .24s ease,transform .48s cubic-bezier(.16,1,.3,1); width:48px; z-index:9989; }
+.focus-return-control span { height:17px; position:relative; width:20px; }
+.focus-return-control span::before,.focus-return-control span::after { content:""; position:absolute; }
+.focus-return-control span::before { border-bottom:3px solid currentColor; border-left:3px solid currentColor; border-radius:0 0 0 3px; bottom:1px; height:11px; left:4px; width:12px; }
+.focus-return-control span::after { border-bottom:3px solid currentColor; border-left:3px solid currentColor; bottom:1px; height:7px; left:2px; transform:rotate(45deg); transform-origin:0 100%; width:7px; }
+.focus-page.is-focus .focus-return-control { opacity:1; pointer-events:auto; transform:translateY(0); }
+.focus-return-control:active { box-shadow:var(--neu-inset); transform:translateY(1px); }
+.focus-return-control:focus-visible { outline:3px solid #fff; outline-offset:3px; }
 .focus-item__note { border-top:1px solid var(--neu-border); padding:var(--module-padding) 0 0; }
 .focus-item__action { margin:0; text-align:center; }
 .focus-page.is-focus [data-focus-item].is-picked.has-focus-details > .focus-item__extras { grid-template-rows:1fr; opacity:1; visibility:visible; }
-.focus-page.is-focus .event-card.is-picked.has-focus-details > .focus-item__extras,
-.focus-page.is-focus .event-card.is-picked > .focus-item__back { margin-top:var(--module-gap); }
-.focus-page.is-focus [data-focus-item].is-picked > .focus-item__back { display:block; }
+.focus-page.is-focus .event-card.is-picked.has-focus-details > .focus-item__extras { margin-top:var(--module-gap); }
 .focus-stage[hidden] { display:none; }
 .focus-page.is-focus [data-focus-list] { display:none; }
 .focus-stage > [data-focus-item] { margin:0; }
 .focus-page [data-focus-item].is-focus-moving { pointer-events:none; will-change:transform; z-index:30; }
 .focus-page.is-focus-closing [data-focus-item].is-picked { pointer-events:none; }
-.focus-page .module-section,.focus-page .year-section__title,.focus-page [data-focus-item] { transition:opacity .36s ease; }
+.focus-page .module-section,.focus-page .year-section__title,.focus-page [data-focus-item] { transition:opacity var(--view-fade-duration) ease-in-out; }
 .focus-page:is(.is-focus-leaving,.is-focus-returning) .module-section:not(.is-picked-section),
 .focus-page:is(.is-focus-leaving,.is-focus-returning) .is-picked-section > .year-section__title,
 .focus-page:is(.is-focus-leaving,.is-focus-returning) .is-picked-section > [data-focus-item]:not(.is-picked) { opacity:0; pointer-events:none; }
@@ -374,7 +392,7 @@
 .release-card img { aspect-ratio:1; border-radius:18px; box-shadow:var(--neu-inset); display:block; object-fit:cover; width:100%; }
 .release-card strong { display:block; padding:9px; }
 
-.calendar-page { max-width:1300px; }
+.calendar-page { max-width:1300px; min-height:var(--viewport-content-height); }
 
 .site-footer { align-items:center; background:rgba(119,128,143,.86); border-top:1px solid rgba(255,255,255,.55); box-shadow:0 -6px 15px rgba(163,177,198,.38); bottom:0; color:#fff; display:flex; font-size:14px; font-weight:600; height:var(--footer-height); justify-content:center; left:0; position:fixed; right:0; z-index:9991; }
 .mobile-dock { display:none; }
@@ -525,22 +543,22 @@
   .home-dog,.home-runner-track { bottom:var(--mobile-dock-height); }
   :root.is-web-app[data-current-view="home"] .home-dog { display:block !important; opacity:1; visibility:visible; }
   .admin-bar .home-stage { top:calc(var(--header-edge) + var(--page-top-clearance)); }
+  .focus-parade-layer {
+    bottom:calc(var(--viewport-bottom-chrome) + 8px);
+    max-width:calc(100vw - 16px);
+  }
   .event-skull-parade {
-    bottom:calc(var(--mobile-dock-height) + 56px + 8px);
     gap:0;
     height:116px;
-    left:8px;
-    margin:0;
-    max-width:none;
     min-height:0;
     padding:0 6px;
-    pointer-events:none;
-    position:fixed;
-    right:8px;
-    z-index:9997;
   }
   .event-skull-parade__slot { max-height:86px; max-width:86px; }
   .event-skull-parade__slot img { --skull-visual-scale:1.34; }
+  .focus-return-control {
+    right:12px;
+    top:var(--header-edge);
+  }
 }
 
 @media (max-width: 480px) {
@@ -590,11 +608,12 @@
 @media (max-width: 767px) {
   .calendar-page--jaikki {
     box-sizing:border-box;
+    min-height:var(--viewport-content-height);
     padding:4px 8px;
   }
-  .calendar-page--jaikki .jaikki-calendar-root { width:100%; }
-  .profile-page { padding-bottom:4px; }
-  .profile-page__layout { gap:8px; grid-template-rows:minmax(0,1fr) minmax(0,1fr); height:calc(100dvh - var(--header-edge) - var(--mobile-dock-height) - 56px - 8px); }
+  .calendar-page--jaikki .jaikki-calendar-root { min-height:100%; width:100%; }
+  .profile-page { min-height:var(--viewport-content-height); padding-bottom:4px; }
+  .profile-page__layout { gap:8px; grid-template-rows:minmax(0,1fr) minmax(0,1fr); height:var(--viewport-content-height); }
   .profile-page__image { border-radius:20px; height:auto; min-height:0; }
   .profile-page__image img { object-position:center 32%; }
   .profile-page .language-tabs { min-height:0; }
@@ -607,3 +626,8 @@
 @media (prefers-reduced-motion: reduce) {
   *,*::before,*::after { animation-duration:.01ms !important; animation-iteration-count:1 !important; scroll-behavior:auto !important; }
 }
+
+@media (max-height: 620px) {
+  .profile-page__layout { height:auto; min-height:var(--viewport-content-height); }
+  .profile-page__image,.profile-page .language-tabs__panel { min-height:260px; }
+}
changedassets/js/theme.js
--- previous/assets/js/theme.js
+++ 0.14.2/assets/js/theme.js
@@ -8,6 +8,7 @@
   var lastLinkThanksAnimation = -1;
   var motionTimeScale = 2;
   var webAppQuery = window.matchMedia('(display-mode: standalone), (display-mode: fullscreen), (display-mode: minimal-ui)');
+  var viewFadeDuration = 860;
 
   function motionTime(milliseconds) {
     return milliseconds * motionTimeScale;
@@ -43,31 +44,58 @@
     root.classList.toggle('is-web-app', webAppQuery.matches || window.navigator.standalone === true);
   }
 
-  function syncHeaderMetrics() {
+  function elementHeight(element) {
+    if (!element || window.getComputedStyle(element).display === 'none') return 0;
+    return Math.max(0, element.getBoundingClientRect().height);
+  }
+
+  function syncLayoutMetrics() {
     var header = document.getElementById('site-header');
-    if (!header) return;
-    var rect = header.getBoundingClientRect();
-    root.style.setProperty('--header-height', Math.max(0, rect.height).toFixed(2) + 'px');
-    root.style.setProperty('--header-top', Math.max(0, rect.top).toFixed(2) + 'px');
-    root.style.setProperty('--header-edge', Math.max(0, rect.bottom).toFixed(2) + 'px');
+    var footer = document.querySelector('.site-footer');
+    var mobileDock = document.getElementById('smf-mobile-dock');
+    var context = document.querySelector('[data-view-context] .context-nav');
+    var headerRect = header ? header.getBoundingClientRect() : { top: 0, bottom: 0, height: 0 };
+    var footerHeight = elementHeight(footer);
+    var mobileDockHeight = elementHeight(mobileDock);
+    var contextHeight = elementHeight(context);
+    var bottomChrome = footerHeight + mobileDockHeight + (window.innerWidth < 768 ? contextHeight : 0);
+
+    root.style.setProperty('--header-height', Math.max(0, headerRect.height).toFixed(2) + 'px');
+    root.style.setProperty('--header-top', Math.max(0, headerRect.top).toFixed(2) + 'px');
+    root.style.setProperty('--header-edge', Math.max(0, headerRect.bottom).toFixed(2) + 'px');
+    root.style.setProperty('--footer-rendered-height', footerHeight.toFixed(2) + 'px');
+    root.style.setProperty('--mobile-dock-rendered-height', mobileDockHeight.toFixed(2) + 'px');
+    root.style.setProperty('--context-nav-rendered-height', contextHeight.toFixed(2) + 'px');
+    root.style.setProperty('--viewport-bottom-chrome', bottomChrome.toFixed(2) + 'px');
+    root.style.setProperty('--viewport-content-height', Math.max(0, window.innerHeight - Math.max(0, headerRect.bottom) - bottomChrome).toFixed(2) + 'px');
   }
 
   function setupLayoutMetrics() {
     syncWebAppMode();
-    syncHeaderMetrics();
-    if (webAppQuery.addEventListener) webAppQuery.addEventListener('change', syncWebAppMode);
-    window.addEventListener('resize', syncHeaderMetrics, { passive: true });
-    window.addEventListener('orientationchange', syncHeaderMetrics);
-    window.addEventListener('pageshow', syncHeaderMetrics);
+    syncLayoutMetrics();
+    if (webAppQuery.addEventListener) webAppQuery.addEventListener('change', function () {
+      syncWebAppMode();
+      syncLayoutMetrics();
+    });
+    window.addEventListener('resize', syncLayoutMetrics, { passive: true });
+    window.addEventListener('orientationchange', syncLayoutMetrics);
+    window.addEventListener('pageshow', syncLayoutMetrics);
     if ('ResizeObserver' in window) {
-      var header = document.getElementById('site-header');
-      if (header) new ResizeObserver(syncHeaderMetrics).observe(header);
-    }
-    if (document.fonts && document.fonts.ready) document.fonts.ready.then(syncHeaderMetrics);
+      var observer = new ResizeObserver(syncLayoutMetrics);
+      ['site-header', 'smf-mobile-dock'].forEach(function (id) {
+        var element = document.getElementById(id);
+        if (element) observer.observe(element);
+      });
+      var footer = document.querySelector('.site-footer');
+      if (footer) observer.observe(footer);
+      var contextSlot = document.querySelector('[data-view-context]');
+      if (contextSlot) observer.observe(contextSlot);
+    }
+    if (document.fonts && document.fonts.ready) document.fonts.ready.then(syncLayoutMetrics);
   }
 
   function scrollTargetBelowHeader(target) {
-    syncHeaderMetrics();
+    syncLayoutMetrics();
     var header = document.getElementById('site-header');
     if (!header) return;
     var destination = window.scrollY + target.getBoundingClientRect().top - header.getBoundingClientRect().bottom - 8;
@@ -140,13 +168,88 @@
     strip.scrollLeft = Math.max(0, current.offsetLeft - strip.offsetLeft - ((strip.clientWidth - current.offsetWidth) / 2));
   }
 
+  function calendarDateFromRoot(calendarRoot) {
+    var candidates = [
+      calendarRoot.getAttribute('data-current-date'),
+      calendarRoot.getAttribute('data-date'),
+      calendarRoot.getAttribute('data-month'),
+      calendarRoot.textContent
+    ].filter(Boolean);
+    for (var index = 0; index < candidates.length; index += 1) {
+      var match = String(candidates[index]).match(/((?:19|20)\d{2})\D{0,6}(1[0-2]|0?[1-9])/);
+      if (match) return { year: Number(match[1]), month: Number(match[2]) };
+    }
+    var now = new Date();
+    return { year: now.getFullYear(), month: now.getMonth() + 1 };
+  }
+
+  function calendarNavButtons(calendarRoot) {
+    var buttons = Array.from(calendarRoot.querySelectorAll('.jaikki-calendar__nav, button, [role="button"]'));
+    var previous = null;
+    var next = null;
+    buttons.forEach(function (button) {
+      var label = [button.getAttribute('aria-label'), button.getAttribute('title'), button.textContent].filter(Boolean).join(' ').toLowerCase();
+      if (!previous && /(prev|previous|前|戻|‹|←)/.test(label)) previous = button;
+      if (!next && /(next|次|進|›|→)/.test(label)) next = button;
+    });
+    if ((!previous || !next) && buttons.length >= 2) {
+      previous = previous || buttons[0];
+      next = next || buttons[buttons.length - 1];
+    }
+    return { previous: previous, next: next };
+  }
+
+  function navigateCalendarTo(year, month) {
+    var calendarRoot = document.querySelector('.jaikki-calendar-root');
+    if (!calendarRoot) return false;
+    var detail = { year: Number(year), month: Number(month) + 1 };
+    var event = new CustomEvent('ho:calendar:navigate', { detail: detail, cancelable: true });
+    calendarRoot.dispatchEvent(event);
+    if (event.defaultPrevented) return true;
+
+    var current = calendarDateFromRoot(calendarRoot);
+    var delta = ((detail.year - current.year) * 12) + (detail.month - current.month);
+    var direction = delta < 0 ? 'previous' : 'next';
+    if (delta === 0) return true;
+    var remaining = Math.min(240, Math.abs(delta));
+    var step = function () {
+      if (!remaining) return;
+      var activeRoot = document.querySelector('.jaikki-calendar-root');
+      var button = activeRoot ? calendarNavButtons(activeRoot)[direction] : null;
+      if (!button) return;
+      button.click();
+      remaining -= 1;
+      if (remaining) window.setTimeout(step, 55);
+    };
+    step();
+    return true;
+  }
+
+  function setupCalendarYearNavigation(nav, links) {
+    links.forEach(function (link) {
+      var onClick = function (event) {
+        var year = Number(link.getAttribute('data-context-key'));
+        if (!year || !navigateCalendarTo(year, 0)) return;
+        event.preventDefault();
+        links.forEach(function (item) { item.removeAttribute('aria-current'); });
+        link.setAttribute('aria-current', 'page');
+        centerContextNavigation(nav);
+      };
+      link.addEventListener('click', onClick);
+      registerViewCleanup(function () { link.removeEventListener('click', onClick); });
+    });
+  }
+
   function setupContextNavigation() {
     var nav = document.querySelector('.context-nav');
     var sections = Array.from(document.querySelectorAll('[data-context-section]'));
     if (!nav) return;
     centerContextNavigation(nav);
-    if (!sections.length) return;
     var links = Array.from(nav.querySelectorAll('[data-context-key]'));
+    if (!sections.length) {
+      if (root.getAttribute('data-current-view') === 'calendar') setupCalendarYearNavigation(nav, links);
+      return;
+    }
 
     function select(key) {
       links.forEach(function (link) {
@@ -260,6 +363,22 @@
     stage.setAttribute('data-focus-stage', '');
     stage.hidden = true;
     page.insertBefore(stage, list);
+    var supportsFloatingReturn = ['opus', 'schedule'].indexOf(page.getAttribute('data-view-root')) !== -1;
+    var returnControl = null;
+    if (supportsFloatingReturn) {
+      returnControl = document.createElement('button');
+      returnControl.className = 'focus-return-control';
+      returnControl.type = 'button';
+      returnControl.setAttribute('aria-label', '一覧へ戻る');
+      returnControl.setAttribute('data-focus-close', '');
+      returnControl.innerHTML = '<span aria-hidden="true"></span>';
+      page.appendChild(returnControl);
+    }
+    var paradeLayer = document.createElement('div');
+    paradeLayer.className = 'focus-parade-layer';
+    paradeLayer.setAttribute('aria-hidden', 'true');
+    document.body.appendChild(paradeLayer);
+    var paradeOrigins = new Map();
     var activeItem = null;
     var activeSection = null;
     var originParent = null;
@@ -291,7 +410,7 @@
     }
 
     function animateTargetBelowHeader(target) {
-      syncHeaderMetrics();
+      syncLayoutMetrics();
       var header = document.getElementById('site-header');
       if (!header) return Promise.resolve();
       var startY = window.scrollY;
@@ -316,6 +435,20 @@
         };
         window.requestAnimationFrame(step);
       });
+    }
+
+    function moveParadesToViewport(item) {
+      item.querySelectorAll('[data-event-skull-parade]').forEach(function (parade) {
+        if (!paradeOrigins.has(parade)) paradeOrigins.set(parade, { parent: parade.parentNode, next: parade.nextSibling });
+        paradeLayer.appendChild(parade);
+      });
+    }
+
+    function restoreParades() {
+      paradeOrigins.forEach(function (origin, parade) {
+        if (origin.parent && document.documentElement.contains(origin.parent)) origin.parent.insertBefore(parade, origin.next);
+      });
+      paradeOrigins.clear();
     }
 
     function loadDetails(item, type, itemId) {
@@ -334,7 +467,8 @@
           if (activeItem !== item || page.classList.contains('is-focus-closing')) return;
           item.classList.add('has-focus-details');
           layoutEventArtists(item);
-          setupEventSkullParades(item);
+          moveParadesToViewport(item);
+          setupEventSkullParades(paradeLayer);
         });
       };
       item.classList.add('is-expanding');
@@ -377,7 +511,8 @@
       var restoredSection = activeSection;
       var hadDetails = restoredItem.classList.contains('has-focus-details');
       page.classList.add('is-focus-closing');
-      resetEventSkullParades(restoredItem);
+      resetEventSkullParades(paradeLayer);
+      restoreParades();
       restoredItem.classList.remove('is-expanding', 'has-focus-details');
       var restore = function () {
         if (thisTransition !== transitionId || activeItem !== restoredItem) return;
@@ -481,9 +616,18 @@
         activeItem.removeAttribute('aria-current');
         if (activeSection) activeSection.classList.remove('is-picked-section');
       }
+      restoreParades();
+      if (returnControl) returnControl.remove();
+      paradeLayer.remove();
       stage.remove();
       page.classList.remove('is-focus', 'is-focus-leaving', 'is-focus-closing', 'is-focus-returning');
     });
+
+    registerViewCleanup(listen(document, 'ho:resetfocus', function () {
+      if (!activeItem) return;
+      replaceLocation(archiveUrl(), { hoPartial: true });
+      closeItem();
+    }));
 
     var initialUrl = new URL(window.location.href);
     var focusHash = initialUrl.hash.match(/^#focus-(event|work|credit)-([0-9]+)$/);
@@ -505,7 +649,7 @@
 
   function initializeView(options) {
     var settings = options || {};
-    syncHeaderMetrics();
+    syncLayoutMetrics();
     setupTabs();
     setupLinkThanks();
     setupContextNavigation();
@@ -522,7 +666,6 @@
     var visibleUrl = window.location.href;
     var navigating = false;
     var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
-    var viewFadeDuration = 860;
 
     function normalizedPath(url) {
       var path = new URL(url, window.location.href).pathname.replace(/\/+$/, '');
@@ -696,11 +839,16 @@
     document.addEventListener('click', function (event) {
       var link = event.target.closest('a[href]');
       if (!link || event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || link.target) return;
+      if (link.closest('.jaikki-calendar-root')) return;
       var targetUrl = new URL(link.href, window.location.href);
       var targetView = targetUrl.origin === window.location.origin ? viewForUrl(targetUrl.href) : '';
       if (!targetView) return;
       if (targetView === currentView()) {
         event.preventDefault();
+        if (document.querySelector('.focus-page.is-focus') && ['opus', 'schedule'].indexOf(targetView) !== -1) {
+          document.dispatchEvent(new CustomEvent('ho:resetfocus'));
+          return;
+        }
         if (targetUrl.href !== window.location.href) pushLocation(targetUrl.href, { hoShell: true, view: targetView });
         if (targetUrl.hash) activateLocationTarget();
         return;
@@ -721,7 +869,22 @@
     });
   }
 
+  function revealInitialView() {
+    var main = document.querySelector('.site-main');
+    var context = document.querySelector('[data-view-context] > *');
+    var isHome = root.getAttribute('data-current-view') === 'home';
+    if (!isHome || window.matchMedia('(prefers-reduced-motion: reduce)').matches || !main || !main.animate) {
+      root.classList.add('is-view-ready');
+      return;
+    }
+    Promise.all([main, context].filter(Boolean).map(function (element) {
+      var animation = element.animate([{ opacity: 0 }, { opacity: 1 }], { duration: viewFadeDuration, easing: 'ease-in-out', fill: 'both' });
+      return animation.finished.catch(function () {}).then(function () { animation.cancel(); });
+    })).then(function () { root.classList.add('is-view-ready'); });
+  }
+
   setupLayoutMetrics();
   initializeView();
   setupViewNavigation();
+  revealInitialView();
 }());
changeddocs/BETA1-CHANGELOG.md
--- previous/docs/BETA1-CHANGELOG.md
+++ 0.14.2/docs/BETA1-CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.14.2-beta.1
+
+- Added the 0.86-second initial home fade-in using the shared transition duration.
+- Added measured viewport chrome variables shared by Profile, Calendar, the floating return control, and skull parade placement.
+- Added the shared Opus/Schedule vertical return capsule and unified list reset behavior.
+- Restored Opus/Schedule list views when their active primary navigation item is selected again.
+- Added Calendar year-button behavior that targets January only while Calendar is active; Schedule keeps its original year navigation.
+- Excluded Calendar controls from the partial-page navigation interceptor.
+- Moved focused skull parades to a fixed footer-adjacent layer while preserving their existing width and layer order.
+
 # Hiroki Ohnuma(β.1) 変更記録
 
 ## 共通表示
changedfunctions.php
--- previous/functions.php
+++ 0.14.2/functions.php
@@ -9,7 +9,7 @@
 	exit;
 }
 
-define( 'HO_THEME_VERSION', '0.14.1-beta.1' );
+define( 'HO_THEME_VERSION', '0.14.2-beta.1' );
 
 require_once get_template_directory() . '/inc/setup.php';
 require_once get_template_directory() . '/inc/post-types.php';
changedheader.php
--- previous/header.php
+++ 0.14.2/header.php
@@ -12,6 +12,7 @@
 	<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
 	<meta name="apple-mobile-web-app-capable" content="yes">
 	<meta name="apple-mobile-web-app-status-bar-style" content="default">
+	<script>document.documentElement.classList.add('ho-js');</script>
 	<?php wp_head(); ?>
 </head>
 <body <?php body_class( 'ho-site' ); ?>>
changedinc/template-tags.php
--- previous/inc/template-tags.php
+++ 0.14.2/inc/template-tags.php
@@ -516,7 +516,6 @@
 			</dl>
 		</div>
 		<div class="focus-item__extras" data-focus-details aria-live="polite"></div>
-		<?php ho_focus_back_link( 'event', $post_id ); ?>
 	</article>
 	<?php
 }
@@ -549,7 +548,6 @@
 			<?php endforeach; ?>
 		</dl>
 		<div class="focus-item__extras" data-focus-details aria-live="polite"></div>
-		<?php ho_focus_back_link( 'work', $post_id ); ?>
 	</article>
 	<?php
 }
changedinc/view-shell.php
--- previous/inc/view-shell.php
+++ 0.14.2/inc/view-shell.php
@@ -92,7 +92,7 @@
 		$active_year = $years ? $years[0] : '';
 		if ( is_singular( 'ho_event' ) ) $active_year = substr( (string) get_post_meta( get_the_ID(), '_ho_event_date', true ), 0, 4 );
 		?>
-		<nav class="context-nav context-nav--schedule" aria-label="Schedule navigation">
+		<nav class="context-nav context-nav--schedule" data-context-mode="<?php echo $is_calendar ? 'calendar' : 'schedule'; ?>" aria-label="Schedule navigation">
 			<a class="context-nav__section-link<?php echo $is_calendar ? '' : ' is-current'; ?>" href="<?php echo esc_url( ho_view_url( 'schedule' ) ); ?>">Schedule</a>
 			<div class="context-nav__years">
 				<?php foreach ( $years as $year ) : ?><a data-context-key="<?php echo esc_attr( $year ); ?>" href="<?php echo esc_url( ho_view_url( 'schedule' ) . '#schedule-' . $year ); ?>"<?php echo (string) $year === (string) $active_year ? ' aria-current="page"' : ''; ?>><?php echo esc_html( $year ); ?></a><?php endforeach; ?>
changedstyle.css
--- previous/style.css
+++ 0.14.2/style.css
@@ -3,7 +3,7 @@
 Theme URI: https://8686.jaikki-rocky.com/
 Author: Hiroki Ohnuma / Codex
 Description: Beta 1 edition of the Hiroki Ohnuma official site theme. A lightweight, plugin-independent theme preserving the current visual system.
-Version: 0.14.1-beta.1
+Version: 0.14.2-beta.1
 Requires at least: 6.6
 Requires PHP: 8.0
 Text Domain: hiroki-ohnuma

原因分析・次回改修への指針

ヘッダーと表示領域の責務が密結合し、ページ別高さ補正と競合したため。


対象ZIP
hiroki-ohnuma-beta1-0.14.2.zip


再利用すべき判断基準

  • 概算値や固定オフセットではなく、共通レイアウト基準または実測された表示領域を一度だけ使う。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。

0.14.1

当時の改修目的

Scheduleの単一表示・管理画面・入出力、共通フェード、Tetris描画と行消去をまとめて整備する。

実装した変更

・全画面ビュー遷移を方向付きスライドから0.86秒フェードへ統一

・`prefers-reduced-motion` 環境では遷移アニメーションを省略

・Profileの写真・本文と、Opus/Scheduleのレコード領域で同じコンテナ幅を使用

・単一展開時にアイキャッチ画像を表示

・横長・正方形寄りの画像は上部へ幅広く表示

・縦長画像は、情報がある場合のみPCで画像/情報の2カラム表示

・縦長画像に付随情報がない場合は、空の右カラムを作らず中央表示

・URLが存在する場合だけ関連リンクを表示

・説明文をVenue/Time/Artists/Chargeと同系統の情報表示スタイルへ統一

・出演者は1〜5名を1列、6名以上を原則2列で表示

・長い出演者名は2列に無理に押し込まず1行分を使用

・スマートフォンでは出演者と縦長画像レイアウトを1列へ戻す

後続版との照合で確定した成果

判定:部分達成を含む確定評価。0.14.2以降でも撤去されず残った仕組みは継続採用とみなす。

Schedule画像レイアウト、出演者列、関連リンク、CSV/Markdown入出力、管理一覧順、Tetris高DPI描画と複数行消去を実装。

後続版で未達・再修正と判明した項目

判定:未達または部分達成。後続版 0.14.2 の要望・修正内容と照合して確定。

この版単体で明示された失敗記録はない。ただし後続版でヘッダー、ページ専用ナビ、表示領域、Back挙動の再設計が必要になった。

次版で実際に求められたこと:共通ヘッダー変数、Opus/ScheduleのBack、同一主メニュー再押下、Calendar年移動、ガイコツ表示を整理する。

実際の変更箇所・実装内容

変更ファイル

変更: 404.php, README.md, archive-ho_event.php, archive-ho_work.php, assets/css/admin.css, assets/css/editor.css, assets/css/tetris.css, assets/css/theme.css, assets/img/8686_dog.webp, assets/img/8686_letters-from-melodies.jpg, assets/img/8686_lost-pieces.png, assets/img/8686_parallactic-angle.jpg, assets/img/8686_return-to-protosun.jpg, assets/img/8686_skull-run.webp, assets/img/8686_skull.png, assets/img/8686_wallpaper.webp, assets/img/tetris/8686_skull_black.png, assets/img/tetris/8686_skull_blue.png, assets/img/tetris/8686_skull_green.png, assets/img/tetris/8686_skull_red.png, assets/img/tetris/8686_skull_yellow.png, assets/img/tetris/8686_smf_Skull_Orange_Girl.png, assets/img/tetris/8686_smf_Skull_Pink_Girl.png, assets/js/admin.js, assets/js/tetris.js, assets/js/theme.js, data/8686-bootstrap.json, docs/8686-event-template.csv, docs/8686-event-template.md, docs/8686-import-template.csv, docs/8686-import-template.md, docs/BETA1-CHANGELOG.md, docs/IMPORT-GUIDE.md, footer.php, front-page.php, functions.php, header.php, home.php, inc/bootstrap-data.php, inc/dev-preview.php, inc/importer.php, inc/legacy-redirects.php, inc/meta-boxes.php, inc/post-types.php, inc/preview.php, inc/settings.php, inc/setup.php, inc/template-tags.php, inc/view-shell.php, index.php, page-templates/calendar.php, page-templates/links.php, page-templates/profile.php, page-templates/releases.php, page-templates/tetris.php, page-templates/works.php, page.php, single-ho_credit.php, single-ho_event.php, single-ho_work.php, single.php, style.css, template-parts/view-tetris.php
追加: 404.php, README.md, archive-ho_event.php, archive-ho_work.php, assets/css/admin.css, assets/css/editor.css, assets/css/tetris.css, assets/css/theme.css, assets/img/8686_dog.webp, assets/img/8686_letters-from-melodies.jpg, assets/img/8686_lost-pieces.png, assets/img/8686_parallactic-angle.jpg, assets/img/8686_return-to-protosun.jpg, assets/img/8686_skull-run.webp, assets/img/8686_skull.png, assets/img/8686_wallpaper.webp, assets/img/tetris/8686_skull_black.png, assets/img/tetris/8686_skull_blue.png, assets/img/tetris/8686_skull_green.png, assets/img/tetris/8686_skull_red.png, assets/img/tetris/8686_skull_yellow.png, assets/img/tetris/8686_smf_Skull_Orange_Girl.png, assets/img/tetris/8686_smf_Skull_Pink_Girl.png, assets/js/admin.js, assets/js/tetris.js, assets/js/theme.js, data/8686-bootstrap.json, docs/8686-event-template.csv, docs/8686-event-template.md, docs/8686-import-template.csv, docs/8686-import-template.md, docs/BETA1-CHANGELOG.md, docs/IMPORT-GUIDE.md, footer.php, front-page.php, functions.php, header.php, home.php, inc/bootstrap-data.php, inc/dev-preview.php, inc/importer.php, inc/legacy-redirects.php, inc/meta-boxes.php, inc/post-types.php, inc/preview.php, inc/settings.php, inc/setup.php, inc/template-tags.php, inc/view-shell.php, index.php, page-templates/calendar.php, page-templates/links.php, page-templates/profile.php, page-templates/releases.php, page-templates/tetris.php, page-templates/works.php, page.php, single-ho_credit.php, single-ho_event.php, single-ho_work.php, single.php, style.css, template-parts/view-tetris.php

baseline(初期基準版)
比較元のテーマZIPがこの記録群にないため、0.14.1以前からの行単位差分は復元できません。以下は0.14.1に含まれるファイル一覧です。

404.php
README.md
archive-ho_event.php
archive-ho_work.php
assets/css/admin.css
assets/css/editor.css
assets/css/tetris.css
assets/css/theme.css
assets/img/8686_dog.webp
assets/img/8686_letters-from-melodies.jpg
assets/img/8686_lost-pieces.png
assets/img/8686_parallactic-angle.jpg
assets/img/8686_return-to-protosun.jpg
assets/img/8686_skull-run.webp
assets/img/8686_skull.png
assets/img/8686_wallpaper.webp
assets/img/tetris/8686_skull_black.png
assets/img/tetris/8686_skull_blue.png
assets/img/tetris/8686_skull_green.png
assets/img/tetris/8686_skull_red.png
assets/img/tetris/8686_skull_yellow.png
assets/img/tetris/8686_smf_Skull_Orange_Girl.png
assets/img/tetris/8686_smf_Skull_Pink_Girl.png
assets/js/admin.js
assets/js/tetris.js
assets/js/theme.js
data/8686-bootstrap.json
docs/8686-event-template.csv
docs/8686-event-template.md
docs/8686-import-template.csv
docs/8686-import-template.md
docs/BETA1-CHANGELOG.md
docs/IMPORT-GUIDE.md
footer.php
front-page.php
functions.php
header.php
home.php
inc/bootstrap-data.php
inc/dev-preview.php
inc/importer.php
inc/legacy-redirects.php
inc/meta-boxes.php
inc/post-types.php
inc/preview.php
inc/settings.php
inc/setup.php
inc/template-tags.php
inc/view-shell.php
index.php
page-templates/calendar.php
page-templates/links.php
page-templates/profile.php
page-templates/releases.php
page-templates/tetris.php
page-templates/works.php
page.php
single-ho_credit.php
single-ho_event.php
single-ho_work.php
single.php
style.css
template-parts/view-tetris.php

原因分析・次回改修への指針

初期基準版であり、後に追加された全体レイアウト要件までは含まれていなかった。


対象ZIP
hiroki-ohnuma-beta1-0.14.1.zip


再利用すべき判断基準

  • 要望を見た目だけでなく、状態管理・DOM構造・CSS責務まで分解して実装する。
  • 次版で修正された箇所は、この版では未達だったものとして扱い、同じ方式を再利用しない。
  • 後続版で維持された仕組みだけを成功として再利用し、後続版で置換された方式は失敗例として参照する。