/**
* Sets `--header-height` and `--header-group-height` to measured values before first paint.
*
* A few CSS rules position content relative to the header at first paint — a hero sized to
* the remaining viewport, a transparent header's negative margin. Without a measured value
* those rules use the `60px` fallback from `sections/header.liquid`, and the content visibly
* jumps once `header.js` corrects the values after load.
*
* Measuring forces layout mid-parse, so it only runs where a consuming rule exists.
* Callers opt in: the transparent-header branch below, or `snippets/measure-header-heights.liquid`,
* which any new first-paint consumer must render too.
*
* Measures once per page; after load, header.js's ResizeObservers keep the values fresh.
* The group-height walk mimics calculateHeaderGroupHeight() in utilities.js — keep them
* in sync.
*/
window.measureHeaderHeights = (function () {
let measured = false;
return function measureHeaderHeights() {
if (measured) return;
const header = document.querySelector('#header-component');
const headerGroup = document.querySelector('#header-group');
if (!header || !headerGroup) return;
measured = true;
const headerHeight = header.offsetHeight;
let headerGroupHeight = 0;
for (const element of headerGroup.children) {
if (element instanceof HTMLElement) headerGroupHeight += element.offsetHeight;
}
/**
* A transparent header is absolutely positioned, so the loop above counts its section
* wrapper as 0px. When a section follows it in the group, header.liquid pushes that
* section down by the header's height using a margin, which offsetHeight also
* excludes — so the header's height has to be added back manually.
*/
if (header.hasAttribute('transparent') && header.parentElement?.nextElementSibling) {
headerGroupHeight += headerHeight;
}
document.body.style.setProperty('--header-height', `${headerHeight}px`);
document.body.style.setProperty('--header-group-height', `${headerGroupHeight}px`);
};
})();
/**
* Transparent-header rules consume the measured heights at first paint on every
* template, so the transparent case opts in globally here instead of per-section.
*/
if (document.querySelector('#header-component')?.hasAttribute('transparent')) {
measureHeaderHeights();
}
/**
* Sets `--top-row-height`, consumed by the header underlays and submenus. Deferred to a
* rAF so the height read doesn't force layout mid-parse, and scoped to the
* header, where all consumers live, so the write only invalidates the header subtree.
* Mimics the `--top-row-height` write in updateHeaderHeights() in utilities.js
*/
(function setTopRowHeight() {
const header = document.querySelector('#header-component');
const headerTopRow = header?.querySelector('.header__row--top');
if (!headerTopRow) return;
window.requestAnimationFrame(function () {
header.style.setProperty('--top-row-height', `${headerTopRow.getBoundingClientRect().height}px`);
});
})();
/**
* Sets `--transparent-header-offset-boolean` when the first section must offset itself
* under a transparent header. The non-offset case relies on the `0` default declared in
* sections/header.liquid — unlike updateTransparentHeaderOffset() in utilities.js, which
* re-runs on editor changes and must write '0' to reset a previously written '1'.
*/
(function updateTransparentHeaderOffset() {
const header = document.querySelector('#header-component');
const headerSection = document.querySelector('#header-group .header-section');
if (!headerSection || !header?.hasAttribute('transparent')) return;
const hasImmediateSection = headerSection.nextElementSibling?.classList.contains('shopify-section');
if (!hasImmediateSection) {
document.body.style.setProperty('--transparent-header-offset-boolean', '1');
}
})();
/**
* Sets the initial menu style (inline menu vs. drawer) on the header component.
* Mimics setHeaderMenuStyle() in utilities.js
*/
(function setHeaderMenuStyle() {
const headerComponent = document.querySelector('#header-component');
if (headerComponent) {
const isTouchDevice = 'ontouchstart' in window && navigator.maxTouchPoints > 0;
const overflowList = headerComponent.querySelector('overflow-list');
const hasReachedMinimum = overflowList && overflowList.hasAttribute('minimum-reached');
headerComponent.dataset.menuStyle = isTouchDevice || hasReachedMinimum ? 'drawer' : 'menu';
}
})();
Your Privacy Choices
As described in our Privacy Policy, we collect personal information from your interactions with us and our website, including through cookies and similar technologies. We may also share this personal information with third parties, including advertising partners. We do this in order to show you ads on other websites that are more relevant to your interests and for other reasons outlined in our privacy policy.
Sharing of personal information for targeted advertising based on your interaction on different websites may be considered "sales", "sharing", or "targeted advertising" under certain U.S. state privacy laws. Depending on where you live, you may have the right to opt out of these activities. If you would like to exercise this opt-out right, please follow the instructions below.
If you visit our website with the Global Privacy Control opt-out preference signal enabled, depending on where you are, we will treat this as a request to opt-out of activity that may be considered a “sale” or “sharing” of personal information or other uses that may be considered targeted advertising for the device and browser you used to visit our website.