70 %

Cheap scroll to fix hero titles for blog posts in Preact

In addition to my rainbow hero headers on my blog post, I wanted a scrolling effect that would keep the title in view as the reader read the post. Here is the commit where I implemented it on my personal site.

one

The effect happens in roughly three stages. The initial scroll:

two

The scroll continuation

four

and finally setting the heading as fixed on the page.

three

Explanation

My first need was to put a ref on the <Header> element in the above image. This required a forwardRef call in the component itself, as well as a useRef in the parent page wrapping component.

JS
import { forwardRef } from "preact/compat";
const Header = forwardRef((props, ref) => (
<header
ref={ref}
...>
...
</header>
))

Each stage is triggered by window scroll event and some very cheap calculations.

JS
window.addEventListener(`scroll`, () => {
const currentPos = window.scrollY;
window.requestAnimationFrame(() => {
const titleOffset = scrollTargetRef.current.getBoundingClientRect().top;
console.log(titleOffset);
if (titleOffset < -50) {
setFontSize(18);
} else if (titleOffset < 0) {
// TODO: calculate lock values for smooth scroll
setFontSize(33);
} else {
// reset with original style values, etc
setFontSize(48);
}
if (titleOffset < -75) {
setHoverTitle(true);
} else {
setHoverTitle(false);
}
});
});

You'll notice that I have a TODO here to calculate smoother positions. This is because the chunkiness of the if statement (and the position fixed swap) is acceptable but not to the level I want it to be.

In the future I'll likely use Framer Motions useMotionValue helper to smoother out the font changes while adding in some extra divs/spacing to smoother out the fixed change as well.