Obsidian 1.11.4 Mobile 以降で自作プラグインの入力欄の下に余計な余白が残る問題への対応


Obsidianの自作プラグイン Nods で、入力欄の下に余計な余白が残ってしまう事案が続いており、頭を悩ませていた

300

操作性改善を目的にReact版を試しに作っていても同じ問題にぶち当たったので、AIにブチギレながら色々確認していたところ、以下のことに気がついた

  • Obsidian 1.11.4 Mobile 以降では、サイドバーのビュー切り替えやノート名といった要素が画面上部から下部に移転している
  • それ以前の場合、サイドバーの下部に入力欄を持つプラグインは、ビュー切り替えやノート名が表示される箇所と、入力欄を起動した際に展開されるソフトウェアキーボードがバッティングすること自体が起こりえなかった
  • しかし要素の移転後は、ソフトウェアキーボードとビュー切り替えなどの要素がどうしてもバッティングしてしまう
    • このとき、素直に画面下部の要素がキーボードの上に出てくるわけではなく、要素を表示する余白分が迫り上がって表示されるのが今回の事象における真の原因と判断された

ということで、以下のCSSを使ってFixした

.is-phone .workspace-drawer-tab-options,
.is-phone .workspace-drawer-tab-options-list,
.is-phone .workspace-drawer-header{
  max-height: max(0px, calc(var(--touch-size-l) - var(--keyboard-height)));
  overflow: hidden !important;
}
.is-phone .workspace-drawer-inner{
  padding-bottom: max(0px, calc(max(var(--safe-area-inset-bottom), var(--size-4-4)) - var(--keyboard-height))) !important;
}

改善後は以下の通り。すばらしい

300

しかし別の問題が発生。キーボードが開いていない際に、タブスイッチャーが機能しなくなる(ビューの切り替えができない)

  • ChatGPTは有効な対策を提示できず、ついには途中案でループしだした

そこでGeminiに泣きついてみた

提示された案については具体的なコードを示すと長くなるので割愛するが、結論から言うと CSSを修正するだけでは無理で、入力欄を構成する元のSvelteモジュールを修正する必要があった

以下は Gemini の回答をそのままコピペしているが、かなり文章としてもまとまっているため参考になりそうな気がする

課題①:

Obsidianのボトムバー(タブスイッチャー)があるため、入力欄の下に巨大な余白ができる。

  • max-height: 0 で潰すと、余白は消えるがボタンが押せなくなる。
  • 要素を残すと、Obsidianが「要素がある」と判断して余白(Padding)を強制挿入してくる。

解決策: 「幽体離脱(Absolute Ghosting)」

  • 手法: CSSでタブスイッチャーを .workspace-drawer-tab-options { position: absolute; } に指定しました。
  • なぜ効いたか:
    • absolute にされた要素は、DOMの「文書の流れ(Flow)」から外れます。
    • これにより親要素は「中身の高さは0だ」と錯覚し、余計な余白(Padding)を作らなくなります。
    • しかし、要素自体は消えていない(display: none ではない)ため、見た目は残り、クリック判定も生き続けます。
    • 結果、「余白はゼロだが、ボタンは押せる」という矛盾した要件をクリアしました。

課題②:

送信ボタンをタップすると、送信される前にキーボードが閉じてしまい、もう一度押さないといけない(ゴーストクリック現象)。

解決策: 「先手必勝(Prevention)」

  • 手法: click イベントではなく、それより早く発生する touchstart (mobile) / mousedown (PC) を使い、ハンドラ内で e.preventDefault() を呼びました。
  • なぜ効いたか:
    • iOSでは、ボタン以外の場所(入力欄外)を触ると、「入力終了」とみなして即座に blur イベント(フォーカス外し)を発火させ、キーボードを閉じようとします。これが click イベントより先に走ります。
    • preventDefault() は、ブラウザに 「このタップによるデフォルトの挙動(フォーカス移動やスクロールなど)をやるな」 と命令します。
    • これにより、フォーカスは入力欄に残ったまま(=キーボードは出たまま)、送信処理だけを実行させることに成功しました。

課題③:

キーボードを閉じた時、入力欄の下に少しだけ隙間が空いていた。

解決策: 「過剰な安全マージンの削除」

  • 手法: .composer クラスに無条件で入っていた env(safe-area-inset-bottom) を削除しました。
  • なぜ効いたか:
    • safe-area は「iPhoneの下部の黒いバー」を避けるためのものですが、今回は入力欄の下に別のビュー(Obsidianのフッターなど)があるレイアウトでした。
    • 「常に画面の最下端に固定されている」わけではない要素にこの余白を与えると、単なる無駄な隙間になります。状況に応じた(あるいは親要素に任せた)余白管理が正解でした。

今回の教訓

  1. 「消す」のではなく「浮かせる」: UIを残しつつレイアウト計算から除外したいときは、max-height: 0 ではなく position: absolute が特効薬。
  2. iOSのタップは click を信用しない: キーボード絡みのボタン操作は、touchstart + preventDefault() が鉄則。
  3. Obsidianモバイルは親(本体CSS)が強すぎる: プラグイン側で頑張るより、:has() を使って親のスタイルを無効化する「下克上CSS」が近道。