【追記: Safariでも動くようになった!】scroll-margin-topがsafariでうまく効かない問題と現状のワークアラウンド
🎉 追記: Safariでもscroll-margin-topをスクロール位置の調整に使えるようになった
iOS Safari 14.7〜、また macOS Safari 14.1〜でscroll-margin-top
による位置調整が問題なく動くようになりました!
手元のiOS端末で試してみた限り、scroll-padding-top
でもイメージ通りに動くようです(2021/09/24時点ではcaniuseに反映されていませんが…)。
Webサイトにおいてヘッダーを上部に固定したいというケースはよくあると思います。その際に問題になるのがページ内リンクが固定ヘッダーで隠れてしまうというものです。
Zennでは記事の表示ページでヘッダーを固定しており、なおかつページ内リンクの集合である目次を使っているため、この問題に対処しなければなりません。
scroll-margin-top
CSSには上記の問題を簡単に解決できるscroll-margin-top
というプロパティがあります。これを使うとページ内リンクの表示位置を調整できます。
:target {
scroll-margin-top: 60px;
}
ヘッダーの高さをCSS変数で指定しておけばもっと楽ですね。
:target {
scroll-margin-top: var(--header-height);
}
ちょうど良いサンプルがCodeSandboxにあったので貼っておきます。
過去の話: 「固定ヘッダーに遷移先が隠れてしまう問題」にSafariでも対応するには
ChromeやFireFoxをお使いの場合は、ページ上部に固定されたリンクをクリックすると、ヘッダーに隠れずに表示されることが分かると思います。
ただしscroll-margin-top
は2020年12月の時点ではSafari(モバイル・デスクトップの両方)でイメージ通りに動きません。リンク先に遷移したときに固定ヘッダーに隠れてしまいます。
詳しくはWebKit Bugzillaの投稿をチェックしてみてください。
Safariを含めて対応するためにはCSSで泥臭い方法を用いる必要があります。dirty workaroundってやつです。
具体的には遷移先の要素(ヘッダーに隠れさせたくない要素)に対して、CSSで以下のように指定します。
.example:before {
display: block;
content: ' '; /* 完全に空だとなぜか効かないことがあったので半角スペースだけ入れる */
margin-top: -60px; /* ヘッダーの高さ */
height: 60px; /* ヘッダーの高さ */
visibility: hidden;
pointer-events: none;
}
何をやっているかというと、要素に対して疑似要素をくっつけて、その要素をヘッダーの高さぶんだけ上にずらして配置するようにします(margin-top: -60px;
の部分)。実際の表示位置がずれることはないように、height: 60px
で帳尻合わせをしています。
2020年にこんな書き方をしないといけないだなんて………………
なお、疑似要素を使わずに直接要素に対してmargin-top: -60px; padding-top: 60px;
のように指定してスクロール位置を調整することもできます。ただ、疑似要素で指定しておけば、Safariでscroll-margin-top
が使えるようになったとき.example:before {}
の部分をまるっと消せば済むため楽なのではないかと思っています。
上記の対処法だと要素自体にpadding
やbackground-color
などの装飾があるときに微調整が必要だったり、疑似要素が上部にある要素と重なってしまったりと色々大変だったりはします。
しかし、現状これ以外に方法はないような気がしています……(僕はかなり試行錯誤しましたし、ネットの奥の奥の奥まで探したりもしました)。早くこの記事自体が不要になることを願います。
Discussion