extroonie.com/assets/js/main.js

89 lines
2.8 KiB
JavaScript
Raw Normal View History

2026-09-18 15:30:20 -07:00
(() => {
const root = document.documentElement;
const toggle = document.querySelector('.theme-toggle');
const system = matchMedia('(prefers-color-scheme: dark)');
let manual = ['light', 'dark'].includes(root.dataset.themeChoice);
function setTheme(theme) {
root.dataset.theme = theme;
toggle?.setAttribute('aria-checked', String(theme === 'dark'));
}
if (toggle) {
toggle.hidden = false;
setTheme(root.dataset.theme || (system.matches ? 'dark' : 'light'));
toggle.addEventListener('click', () => {
const theme = root.dataset.theme === 'dark' ? 'light' : 'dark';
manual = true;
root.dataset.themeChoice = theme;
setTheme(theme);
try {
localStorage.setItem('theme', theme);
} catch {}
});
system.addEventListener('change', () => {
if (!manual) setTheme(system.matches ? 'dark' : 'light');
});
window.addEventListener('storage', (event) => {
if (event.key !== 'theme') return;
manual = ['light', 'dark'].includes(event.newValue);
root.dataset.themeChoice = manual ? event.newValue : 'system';
setTheme(manual ? event.newValue : system.matches ? 'dark' : 'light');
});
}
const biography = document.querySelector('#about');
function fragmentTarget(hash = location.hash) {
let id;
try {
id = decodeURIComponent(hash.slice(1));
} catch {
return;
}
return id ? document.getElementById(id) : null;
}
function revealFragment() {
const target = fragmentTarget();
if (!target) return;
if (biography?.contains(target)) biography.open = true;
requestAnimationFrame(() => {
target.scrollIntoView({ block: 'start' });
target.focus({ preventScroll: true });
});
}
window.addEventListener('hashchange', revealFragment);
if (location.hash) revealFragment();
// Keep native fragment navigation: browser history, URL, and no-JS behavior.
document.addEventListener('click', (event) => {
if (
event.defaultPrevented ||
event.button !== 0 ||
event.metaKey ||
event.ctrlKey ||
event.shiftKey ||
event.altKey
)
return;
const link = event.target.closest('a[href^="#"]');
if (!link) return;
const target = fragmentTarget(link.hash);
if (target && biography?.contains(target)) biography.open = true;
});
const notice = document.querySelector('.notice');
let timer;
document.querySelectorAll('[data-copy]').forEach((button) => {
const fallback = button.parentElement.querySelector('.discord-fallback');
button.hidden = false;
fallback.hidden = true;
button.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(button.dataset.copy);
notice.textContent = 'Discord username copied.';
} catch {
fallback.hidden = false;
notice.textContent = 'Select the username to copy it.';
}
clearTimeout(timer);
notice.classList.add('visible');
timer = setTimeout(() => notice.classList.remove('visible'), 3000);
});
});
})();