Problem
Many websites use a sticky / fixed header (which usually includes the logo and a navigation menu) that is always at the top of the viewport (using position: sticky
or position: fixed
), even when the user scrolls the page.
While this may improve the user experience (since the navigation menu is always easily accessible), it can cause a problem when trying to auto-scroll to a particular element on the page.
The target element (to which we want to scroll) will be covered behind the sticky header.
This is because the sticky header “floats” on the page and its size is not taken into account.
By default, the target element will be snapped to the top of the page, and therefore will be hidden behind the sticky header that’s also right at the top of the page.
We will face this problem whether we use JavaScript or anchor links (e.g. id="form"
in the element and #form
as the link) to perform the auto-scrolling.
Solution
Fortunately, there’s an easy way to fix this using a single CSS property: scroll-padding-top
.
This is a very useful and simple CSS property that is also well supported by the vast majority of browsers.
Let’s say we have a sticky header whose height is 70px
.
We’ll simply add this CSS code:
html {
scroll-padding-top: 70px;
}
This will add “invisible padding” to the main html
element that will only be used when auto-scrolling is performed. As a result, the element will be placed not quite at the top of the page, but 70px
from it, allowing it to appear right after the sticky header.
We may want to add a small gap so that the element has a few pixels of space from the header. To do this, we can simply adjust the value of scroll-padding-top
. For example, use 80px
to create a 10px
space from a 70px
sticky header.
Finally, if the height of the sticky header varies between mobile and desktop (say, 70px
on mobile and 100px
on desktop), we can change the scroll-padding-top
value using media queries, similar to the following SCSS code:
html {
scroll-padding-top: 70px;
@media (min-width: $min-lg) {
scroll-padding-top: 100px;
}
}