プロフィール

arismmn timeline blog

← ブログ一覧に戻る

フローティングメニューの管理機能実装と、ブラウザ拡張によるCSS注入との戦い

機能実装AstroUI/UXデバッグ

概要

左下に表示されるフローティングメニューのリンクを、管理画面から自由に設定できる機能を実装した。実装自体はほぼ完成したが、管理ページのレイアウトがどうしても左寄りになる問題が最後まで残り、原因の特定に手間取った。最終的にはブラウザ拡張機能が html 要素と body 要素に外部から CSS を注入していたことが判明した。


実装内容

Astro ページの自動検出

追加できるページの候補を手動でリストアップするのは管理コストが高い。そこで Vite の import.meta.glob を利用し、ビルド時に src/pages/ 以下の .astro ファイルを自動列挙する方式を採用した。

const pageModules = import.meta.glob('/src/pages/**/*.astro');

取得したファイルパスを URL に変換し、パスの構造からラベルを自動推測して表示する。ラベルは追加前に編集できるようにしてある。

データ保存

新しいテーブルやマイグレーションは不要。既存のキーバリュー型の設定テーブルに JSON 形式で保存することで、スキーマ変更なしに実装できた。

タッチ・マウス両対応のドラッグ&ドロップ

image

HTML5 の Drag API はモバイルで動作しない。そのため、mousedown / mousemove / mouseuptouchstart / touchmove / touchend を個別に実装した。

ドラッグ開始の誤検知を防ぐため、マウスボタンを押した時点ではなく、5px 以上移動した時点でドラッグを開始するしきい値を設けている。これにより、単純なクリックでゴースト要素が出現する問題も解消した。

// 5px のしきい値を超えたときだけドラッグ開始
const dx = e.clientX - startX;
const dy = e.clientY - startY;
if (dx * dx + dy * dy < 25) return;

Astro のスコープ CSS と動的生成要素

Astro の <style> タグは自動的にスコープ処理され、テンプレート内の要素には data-astro-cid-xxxx 属性が付与される。しかし JavaScript の createElement() で生成した要素にはこの属性が付かないため、スコープ CSS が届かない。

ドラッグ中に表示するゴースト要素や、並べ替えリストの各行など、JavaScript で動的に生成する要素のスタイルには <style is:global> を使用した。


中央配置が効かない問題

管理ページのコンテナを画面中央に配置しようとして、margin: 0 autodisplay: flex; justify-content: center など複数の方法を試したが、いずれも左寄りのままだった。

当初は Astro のスコープ処理が原因と疑ったが、同じ構造の他の管理ページでは問題が起きていなかった。インラインスタイルで margin-left: auto !important を直接記述しても変わらない。

解決の糸口

Chrome DevTools の Computed タブで html 要素のスタイルを確認したところ、width: 2560px という固定値が当たっていた。

html {
  width: 2560px;  /* ← 意図しない固定値 */
  overflow-x: hidden;
}
body {
  max-width: 1200px;  /* ← ブラウザ拡張が追加 */
  font-family: sans-serif;
  background-color: #f9f9f9;
  color: #4a3f35;
}

body に当たっていた background-color: #f9f9f9color: #4a3f35 は、こちらで書いた CSS とは別の値だった。ブラウザ拡張機能が htmlbody に外部スタイルを注入していたことがわかった。

html が 2560px(スクリーンの物理ピクセル幅と一致)に固定されているため、ビューポートの外に大きくはみ出している。overflow-x: hidden でその横スクロールが隠されているため、一見正常に見えた。bodymax-width: 1200px で制限され、左端から配置されることで、コンテナを中央寄せしても「分割されたように見える」状態になっていた。

修正方法

is:global の CSS で !important を使い、拡張機能によるスタイルを上書きした。

html {
  width: 100% !important;
  max-width: 100% !important;
  overflow-x: auto !important;
}
body {
  display: block !important;
  width: 100% !important;
  max-width: none !important;
}

この修正でレイアウトが正しく中央配置された。


学び

  • CSS が効かないとき、外部注入を疑う: インラインスタイルや !important でも効かない場合、ブラウザ拡張が DOM に手を加えている可能性がある。DevTools の Computed タブで、自分が書いていない CSS プロパティが当たっていないか確認する。
  • html 要素の幅は盲点になりやすい: body や container のスタイルだけ見ていると、html 要素が原因の問題に気づきにくい。レイアウト崩れの調査は html から順に見ていく。
  • import.meta.glob はページ自動検出に有効: Cloudflare Workers のランタイムにはファイルシステムアクセスがないため、ページ一覧の取得はビルド時に完結する Vite の機能を使う必要がある。
Claude Code
Powered by
Claude Code
(使用モデル Sonnet 4.6)
← ブログ一覧に戻る