ツールバーのオーバーフロー計算が `data-prevent-overflow` 要素を正しく考慮するよう修正

basecamp/lexxy

ツールバーのオーバーフロー計算ロジックが data-prevent-overflow 属性を持つ要素を無視していたバグを修正しました。これにより、エクステンション経由で挿入された「オーバーフロー禁止」ボタンがツールバーのコンテナ幅を突き破る問題が解消されます。

背景

Lexxy のツールバーは、表示幅が不足すると一部のボタンをオーバーフローメニューへ移動するコンパクト機能を持っています。ただし、data-prevent-overflow 属性を持つ要素はオーバーフローメニューへの移動対象外として扱う設計になっています。問題は、オーバーフローが発生するかどうかを計算する際にこれらの要素が考慮されていなかった点にあります。

エクステンションが data-prevent-overflow 付きのボタンを挿入した場合、そのボタンはオーバーフローメニューには送られないにもかかわらず、幅の計算では他のボタンと同様に扱われ、結果としてコンテナ幅をはみ出す表示崩れが発生していました。

技術的な変更

#refreshOverflow() および #compactMenu() のロジックが見直され、オーバーフロー判定の前に #hideOverflowMenuButton() の呼び出しが追加されました。また、オーバーフロー状態の検出に使うセレクタが #overflowMenu から #overflowMenuDropdown へ変更されており、ドロップダウンの子要素数をより正確に参照するようになっています。

変更前:

#refreshOverflow() {
  this.#resetToolbarOverflow()
  this.#reindexToolbarItems()
  this.#compactMenu()

  const isOverflowing = this.#overflowMenu.children.length > 0

  this.toggleAttribute("overflowing", isOverflowing)

  this.#overflow.style.display = isOverflowing ? "block" : "none"
  this.#overflow.setAttribute("nonce", getNonce())

  this.#overflowMenu.toggleAttribute("disabled", !isOverflowing)
}

変更後:

#refreshOverflow() {
  this.#hideOverflowMenuButton()
  this.#resetToolbarOverflow()
  this.#reindexToolbarItems()
  this.#compactMenu()

  const isOverflowing = this.#overflowMenuDropdown.children.length > 0

  this.toggleAttribute("overflowing", isOverflowing)
}

あわせて、パブリックメソッド closeDropdowns() が新たに追加されました。このメソッドは引数 except で指定したドロップダウンを除く全ドロップダウンを閉じるもので、ツールバー内の複数ドロップダウン間の排他制御に利用されます。

closeDropdowns({ except } = {}) {
  this.#dropdowns.forEach((dropdown) => {
    if (dropdown !== except) {
      dropdown.close({ focusEditor: false })
    }
  })
}

テスト側では、data-prevent-overflow 付きのボタンを2つ動的に挿入し、360px という狭いビューポートでオーバーフロー再計算を行うブラウザテストが追加されました。テストは「追加したボタンがオーバーフローメニューに入らないこと」と「全ツールバーアイテムがコンテナの左右パディング内に収まること」の2点を検証します。

設計判断

リフロー回避のための読み書き分離という既存方針を維持しつつ、オーバーフロー判定前に表示状態をリセットする手順が整理されています。#hideOverflowMenuButton()#resetToolbarOverflow() より先に呼び出すことで、メニューボタン自身の表示がレイアウト計算に干渉しないようにする意図が読み取れます。

#overflowMenu から #overflowMenuDropdown への参照先変更は、オーバーフロー状態の有無をドロップダウンの実コンテンツ数で判定するという、より意味論的に正確な実装への移行といえます。また、オーバーフローメニューボタンの display 操作や nonce 付与といった副作用処理が #refreshOverflow() から取り除かれており、責務の分離が進んでいます。

まとめ

この修正は、エクステクションによるツールバー拡張という正規のユースケースがオーバーフロー計算のバグによって壊れていた問題を正確に特定し、計算順序と参照先の整理によって解消したものです。data-prevent-overflow の意味論がレイアウト計算全体に一貫して反映されたことで、エクステンション開発者は属性の挙動を信頼できるようになります。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
8edaddc8

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)の3部構成が明確で、各セクションの役割が適切に果たされています。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライトは正しく使用されていますが、GitHubのPRリンク記法がガイドラインの推奨形式([#123](URL))とわずかに異なります([PR #1058](URL))。ただし、リンク自体は正しく機能しています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

ツールバーの内部実装、リフロー回避、DOM操作といった専門的なトピックを扱い、対象読者であるエンジニアに適した技術レベルと表現です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成で、各パラグラフがトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、非常に読みやすい構造になっています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは、提供されたDiffの内容(変更前・変更後・追加メソッド)を正確に引用しています。テストコードに関する説明もDiffの内容と整合性が取れています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`data-prevent-overflow`、リフロー、セレクタ、ビューポートなどの技術用語が文脈に応じて正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

コード変更の意図や影響に関する説明は、Diffの内容と論理的に整合しており、技術的に正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事の主張はすべてPR情報(Description、Diff)に基づいています。「設計判断」セクションはPRに明記されていないものの、コードの変更内容から論理的に導かれる深い分析であり、ハルシネーションには該当しません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#1058)、メソッド名、テストで使用されたビューポート幅(360px)など、数値や固有名詞はすべて正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルはPRのタイトル「Calculate for `prevent-overflow` items correctly in toolbar」を正確に翻訳し、記事全体の主題と一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR情報にない外部知識(バージョン情報やリリース予定など)の追加はなく、提供された情報源に忠実です。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「〜でした」「〜します」といった時間表現は、PRの文脈と矛盾なく、正確に使用されています。