Skip to content

Instantly share code, notes, and snippets.

@ebucario
Created June 16, 2026 04:47
Show Gist options
  • Select an option

  • Save ebucario/8763a188744839bcec78cce304b82fcf to your computer and use it in GitHub Desktop.

Select an option

Save ebucario/8763a188744839bcec78cce304b82fcf to your computer and use it in GitHub Desktop.
main-page-parallax-bg
class MainPageParallaxBg extends HTMLElement {
static observedAttributes = ["image-bg", "image-mid", "image-fg"];
divRefs = {
"image-bg": null,
"image-mid": null,
"image-fg": null,
};
attributeRefs = {
"image-bg": null,
"image-mid": null,
"image-fg": null,
};
connectedCallback() {
const shadow = this.attachShadow({ mode: "open" });
const style = document.createElement("style");
style.innerText = `
#wrapper {
width: 100%;
height: 100%;
position: relative;
background: #004944;
overflow: hidden;
transition: transform-origin 2s ease-out;
}
.bg,
.mid,
.fg,
.cover {
display: block;
position: absolute;
background: right 40% center / cover;
inset: 0;
transform-origin: inherit; /* set in JS on mouseover of parent element */
}
.bg {
inset: -2.5vw; /* eyeballed a bit */
transform: scale(0.98);
}
.mid {
transform: scale(1.05);
filter: blur(2px);
}
.fg {
transform: scale(1.2);
filter: blur(5px);
}
.cover {
/* 0.7 opacity before
so it's 30%
which is 0.3 * 256
which is 76.8
which in hex is 4c */
background-color: #ffffff4c;
}
`;
shadow.appendChild(style);
const div = document.createElement("div");
div.id = "wrapper";
const bg = document.createElement("div");
bg.classList.add("bg");
const mid = document.createElement("div");
mid.classList.add("mid");
const fg = document.createElement("div");
fg.classList.add("fg");
const cover = document.createElement("div");
fg.classList.add("cover");
this.divRefs["image-bg"] = bg;
this.divRefs["image-mid"] = mid;
this.divRefs["image-fg"] = fg;
this.applyInlineStyles();
div.appendChild(bg);
div.appendChild(mid);
div.appendChild(fg);
div.appendChild(cover);
const mouseMove = (() => {
let throttler = 0;
return (e) => {
if (throttler !== 0) return;
throttler = setTimeout(() => {
throttler = 0;
}, 100);
const leader = e.currentTarget;
const rect = leader.getBoundingClientRect();
const width = ((e.clientX - rect.left) / rect.width) * 100;
const height = ((e.clientY - rect.top) / rect.height) * 100;
leader.style.transformOrigin = `${width.toFixed(2)}% ${height.toFixed(2)}%`;
};
})();
div.onmousemove = window.matchMedia(
"(prefers-reduced-motion: no-preference)",
).matches
? mouseMove
: undefined;
shadow.appendChild(div);
}
applyInlineStyles() {
for (const ref of MainPageParallaxBg.observedAttributes) {
const attribute = this.attributeRefs[ref];
const div = this.divRefs[ref];
console.log(`attribute: ${attribute}, div: ${div}`);
if (attribute && div) {
div.style.backgroundImage = `url(${attribute})`;
}
}
}
attributeChangedCallback(name, oldValue, newValue) {
this.attributeRefs[name] = newValue;
this.applyInlineStyles();
}
}
customElements.define("main-page-parallax-bg", MainPageParallaxBg);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment