iOSのXの新しいハーフモーダル形式WebViewで画面外にある sticky 要素が壊れる問題

TL;DR

  • Webページの画面外に position: sticky がある場合、その要素の位置が数px上に移動し sticky が動作しない問題がある。
  • iOSもしくはXのバグである可能性が高い
  • IntersectionObserver などを使用して画面に sticky 要素が表示されてからレンダリングすると解決する
  • 誰か原因突き止めてくれ!!

再現環境

2025年12月9日現在

  • iPhone13 / iOS 26.1
  • X v11.46.1

本編

最近、iOSアプリのXで新しい WebView がリリースされました。ハーフモーダル形式のWebViewとなっており、ユーザーはWebページからXへの復帰がしやすくなっています。

個人的にはポジティブな変更ですが、一部問題が発生したので本エントリで共有しようと思います。

というのも、 sticky になっている要素が数pxほど上に上がってしまいさらに sticky が動作しないという問題です。

実際にWebViewで数px上部に表示されてしまっている例

これのなにが問題かというと、 sticky 要素に操作するボタン等があると操作ができなくなります。また、スクロールしても sticky 設定している要素が画面に表示されない問題も発生します。

再現環境

再現する環境は、「iOS」の「X」の「WebView」です。Safari や Android の Chrome などでは再現しないです。なので、 iOS もしくは X のバグの可能性が高いです。

そして、以下に再現する PoC を作りました。URLをXでポストして確認してみてください。

github.com

Note

動画を GitHub にアップロードしています。

どういうケースで再現するか

私が確認した限りでは、画面外で sticky 要素がレンダリングされる場合 に再現しそうです。そのため、ファーストビュー内だったり、表示されている画面内でレンダリングされた場合には表示がおかしくなるようなことは無いようです。

ファーストビューで sticky を表示させたケース。問題は発生していない

対応方法

画面内に表示されたら sticky 要素をレンダリングできると良さそうです。IntersectionObserver API を使用して遅延表示します。どうやら display: nonedisplay: block にするだけでも解決できそうだったので以下のようなスクリプトを配置してみました。

const stickyContainer = document.querySelector('.js-sticky-container');
if(!stickyContainer) {
    console.error('Sticky container not found');
}

const observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            console.log('Sticky container is intersecting');
            const stickyElement = entry.target.querySelector('.js-sticky');
            if (stickyElement) {
                stickyElement.style.display = 'block';
            }
            observer.unobserve(entry.target);
        }
    });
}, { threshold: 0.3 });
observer.observe(stickyContainer);

Note