Menu
Enjoy a collection of Tea for Thought to delight your spirit!
/**
* Tư-Trà Hero Header Suppress
*
* Keeps the header at full size while a "fullsize hero" section is
* visible above the header. .scrolling only activates once the next
* section's top crosses the bottom of the header — i.e., the moment
* the next section actually starts sliding under the sticky header.
*
* Usage:
* In Bricks Builder, add the CSS class `tt-fullsize-hero` to the
* hero section. The script automatically pairs it with the next
* sibling section to detect when the hero region ends.
*
* Architecture:
* - Threshold: next-sibling.rect.top vs header.rect.bottom
* > 0 : hero region dominant — suppress .scrolling
* <= 0 : next section overlapping header — allow .scrolling
*
* - Synchronous scroll listener (no rAF throttle): runs in the same
* event tick as Bricks's handler, so .scrolling is stripped before
* the browser paints. No frame-ordering race, no hesitation on iOS
* momentum scroll.
*
* - MutationObserver on body + header class attribute: backup for
* when Bricks toggles .scrolling outside of scroll events (rAF,
* setTimeout, transitionend). Fires as a microtask, before paint —
* the briefly-added class is gone before the user sees it.
*
* - Auto-no-op on pages without a hero (early return).
* - No CSS changes required.
*
* Version: 1.2.0
* Author: Charlie + Claude
*
* History
* v1.0.0: IntersectionObserver on hero. Broke for sticky heroes —
* IO reports the hero as intersecting for its entire stuck
* duration because the bounding rect stays at top:0.
* v1.1.0: Switched to next-sibling rect check. Fixed sticky case.
* Worked on desktop, but rAF throttle exposed a frame-ordering
* race against Bricks's own rAF on iOS momentum scroll —
* visible hesitation when scrolling back to top.
* v1.2.0: Synchronous scroll handler eliminates the rAF race.
* MutationObserver covers Bricks's non-scroll additions of
* .scrolling. Threshold raised from y=0 to header.bottom so
* shrink begins at the correct visual moment (next section
* meeting the header, not crossing the viewport top).
*/
(function () {
'use strict';
var HERO_SELECTOR = '.tt-fullsize-hero';
var SCROLLING_CLASS = 'scrolling';
var HEADER_SELECTOR = '#brx-header, header.brxe-header';
function init() {
var heroes = document.querySelectorAll(HERO_SELECTOR);
if (heroes.length === 0) return;
// Pair each hero with its next sibling. Next sibling acts as the
// visual signal for "hero region has ended" — works for both
// sticky and non-sticky heroes.
var pairs = [];
for (var i = 0; i < heroes.length; i++) {
pairs.push({ hero: heroes[i], next: heroes[i].nextElementSibling });
}
var headerEls = document.querySelectorAll(HEADER_SELECTOR);
var heroActive = false;
function isHeroActive() {
// Read header bottom each call. The header may itself shrink
// (changing its height) when .scrolling is on, but we only ever
// need this measurement at the threshold moment, where the
// header is still full-size.
var headerBottom = 0;
if (headerEls.length > 0) {
headerBottom = headerEls[0].getBoundingClientRect().bottom;
}
for (var j = 0; j < pairs.length; j++) {
var p = pairs[j];
var endRef = p.next
? p.next.getBoundingClientRect().top
: p.hero.getBoundingClientRect().bottom;
if (endRef > headerBottom) return true;
}
return false;
}
function clearScrolling() {
if (document.body.classList.contains(SCROLLING_CLASS)) {
document.body.classList.remove(SCROLLING_CLASS);
}
for (var k = 0; k < headerEls.length; k++) {
headerEls[k].classList.remove(SCROLLING_CLASS);
}
}
function update() {
heroActive = isHeroActive();
if (heroActive) clearScrolling();
}
// Synchronous scroll listener: no rAF. Runs in the same tick as
// Bricks's handler; ordering doesn't matter because the
// MutationObserver below catches any add we miss.
window.addEventListener('scroll', update, { passive: true });
window.addEventListener('resize', update);
// Backup: if anything (Bricks's rAF, a transitionend handler, etc.)
// adds .scrolling while heroActive, strip it immediately as a
// microtask, before the next paint.
var classObserver = new MutationObserver(function () {
if (heroActive) clearScrolling();
});
classObserver.observe(document.body, {
attributes: true,
attributeFilter: ['class']
});
for (var m = 0; m < headerEls.length; m++) {
classObserver.observe(headerEls[m], {
attributes: true,
attributeFilter: ['class']
});
}
update(); // Handle reload-mid-page
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();Enjoy a collection of Tea for Thought to delight your spirit!