We can easily do sticky nav’s with position: sticky.
CSS way
position: fixed;
top: 0;
left: 0;
width: 100%;
@supports (position: sticky) {
position: sticky;
top: 0;
left: unset;
width: unset;
}
A better way then a sticky header?
In some circumstances sticky navs can get in the way (something a smaller “to top of page” could help with,especially on limited screen sizes like mobile) and there’s perhaps better ways to help a user navigate a sub section of a site.
Entirely content dependent, but there are accessibility patterns to put navigation sub-menus in a side-area next to the main content as related content. Makes it readily accessible and viewable, and in an aside (side bar) it’s out of the main content.
We could have small table of contents of the page (if lots of content) or a side nav that shows pages linked/related to the current content - that could be sticky (on larger screens) and take up less space.
Dev issues
IE11 can be supported through use of position: fixed, but that’s where I’d say progressive enhancement comes in… you can add position: fixed, but there is a performance hit in IE. Newer evergreen browsers work with fixed much better (it causes multi-layer repaints), and one of the reasons why sticky came about.
Combine using margin to “space” the header away from the top of the page, and apply a background colour to the header. position: sticky with top: 0 will ignore the margin, thus giving you a thin header area, than if you’d used padding.
No stuck state available
If not, or the contents of the header change size if stuck, you’ll have to use Intersection Observer to detect when/if it goes off the page, allowing classes/animation names.
Beware that position: sticky causes browser to renders the area it would have taken up, so if a logo in the header is resized upon being stuck, the area it took up further inthe page (now hidden) will cause the page length to decrease/increase and could re-trigger Intersection Observer and it goes in an infiinite loop. So, resize the logo height inside a fixed height container.
Location/activation within the DOM
Sticky will only activate on elements that are directly relative/affected by the page canvas - so the nearer the :root node the more likely it is to work (you can reverse engineer it by testing for other CSS styles affecting a parent of the sticky element).
Offsets
JS
You’ll need JS to put in offsets, because if you scroll to ID’s lower in the page (eg, link to a newsletter signup form) the header will be anchored to the top of the page above the content. The JS offset will measure the header height and dynamically apply that to scrolled content.
CSS
There are two CSS ways of doing offsets (which works great when there are differing head heights at different media queries) but with the big caveat that in one technique it styles a ::before pseudo element when an element is focused (scrolled to), so if you already have a ::before styled on it… you’re gonna have a bad time. The otehr uses margin, so may conflict there too.
CSS Target located in \styles\2-elements\_js-queries.scss
$O_TARGET-fix-md: 100px;
$O_TARGET-fix-lg: 250px;
// This prevents #achor scroll position from being obscurred by a position:fixed / position:sticky element (eg, header)
// Or you can intercept with JS
:target {
// This technique doesn't always work...
&::before {
content: "";
display: inline-block; // inline-block might be better if it's attached to a display:block parent element
height: $O_TARGET-fix-md;//px2rem($O_TARGET-fix-md);
@include margin(-$O_TARGET-fix-md, 0, 0);
@include media(screen, ">menu-large") {
height: $O_TARGET-fix-lg;//px2rem($O_TARGET-fix-lg);
@include margin(-$O_TARGET-fix-lg, 0, 0);
}
}
}
$O_TARGET-fix-md: 100px;
$O_TARGET-fix-lg: 250px;
// This prevents #achor scroll position from being obscurred by a position:fixed / position:sticky element (eg, header)
// Or you can intercept with JS
:target {
// This technique seems more consistently reliable - except with form inputs, where it conflicts with margin
background-clip: padding-box;
border-top: px2rem($O_TARGET-fix-md) solid transparent;
@include margin(-$O_TARGET-fix-md, 0, 0);
@include media(screen, ">menu-large") {
border-top-width: px2rem($O_TARGET-fix-lg);
@include margin(-$O_TARGET-fix-lg, 0, 0);
}
}
Examples
- Example 1 sticky header
- Example 2 multi stickies that cover each other up
- Example 3 a stuck side area