updated cybersigilism theme
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
---
|
||||
/*
|
||||
* CyberFx — ambient + interactive layer for the `.cybersigil` theme.
|
||||
*
|
||||
* Renders an aria-hidden overlay root on every page. All visuals are CSS,
|
||||
* scoped to `.cybersigil .cs-fx*` in global.css, so this is an inert,
|
||||
* display:none no-op under every other theme. The bundled script only wires
|
||||
* the JS-driven mechanics (custom sigil cursor + fading trail, scroll-entry
|
||||
* databend on images) and self-disables off-theme, on touch, or under
|
||||
* prefers-reduced-motion.
|
||||
*/
|
||||
---
|
||||
|
||||
<div class="cs-fx" aria-hidden="true">
|
||||
<div class="cs-fx-halftone"></div>
|
||||
<div class="cs-fx-wire"></div>
|
||||
<div class="cs-fx-tear"></div>
|
||||
<i class="cs-fx-corner cs-fx-corner--tl"></i>
|
||||
<i class="cs-fx-corner cs-fx-corner--tr"></i>
|
||||
<i class="cs-fx-corner cs-fx-corner--bl"></i>
|
||||
<i class="cs-fx-corner cs-fx-corner--br"></i>
|
||||
<div class="cs-cursor"><span class="cs-cursor-ring"></span></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function initCyberFx() {
|
||||
const root = document.documentElement;
|
||||
if (!root.classList.contains('cybersigil')) return;
|
||||
|
||||
const fx = document.querySelector('.cs-fx') as HTMLElement | null;
|
||||
if (!fx) return;
|
||||
|
||||
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
const finePointer = window.matchMedia('(pointer: fine)').matches;
|
||||
const noHover = window.matchMedia('(hover: none)').matches;
|
||||
|
||||
/* ─── Custom sigil cursor + fading trail ─── */
|
||||
const cursor = fx.querySelector('.cs-cursor') as HTMLElement | null;
|
||||
if (cursor && finePointer && !noHover) {
|
||||
root.classList.add('cs-cursor-on');
|
||||
let cx = window.innerWidth / 2;
|
||||
let cy = window.innerHeight / 2;
|
||||
let raf = 0;
|
||||
let lastTrail = 0;
|
||||
const HOT = 'a,button,input,textarea,select,[role="button"],.btn,.plate,.topbar-control,.back-link,.chip';
|
||||
|
||||
function paint() {
|
||||
raf = 0;
|
||||
cursor!.style.transform = `translate3d(${cx}px, ${cy}px, 0)`;
|
||||
}
|
||||
window.addEventListener(
|
||||
'mousemove',
|
||||
(e) => {
|
||||
cx = e.clientX;
|
||||
cy = e.clientY;
|
||||
if (!raf) raf = requestAnimationFrame(paint);
|
||||
|
||||
const t = e.target as Element | null;
|
||||
const hot = !!(t && t.closest && t.closest(HOT));
|
||||
cursor!.classList.toggle('cs-cursor--hot', hot);
|
||||
|
||||
if (!reduced && e.timeStamp - lastTrail > 28) {
|
||||
lastTrail = e.timeStamp;
|
||||
const dot = document.createElement('span');
|
||||
dot.className = 'cs-trail';
|
||||
dot.style.left = cx + 'px';
|
||||
dot.style.top = cy + 'px';
|
||||
fx!.appendChild(dot);
|
||||
dot.addEventListener('animationend', () => dot.remove(), { once: true });
|
||||
}
|
||||
},
|
||||
{ passive: true }
|
||||
);
|
||||
window.addEventListener('mousedown', () => cursor!.classList.add('cs-cursor--down'));
|
||||
window.addEventListener('mouseup', () => cursor!.classList.remove('cs-cursor--down'));
|
||||
document.addEventListener('mouseleave', () => cursor!.classList.add('cs-cursor--gone'));
|
||||
document.addEventListener('mouseenter', () => cursor!.classList.remove('cs-cursor--gone'));
|
||||
}
|
||||
|
||||
/* ─── Scroll-entry databend on images ─── */
|
||||
if (!reduced && 'IntersectionObserver' in window) {
|
||||
const targets = document.querySelectorAll<HTMLElement>(
|
||||
'.prose img, .prose figure img, .plate-image img'
|
||||
);
|
||||
if (targets.length) {
|
||||
const io = new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const en of entries) {
|
||||
if (!en.isIntersecting) continue;
|
||||
const el = en.target as HTMLElement;
|
||||
el.classList.remove('cs-databent');
|
||||
// reflow so the animation can retrigger
|
||||
void el.offsetWidth;
|
||||
el.classList.add('cs-databent');
|
||||
io.unobserve(el);
|
||||
}
|
||||
},
|
||||
{ rootMargin: '0px 0px -12% 0px', threshold: 0.15 }
|
||||
);
|
||||
targets.forEach((t) => io.observe(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initCyberFx();
|
||||
// MPA back/forward restores: re-arm if needed.
|
||||
window.addEventListener('pageshow', (e) => {
|
||||
if (e.persisted) initCyberFx();
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user