Add the [data-headroom-nav] attribute to the sds-pageLayout__header element
Use the [data-reacts-to-headroom-nav] attribute to have elements change their top value to match the visible part of the navigation when sticky.
Add the [data-nav-module] attribute to the sds-pageLayout__header element.
This will take care of telling the page layout to add padding top equal to the nav height and set the header's position to fixed so its sticky behaviour can still work as intended no matter how deeply it is nested within the page's content. This works through the CSS :has() selector on the page layout itself. If the selector isn't supported the default behaviour is applied as a fallback and the nav isn't sticky
<div class="sds-pageLayout">
<div class="sds-pageLayout__header" data-headroom-nav>
</div>
<main class="sds-pageLayout__main">
<div class="sds-pageLayout__mainTop">
</div>
<div class="sds-pageLayout__mainCenter">
</div>
<div class="sds-pageLayout__mainBottom">
</div>
</main>
<footer class="sds-pageLayout__footer">
</footer>
</div>
<div class="{{ namespace }}pageLayout{% for mod in pageLayoutModifiers %} {{ mod }}{% endfor %}{% for mod in classes %} {{ mod }}{% endfor %}">
{% if not navUsedAsModule %}
<div class="{{ namespace }}pageLayout__header"{% if navUsedAsModule %} data-nav-module{% else %} data-headroom-nav{% endif %}>
{% endif %}
{% block header %}
{% endblock %}
{% if not navUsedAsModule %}
</div>
{% endif %}
<main class="{{ namespace }}pageLayout__main">
{% block breadcrumbs %}
{% endblock %}
<div class="{{ namespace }}pageLayout__mainTop">
{% if navUsedAsModule %}
<div class="{{ namespace }}pageLayout__header"{% if navUsedAsModule %} data-nav-module{% else %} data-headroom-nav{% endif %}>
{% endif %}
{% block headerAsModule %}
{% endblock %}
{% if navUsedAsModule %}
</div>
{% endif %}
{% block mainTop %}
{% endblock %}
</div>
<div class="{{ namespace }}pageLayout__mainCenter">
{% block main %}
{% endblock %}
</div>
<div class="{{ namespace }}pageLayout__mainBottom">
{% block mainBottom %}
{% endblock %}
</div>
</main>
{% block contactFooter %}
{% endblock %}
<footer class="{{ namespace }}pageLayout__footer">
{% block footer %}
{% endblock %}
</footer>
</div>
{% block misc %}
{% endblock %}
import spacerTokens from "../../../../config/design-tokens-generated-config/js/spacer";
export default class SpuerkeessSiteHeadroom {
constructor() {
this.fireSpkHeadroom();
}
fireSpkHeadroom() {
let nav = document.querySelector("[data-headroom-nav]");
if (CSS && CSS.supports && CSS.supports("position", "sticky")) {
let navHeight = nav.clientHeight;
let navTopBar = nav.querySelector(".sds-header__nav").clientHeight;
let hr = new Headroom(nav, {
// vertical offset in px before element is first unpinned
offset : navHeight,
// or you can specify offset individually for up/down scroll
// scroll tolerance in px before state changes
tolerance : 0,
// or you can specify tolerance individually for up/down scroll
// css classes to apply
classes : {
// when element is initialised
initial : "-headroom",
// when scrolling up
pinned : "-headroomPinned",
// when scrolling down
unpinned : "-headroomUnpinned",
// when above offset
top : "-headroomTop",
// when below offset
notTop : "-headroomNotTop",
// when at bottom of scroll area
bottom : "-headroomBottom",
// when not at bottom of scroll area
notBottom : "-headroomNotBottom",
// when frozen method has been called
frozen: "-headroomFrozen",
},
// callback when pinned, `this` is headroom object
onPin : function() {
nav.style.top = 0;
$('[data-reacts-to-headroom]')?.css("top", `${navTopBar + parseInt(spacerTokens.SpacerUnit8, 10)}px`);
},
// callback when unpinned, `this` is headroom object
onUnpin : function() {
nav.style.top = `-${navTopBar}px`;
$('[data-reacts-to-headroom]')?.css("top", `${spacerTokens.SpacerUnit8}`);
},
// callback when above offset, `this` is headroom object
onTop : function() {
$('[data-reacts-to-headroom]')?.css("top", `${navTopBar}px`);
},
// callback when below offset, `this` is headroom object
onNotTop : function() {},
// callback when at bottom of page, `this` is headroom object
onBottom : function() {},
// callback when moving away from bottom of page, `this` is headroom object
onNotBottom : function() {}
});
hr.init();
} else {
console.log("no position: sticky; support");
}
}
}
/* variables specific to current element */
$element-specific-variables: "";
.#{$namespace}pageLayout {
/* Save root element context for easy access if nesting is needed */
$self: &;
/* properties of current element + media queries */
display: flex;
flex-direction: column;
min-height: 100%;
/* Pseudo Elements */
&::before {
}
&::after {
}
/*
Include elements that are linked to the current element but have to reside at the root level of the stylesheet
(e.g: keyframes)
*/
@at-root {
}
/* children - write selector in full in comments in order to facilitate search */
// pageLayout__header
&__header {
// follows same logic as parent
position: sticky;
z-index: z("fixed");
top: 0;
transition: top $snet-frame-main-header-transition-duration-global linear;
&[data-nav-module] {
position: fixed;
left: 0;
width: 100%;
}
}
&__main {
margin-bottom: map-deep-get($token-spacer-content-section-map, "sm");
flex-grow: 1;
@include media-breakpoint-up("lg") {
margin-bottom: map-deep-get($token-spacer-content-section-map, "m");
}
}
&__footer {
}
&:has([data-nav-module]) {
padding-top: $nav-topbar-height-global;
}
/* modifiers */
// pageLayout -mainNoSpacing
&.-mainNoSpacing {
// follows same logic as base element
#{$self}__main {
margin-bottom: 0;
}
}
/* random parent element */
/*
*
* Syntax : .randomParentElt & {}
*
*/
/* Pseudo Classes */
&:hover {
@media (hover: hover) {
}
}
&:focus {
}
&:active {
}
&:focus,
&:active {
}
}