WordPressのスティッキーヘッダーを実装してたら、iPhoneだけでposition: fixedが全く効かなくてハマった話です🐧
結論から言うと、container-type: inline-sizeが犯人でした。
同じ問題にハマってる人の参考になれば!!
まず結論: JavaScriptで解決する
container-typeを削除して、スクロールバーを除いた幅はJavaScriptで計算する方法に切り替えます。
修正前(NG)
body {
container-type: inline-size;
--width__viewport: 100cqw;
font-family: var(--font-family__base);
// ...
}修正後(OK)
body {
font-family: var(--font-family__base);
// container-type を削除
// ...
}JavaScriptで幅を計算するコード:
const setViewportWidthVariable = () => {
const setWidth = () => {
const viewportWidth = document.documentElement.clientWidth;
document.documentElement.style.setProperty(
'--js-width__viewport',
`${viewportWidth}px`,
);
};
setWidth();
window.addEventListener('load', () => {
setTimeout(setWidth, 50);
});
window.addEventListener('resize', setWidth);
};
export { setViewportWidthVariable };main.jsで初期化:
import { setHeaderHeightVariable } from './modules/set-header-height-variable.js';
import { setViewportWidthVariable } from './modules/set-viewport-width-variable.js';
document.addEventListener('DOMContentLoaded', () => {
setHeaderHeightVariable();
setViewportWidthVariable(); // 追加
// ...
});SCSS側では変数名を--js-width__viewportに変更:
.l-inner--release-left .l-inner__content {
margin-left: calc(50% - var(--js-width__viewport) / 2);
}これでiPhoneでもちゃんと動くようになりました!!
何が起きたか
WordPressテーマでスティッキーヘッダーを実装中、PCでは完璧に動いてるのにiPhoneだけ全く効かない…!!
症状はこんな感じでした:
- トップページ: スクロールしてもヘッダーが固定されない
- 下層ページ: 最初から
is-stickyクラスが付いてるのに固定されない - PC・デスクトップ: 完全に正常動作
- iPhone(Safari・Chrome両方): 完全に動かない
最初はJavaScriptのタイミング問題かと思ったんですが、下層ページではページ読み込み時から単純にposition: fixedを設定してるだけなのに動かない。
これはCSS側の問題だなと。
原因: container-typeがcontaining blockを生成
犯人はbodyに設定してたcontainer-type: inline-sizeでした。
元々、Container Queriesの100cqwを使ってスクロールバーを除いた正確なビューポート幅を取得するために設定してたんですが、これが裏目に出ました。
仕組み
CSS Container Queriesのcontainer-typeプロパティはcontaining blockを生成します。
これにより、子孫要素のposition: fixedがviewport基準ではなく、親要素(body)基準になってしまうんです。
この問題は仕様上すべてのブラウザで発生するはずですが、iOS Safari/Chromeで特に顕著でした。
モバイルブラウザのレンダリングエンジンが仕様をより厳密に扱ってるからかもしれません。
解決策の実装手順
ステップ1: container-typeを削除
bodyからcontainer-type: inline-sizeと--width__viewport: 100cqwを削除します。
ステップ2: JavaScriptで幅を計算
document.documentElement.clientWidthでスクロールバーを除いた正確な幅を取得し、CSS変数にセットします。--js-width__viewportという名前にすることで、JavaScriptで動的に設定される値だと明確になります(--js-height__headerと同じ命名規則)。
ステップ3: SCSS変数名を更新
レイアウトファイルで使ってた--width__viewportをすべて--js-width__viewportに変更します。
grep で一括置換すると楽です。
Chrome 145で100vwがスクロールバー認識に対応
実は2026年1月にリリースされたChrome 145で、長年の100vw問題がついに解決しました!!
特定の条件下で100vwが垂直スクロールバーの幅を認識するようになったんです。
有効になる条件
html要素に以下のいずれかが設定されている場合:
overflow-y: scroll— 垂直スクロールバーを常に表示scrollbar-gutter: stable— スクロールバー用のスペースを確保
これらが設定されていると、100vwは自動的にスクロールバーの幅を差し引いて計算されます。
ブラウザサポート状況(2026年3月時点)
- ✅ Chrome 145+: サポート済み
- ✅ Chromiumベースブラウザ: サポート済み(Edge, Brave等)
- ⏳ Safari: 未サポート
- ⏳ Firefox: 未サポート
今後の推奨アプローチ
当面(2026年〜2027年頃)はJavaScriptによる計算を継続するのがベストです。
すべてのブラウザで確実に動作しますし、スクロールバーの幅を正確に取得できます。
将来的(2027年以降)、主要ブラウザが対応したらscrollbar-gutter: stableと100vwの組み合わせに移行できます。
その時はJavaScriptによる計算は不要になります。
ハマりポイント
ハマりポイント①: iPhoneだけで再現
PCでは動いてるのにiPhoneだけダメっていうのが厄介でした。
モバイルブラウザの方が仕様を厳密に扱うケースがあるので、実機テストは必須です。
ハマりポイント②: containing blockの概念
container-typeだけじゃなく、transform、filter、perspective、will-changeなども containing blockを生成します。position: fixedを使う場合は、親要素にこれらのプロパティがないか確認しましょう。
まとめ
container-typeは containing blockを生成し、子孫要素のposition: fixedに影響する- 解決策は
container-typeを削除してJavaScriptで幅を計算すること document.documentElement.clientWidthでスクロールバーを除いた幅が取得できる- Chrome 145以降は特定条件下で
100vwがスクロールバー認識に対応したが、まだSafari/Firefoxは未サポート - 当面はJavaScript計算が最も確実な解決策
参考
- Using 100vw is now scrollbar-aware (in Chrome 145+) – Bram.us
- [css-values] Use of 100vw is causing pointless horizontal scrollbars – CSSWG
- New CSS Viewport Units Do Not Solve The Classic Scrollbar Problem – Smashing Magazine
以上!!
誰かのお役に立てれば嬉しいです🐧
