160 lines
4.0 KiB
JavaScript
160 lines
4.0 KiB
JavaScript
// Menu system
|
|
|
|
const body = document.body;
|
|
const menuToggle = document.getElementById('menuToggle');
|
|
const menuToggleLabel = menuToggle?.querySelector('.menu-toggle__label');
|
|
const menuOverlay = document.getElementById('menuOverlay');
|
|
const mobileMenu = document.getElementById('mobileMenu');
|
|
const siteContent = document.getElementById('siteContent');
|
|
|
|
function openMenu() {
|
|
body.classList.add('menu-open');
|
|
menuOverlay.hidden = false;
|
|
menuToggle.classList.add('is-open');
|
|
menuToggle.setAttribute('aria-expanded', 'true');
|
|
menuToggle.setAttribute('aria-label', 'Close menu');
|
|
if (menuToggleLabel) menuToggleLabel.textContent = 'Close';
|
|
mobileMenu.setAttribute('aria-hidden', 'false');
|
|
siteContent.setAttribute('inert', '');
|
|
menuToggle.focus({ preventScroll: true });
|
|
}
|
|
|
|
function closeMenu() {
|
|
body.classList.remove('menu-open');
|
|
menuToggle.classList.remove('is-open');
|
|
menuToggle.setAttribute('aria-expanded', 'false');
|
|
menuToggle.setAttribute('aria-label', 'Open menu');
|
|
if (menuToggleLabel) menuToggleLabel.textContent = 'Menu';
|
|
mobileMenu.setAttribute('aria-hidden', 'true');
|
|
siteContent.removeAttribute('inert');
|
|
menuToggle.focus({ preventScroll: true });
|
|
|
|
window.setTimeout(() => {
|
|
if (!body.classList.contains('menu-open')) {
|
|
menuOverlay.hidden = true;
|
|
}
|
|
}, 250);
|
|
}
|
|
|
|
function toggleMenu() {
|
|
if (body.classList.contains('menu-open')) {
|
|
closeMenu();
|
|
} else {
|
|
openMenu();
|
|
}
|
|
}
|
|
|
|
menuToggle.addEventListener('click', toggleMenu);
|
|
menuOverlay.addEventListener('click', closeMenu);
|
|
|
|
document.addEventListener('keydown', (event) => {
|
|
if (event.key === 'Escape' && body.classList.contains('menu-open')) {
|
|
closeMenu();
|
|
}
|
|
});
|
|
|
|
window.addEventListener('resize', () => {
|
|
if (window.innerWidth >= 768 && body.classList.contains('menu-open')) {
|
|
closeMenu();
|
|
}
|
|
});
|
|
|
|
|
|
// accessibility toggle
|
|
|
|
{
|
|
const STORAGE_KEY = 'extra-css-enabled';
|
|
|
|
function applyOverrideState(enabled) {
|
|
const link = document.getElementById('a11y-css');
|
|
const button = document.getElementById('a11y-toggle');
|
|
if (!link) return;
|
|
|
|
link.disabled = !enabled;
|
|
button?.setAttribute('aria-pressed', String(enabled));
|
|
}
|
|
|
|
const enabled = localStorage.getItem(STORAGE_KEY) === 'true';
|
|
applyOverrideState(enabled);
|
|
|
|
document.getElementById('a11y-toggle')?.addEventListener('click', () => {
|
|
const nextState = localStorage.getItem(STORAGE_KEY) !== 'true';
|
|
localStorage.setItem(STORAGE_KEY, String(nextState));
|
|
applyOverrideState(nextState);
|
|
});
|
|
}
|
|
|
|
|
|
// light-dark toggle
|
|
|
|
{
|
|
const STORAGE_KEY = 'theme-preference';
|
|
const root = document.documentElement;
|
|
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
|
|
|
function getSystemTheme() {
|
|
return mq.matches ? 'dark' : 'light';
|
|
}
|
|
|
|
function getSavedTheme() {
|
|
try {
|
|
return localStorage.getItem(STORAGE_KEY) || 'system';
|
|
} catch {
|
|
return 'system';
|
|
}
|
|
}
|
|
|
|
function setSavedTheme(value) {
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, value);
|
|
} catch {}
|
|
}
|
|
|
|
function applyTheme(theme = getSavedTheme()) {
|
|
root.style.colorScheme = theme === 'system' ? 'light dark' : theme;
|
|
}
|
|
|
|
function getNextTheme() {
|
|
return getSavedTheme() === 'system'
|
|
? (getSystemTheme() === 'dark' ? 'light' : 'dark')
|
|
: 'system';
|
|
}
|
|
|
|
function updateButton() {
|
|
const btn = document.getElementById('theme-toggle');
|
|
if (!btn) return;
|
|
|
|
const theme = getSavedTheme();
|
|
// btn.textContent =
|
|
// theme === 'system'
|
|
// ? `Use ${getSystemTheme() === 'dark' ? 'light' : 'dark'} theme`
|
|
// : 'Use browser/OS theme';
|
|
}
|
|
|
|
function toggleTheme() {
|
|
setSavedTheme(getNextTheme());
|
|
applyTheme();
|
|
updateButton();
|
|
}
|
|
|
|
function initThemeToggle() {
|
|
applyTheme();
|
|
updateButton();
|
|
|
|
const btn = document.getElementById('theme-toggle');
|
|
if (btn) btn.addEventListener('click', toggleTheme);
|
|
|
|
mq.addEventListener('change', () => {
|
|
if (getSavedTheme() === 'system') {
|
|
applyTheme('system');
|
|
updateButton();
|
|
}
|
|
});
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', initThemeToggle);
|
|
} else {
|
|
initThemeToggle();
|
|
}
|
|
} |