diff options
| author | Danilo M. <danix@danix.xyz> | 2026-04-15 21:42:04 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-04-15 21:42:04 +0200 |
| commit | 9934e042c1e97765ac839f6c3f06d30f02b5d9d0 (patch) | |
| tree | cf102ee79caf5d304e303956a1becd5fb680ca39 /themes/danix-xyz-hacker/assets/js/menu.js | |
| parent | eb8a17482fc138c861db685f9efc86871bf2d7d2 (diff) | |
| download | danixxyz-9934e042c1e97765ac839f6c3f06d30f02b5d9d0.tar.gz danixxyz-9934e042c1e97765ac839f6c3f06d30f02b5d9d0.zip | |
Fix design system compliance: CSS variables, accessibility, and theme system
Core CSS improvements:
- Add --surface-rgb, --type-* (tech, life, quote, link, photo) custom properties
- Add --type-* semantic color classes for article badges
- Convert article badges from inline styles to CSS variable system
- Add prefers-color-scheme light fallback for no-JS users
- Add prefers-reduced-motion support to respect user accessibility settings
- Replace *:focus with *:focus-visible (keyboard-only outlines)
- Add clamp() fluid typography for hero-title and section-title
- Refactor container rules to mobile-first with 1060px breakpoint
Theme & Icon fixes:
- Fix theme toggle icon display with Alpine.js (was broken with Tailwind dark: classes)
- Add aria-hidden="true" to icon elements
- Update header with proper ARIA attributes on menu toggle
Accessibility enhancements:
- Add skip-to-main-content link in baseof.html
- Update hamburger menu with aria-expanded, aria-controls, aria-hidden
- Implement focus trap (Tab loops) within mobile menu
- Return focus to trigger button on menu close
- Add menu open/close state management with proper ARIA
Semantic HTML:
- Wrap article pages in <article> element (articles/single.html, _default/single.html)
- Fix quote article to use --type-quote border color instead of generic accent
Image optimization:
- Add loading="lazy" to profile image in index.html
- Add loading="lazy" to featured image in photo.html
Template fixes:
- Remove broken os.Getenv "THEME" runtime check from article-list-item.html
- Replace inline color styles with semantic .type-* classes
- Add 1060px lg: breakpoint to tailwind.config.js
i18n updates:
- Add skipToContent translations (en, it)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Diffstat (limited to 'themes/danix-xyz-hacker/assets/js/menu.js')
| -rw-r--r-- | themes/danix-xyz-hacker/assets/js/menu.js | 82 |
1 files changed, 71 insertions, 11 deletions
diff --git a/themes/danix-xyz-hacker/assets/js/menu.js b/themes/danix-xyz-hacker/assets/js/menu.js index f61e60b..3f32642 100644 --- a/themes/danix-xyz-hacker/assets/js/menu.js +++ b/themes/danix-xyz-hacker/assets/js/menu.js @@ -1,26 +1,59 @@ document.addEventListener('DOMContentLoaded', () => { const menuToggle = document.getElementById('menu-toggle'); const menuOverlay = document.getElementById('menu-overlay'); - const menuPanel = document.getElementById('menu-panel'); + const menuPanel = document.getElementById('hamburger-menu'); - function toggleMenu() { + function openMenu() { if (!menuOverlay || !menuPanel) return; - // Toggle overlay visibility classes - menuOverlay.classList.toggle('opacity-0'); - menuOverlay.classList.toggle('invisible'); + // Show overlay + menuOverlay.classList.remove('opacity-0'); + menuOverlay.classList.remove('invisible'); + + // Slide menu in + menuPanel.classList.remove('translate-x-full'); - // Toggle menu panel translation - menuPanel.classList.toggle('translate-x-full'); + // Manage accessibility + menuToggle.setAttribute('aria-expanded', 'true'); + menuPanel.removeAttribute('aria-hidden'); // Control body overflow - const isMenuOpen = !menuOverlay.classList.contains('opacity-0'); - document.body.style.overflow = isMenuOpen ? 'hidden' : ''; + document.body.style.overflow = 'hidden'; + + // Focus first focusable element in menu + const firstFocusable = menuPanel.querySelector('a, button'); + if (firstFocusable) { + setTimeout(() => firstFocusable.focus(), 50); + } } function closeMenu() { if (!menuOverlay || menuOverlay.classList.contains('opacity-0')) return; - toggleMenu(); + + // Hide overlay + menuOverlay.classList.add('opacity-0'); + menuOverlay.classList.add('invisible'); + + // Slide menu out + menuPanel.classList.add('translate-x-full'); + + // Manage accessibility + menuToggle.setAttribute('aria-expanded', 'false'); + menuPanel.setAttribute('aria-hidden', 'true'); + + // Restore body overflow + document.body.style.overflow = ''; + + // Return focus to toggle button + menuToggle.focus(); + } + + function toggleMenu() { + if (menuOverlay && menuOverlay.classList.contains('opacity-0')) { + openMenu(); + } else { + closeMenu(); + } } // Toggle menu when clicking the hamburger button @@ -38,7 +71,7 @@ document.addEventListener('DOMContentLoaded', () => { } // Close menu when clicking menu items - const menuLinks = document.querySelectorAll('#menu-panel a, #menu-panel button'); + const menuLinks = document.querySelectorAll('#hamburger-menu a, #hamburger-menu button'); menuLinks.forEach(link => { link.addEventListener('click', closeMenu); }); @@ -49,4 +82,31 @@ document.addEventListener('DOMContentLoaded', () => { closeMenu(); } }); + + // Focus trap: keep tab within menu when open + if (menuPanel) { + menuPanel.addEventListener('keydown', (e) => { + if (e.key !== 'Tab') return; + + const focusableElements = menuPanel.querySelectorAll('a, button, [tabindex]:not([tabindex="-1"])'); + if (focusableElements.length === 0) return; + + const firstElement = focusableElements[0]; + const lastElement = focusableElements[focusableElements.length - 1]; + const isMenuOpen = !menuOverlay.classList.contains('opacity-0'); + + if (!isMenuOpen) return; + + // Shift+Tab on first element: move to last + if (e.shiftKey && document.activeElement === firstElement) { + e.preventDefault(); + lastElement.focus(); + } + // Tab on last element: move to first + else if (!e.shiftKey && document.activeElement === lastElement) { + e.preventDefault(); + firstElement.focus(); + } + }); + } }); |
