128 lines
3.5 KiB
JavaScript
128 lines
3.5 KiB
JavaScript
const wordmark = document.getElementById('wordmark');
|
|
if (wordmark) {
|
|
document.addEventListener('mousemove', (e) => {
|
|
const cx = window.innerWidth / 2;
|
|
const cy = window.innerHeight / 2;
|
|
const dx = (e.clientX - cx) / cx;
|
|
const dy = (e.clientY - cy) / cy;
|
|
wordmark.style.transform = `perspective(800px) rotateX(${-dy * 5}deg) rotateY(${dx * 5}deg)`;
|
|
});
|
|
document.addEventListener('mouseleave', () => {
|
|
wordmark.style.transform = '';
|
|
});
|
|
}
|
|
|
|
const THEME_KEY = 'theme';
|
|
const themeMenu = document.querySelector('.theme-menu');
|
|
const themeToggle = themeMenu ? themeMenu.querySelector('.theme-toggle') : null;
|
|
const themeDropdown = themeMenu ? themeMenu.querySelector('.theme-dropdown') : null;
|
|
const themeOptions = themeMenu ? themeMenu.querySelectorAll('[data-theme-choice]') : [];
|
|
const themeQuery = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
|
|
|
|
function getEffectiveTheme(mode) {
|
|
if (mode === 'auto') {
|
|
return themeQuery && themeQuery.matches ? 'dark' : 'light';
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
function updateToggle(mode, effectiveTheme) {
|
|
if (!themeToggle) {
|
|
return;
|
|
}
|
|
|
|
let iconClass = 'fa-circle-half-stroke';
|
|
if (mode === 'dark') {
|
|
iconClass = 'fa-sun';
|
|
} else if (mode === 'light') {
|
|
iconClass = 'fa-moon';
|
|
}
|
|
|
|
themeToggle.innerHTML = `
|
|
<i class="fa-solid ${iconClass}"></i>
|
|
<span class="theme-caret"><i class="fa-solid fa-chevron-down"></i></span>
|
|
`;
|
|
themeToggle.setAttribute('aria-label', `Theme: ${mode.charAt(0).toUpperCase() + mode.slice(1)}`);
|
|
themeToggle.setAttribute('data-effective-theme', effectiveTheme);
|
|
}
|
|
|
|
function updateActiveOption(mode) {
|
|
themeOptions.forEach((option) => {
|
|
const isActive = option.dataset.themeChoice === mode;
|
|
option.classList.toggle('is-active', isActive);
|
|
option.setAttribute('aria-checked', isActive ? 'true' : 'false');
|
|
});
|
|
}
|
|
|
|
function setThemeMode(mode, persist = true) {
|
|
const effectiveTheme = getEffectiveTheme(mode);
|
|
document.documentElement.setAttribute('data-theme', effectiveTheme);
|
|
updateToggle(mode, effectiveTheme);
|
|
updateActiveOption(mode);
|
|
if (persist) {
|
|
localStorage.setItem(THEME_KEY, mode);
|
|
}
|
|
}
|
|
|
|
function initTheme() {
|
|
const savedMode = localStorage.getItem(THEME_KEY);
|
|
const mode = savedMode || 'auto';
|
|
setThemeMode(mode, Boolean(savedMode));
|
|
}
|
|
|
|
function openThemeMenu() {
|
|
if (!themeMenu || !themeToggle) {
|
|
return;
|
|
}
|
|
themeMenu.classList.add('is-open');
|
|
themeToggle.setAttribute('aria-expanded', 'true');
|
|
}
|
|
|
|
function closeThemeMenu() {
|
|
if (!themeMenu || !themeToggle) {
|
|
return;
|
|
}
|
|
themeMenu.classList.remove('is-open');
|
|
themeToggle.setAttribute('aria-expanded', 'false');
|
|
}
|
|
|
|
if (themeMenu && themeToggle && themeDropdown) {
|
|
initTheme();
|
|
|
|
themeToggle.addEventListener('click', (event) => {
|
|
event.stopPropagation();
|
|
themeMenu.classList.contains('is-open') ? closeThemeMenu() : openThemeMenu();
|
|
});
|
|
|
|
themeOptions.forEach((option) => {
|
|
option.addEventListener('click', () => {
|
|
const mode = option.dataset.themeChoice || 'auto';
|
|
setThemeMode(mode);
|
|
closeThemeMenu();
|
|
});
|
|
});
|
|
|
|
document.addEventListener('click', (event) => {
|
|
if (!themeMenu.contains(event.target)) {
|
|
closeThemeMenu();
|
|
}
|
|
});
|
|
|
|
document.addEventListener('keydown', (event) => {
|
|
if (event.key === 'Escape') {
|
|
closeThemeMenu();
|
|
}
|
|
});
|
|
} else {
|
|
initTheme();
|
|
}
|
|
|
|
if (themeQuery) {
|
|
themeQuery.addEventListener('change', () => {
|
|
const mode = localStorage.getItem(THEME_KEY) || 'auto';
|
|
if (mode === 'auto') {
|
|
setThemeMode('auto', false);
|
|
}
|
|
});
|
|
}
|